diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in
index 3d20e3ca460f..4bb83d50c6c9 100644
--- a/Documentation/Doxyfile.in
+++ b/Documentation/Doxyfile.in
@@ -886,6 +886,7 @@ EXCLUDE_SYMBOLS        = libcamera::BoundMethodArgs \
                          libcamera::BoundMethodStatic \
                          libcamera::CameraManager::Private \
                          libcamera::SignalBase \
+                         libcamera::ipa::AlgorithmFactoryBase \
                          *::details \
                          std::*
 
diff --git a/src/ipa/libipa/algorithm.cpp b/src/ipa/libipa/algorithm.cpp
index 2df91e5d8fed..1d0998089044 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,49 @@ namespace ipa {
  * such that the algorithms use up to date state as required.
  */
 
+/**
+ * \class AlgorithmFactory
+ * \brief Registration of Algorithm classes and creation of instances
+ * \tparam _Algorithm The algorithm class type for this factory
+ *
+ * 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 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::create()
+ * \brief Create an instance of the Algorithm corresponding to the factory
+ * \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
+ * \param[in] name Name of the algorithm
+ *
+ * Register an Algorithm subclass with the IPA module to make it available for
+ * instantiation through Module::createAlgorithm(). The \a name identifies the
+ * algorithm and must be unique across all algorithms registered for the IPA
+ * module.
+ */
+
 } /* namespace ipa */
 
 } /* namespace libcamera */
diff --git a/src/ipa/libipa/algorithm.h b/src/ipa/libipa/algorithm.h
index fd2ffcfbc900..cfbe4ed8efac 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,46 @@ public:
 	}
 };
 
+template<typename _Module>
+class AlgorithmFactoryBase
+{
+public:
+	AlgorithmFactoryBase(const char *name)
+		: name_(name)
+	{
+		_Module::registerAlgorithm(this);
+	}
+
+	virtual ~AlgorithmFactoryBase() = default;
+
+	const std::string &name() const { return name_; }
+
+	virtual std::unique_ptr<Algorithm<_Module>> create() const = 0;
+
+private:
+	std::string name_;
+};
+
+template<typename _Algorithm>
+class AlgorithmFactory : public AlgorithmFactoryBase<typename _Algorithm::Module>
+{
+public:
+	AlgorithmFactory(const char *name)
+		: AlgorithmFactoryBase<typename _Algorithm::Module>(name)
+	{
+	}
+
+	~AlgorithmFactory() = default;
+
+	std::unique_ptr<Algorithm<typename _Algorithm::Module>> create() const override
+	{
+		return std::make_unique<_Algorithm>();
+	}
+};
+
+#define REGISTER_IPA_ALGORITHM(algorithm, name) \
+static AlgorithmFactory<algorithm> global_##algorithm##Factory(name);
+
 } /* 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..05f39801db47 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 AlgorithmFactoryBase<Module> *factory : factories()) {
+			if (factory->name() == name)
+				return factory->create();
+		}
+
+		return nullptr;
+	}
+
+	static void registerAlgorithm(AlgorithmFactoryBase<Module> *factory)
+	{
+		factories().push_back(factory);
+	}
+
+private:
+	static std::vector<AlgorithmFactoryBase<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<AlgorithmFactoryBase<Module> *> factories;
+		return factories;
+	}
 };
 
 } /* namespace ipa */
