diff --git a/src/ipa/libipa/algorithm.cpp b/src/ipa/libipa/algorithm.cpp
index 2df91e5d8fed..6e0bba56ecb2 100644
--- a/src/ipa/libipa/algorithm.cpp
+++ b/src/ipa/libipa/algorithm.cpp
@@ -32,6 +32,11 @@ namespace ipa {
  * argument.
  */
 
+/**
+ * \typedef Algorithm::Module
+ * \brief The IPA module type for this class of algorithms
+ */
+
 /**
  * \fn Algorithm::configure()
  * \brief Configure the Algorithm given an IPAConfigInfo
@@ -94,6 +99,55 @@ namespace ipa {
  * such that the algorithms use up to date state as required.
  */
 
+/**
+ * \class AlgorithmFactory
+ * \brief Registration of Algorithm classes and creation of instances
+ *
+ * To facilitate instantiation of Algorithm classes, the AlgorithmFactory class
+ * implements auto-registration of algorithms with the IPA Module class. Each
+ * Algorithm subclass shall register itself using the REGISTER_IPA_ALGORITHM()
+ * macro, which will create a corresponding instance of an AlgorithmFactory
+ * subclass and register it with the IPA Module.
+ */
+
+/**
+ * \fn AlgorithmFactory::AlgorithmFactory()
+ * \brief Construct an algorithm factory
+ * \param[in] name Name of the algorithm class
+ *
+ * Creating an instance of the factory automatically registers is with the IPA
+ * Module class, enabling creation of algorithm instances through
+ * Module::createAlgorithm().
+ *
+ * The factory \a name identifies the algorithm and shall be unique.
+ */
+
+/**
+ * \fn AlgorithmFactory::name()
+ * \brief Retrieve the factory name
+ * \return The factory name
+ */
+
+/**
+ * \fn AlgorithmFactory::create()
+ * \brief Create an instance of the Algorithm corresponding to the factory
+ *
+ * This virtual function is implemented by the REGISTER_IPA_ALGORITHM()
+ * macro. It creates an algorithm instance.
+ *
+ * \return A pointer to a newly constructed instance of the Algorithm subclass
+ * corresponding to the factory
+ */
+
+/**
+ * \def REGISTER_IPA_ALGORITHM
+ * \brief Register an algorithm with the IPA module
+ * \param[in] algorithm Class name of Algorithm derived class to register
+ *
+ * Register an Algorithm subclass with the IPA module to make it available for
+ * instantiation through Module::createAlgorithm().
+ */
+
 } /* namespace ipa */
 
 } /* namespace libcamera */
diff --git a/src/ipa/libipa/algorithm.h b/src/ipa/libipa/algorithm.h
index fd2ffcfbc900..356c094976d4 100644
--- a/src/ipa/libipa/algorithm.h
+++ b/src/ipa/libipa/algorithm.h
@@ -6,14 +6,19 @@
  */
 #pragma once
 
+#include <memory>
+#include <string>
+
 namespace libcamera {
 
 namespace ipa {
 
-template<typename Module>
+template<typename _Module>
 class Algorithm
 {
 public:
+	using Module = _Module;
+
 	virtual ~Algorithm() {}
 
 	virtual int configure([[maybe_unused]] typename Module::Context &context,
@@ -34,6 +39,40 @@ public:
 	}
 };
 
+template<typename Module>
+class AlgorithmFactory
+{
+public:
+	AlgorithmFactory(const char *name)
+		: name_(name)
+	{
+		Module::registerAlgorithm(this);
+	}
+
+	virtual ~AlgorithmFactory() = default;
+
+	const std::string &name() const { return name_; }
+
+	virtual std::unique_ptr<Algorithm<Module>> create() const = 0;
+
+private:
+	std::string name_;
+};
+
+#define REGISTER_IPA_ALGORITHM(algorithm)					\
+class algorithm##Factory final : public AlgorithmFactory<algorithm::Module>	\
+{										\
+public:										\
+	algorithm##Factory() : AlgorithmFactory(#algorithm) {}			\
+										\
+	std::unique_ptr<ipa::Algorithm<algorithm::Module>> create() const	\
+	{									\
+		return std::make_unique<algorithm>();				\
+	}									\
+};										\
+										\
+static algorithm##Factory global_##algorithm##Factory;
+
 } /* namespace ipa */
 
 } /* namespace libcamera */
diff --git a/src/ipa/libipa/module.cpp b/src/ipa/libipa/module.cpp
index d03cc8ef03ed..451614fd04da 100644
--- a/src/ipa/libipa/module.cpp
+++ b/src/ipa/libipa/module.cpp
@@ -75,6 +75,30 @@ namespace ipa {
  * \brief The type of the IPA statistics and ISP results
  */
 
+/**
+ * \fn Module::createAlgorithm()
+ * \brief Create an instance of an Algorithm by name
+ * \param[in] name The algorithm name
+ *
+ * This function is the entry point to algorithm instantiation for the IPA
+ * module. It creates and returns an instance of an algorithm identified by its
+ * \a name. If no such algorithm exists, the function returns nullptr.
+ *
+ * To make an algorithm available to the IPA module, it shall be registered with
+ * the REGISTER_IPA_ALGORITHM() macro.
+ *
+ * \return A new instance of the Algorithm subclass corresponding to the \a name
+ */
+
+/**
+ * \fn Module::registerAlgorithm()
+ * \brief Add an algorithm factory class to the list of available algorithms
+ * \param[in] factory Factory to use to construct the algorithm
+ *
+ * This function registers an algorithm factory. It is meant to be called by the
+ * AlgorithmFactory constructor only.
+ */
+
 } /* namespace ipa */
 
 } /* namespace libcamera */
diff --git a/src/ipa/libipa/module.h b/src/ipa/libipa/module.h
index c4d778120408..f30fc33711bb 100644
--- a/src/ipa/libipa/module.h
+++ b/src/ipa/libipa/module.h
@@ -7,6 +7,12 @@
 
 #pragma once
 
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "algorithm.h"
+
 namespace libcamera {
 
 namespace ipa {
@@ -23,6 +29,33 @@ public:
 	using Stats = _Stats;
 
 	virtual ~Module() {}
+
+	static std::unique_ptr<Algorithm<Module>> createAlgorithm(const std::string &name)
+	{
+		for (const AlgorithmFactory<Module> *factory : factories()) {
+			if (factory->name() == name)
+				return factory->create();
+		}
+
+		return nullptr;
+	}
+
+	static void registerAlgorithm(AlgorithmFactory<Module> *factory)
+	{
+		factories().push_back(factory);
+	}
+
+private:
+	static std::vector<AlgorithmFactory<Module> *> &factories()
+	{
+		/*
+		 * The static factories map is defined inside the function to ensure
+		 * it gets initialized on first use, without any dependency on
+		 * link order.
+		 */
+		static std::vector<AlgorithmFactory<Module> *> factories;
+		return factories;
+	}
 };
 
 } /* namespace ipa */
