diff --git a/modules/shader_compiler/include/vkcv/shader/Compiler.hpp b/modules/shader_compiler/include/vkcv/shader/Compiler.hpp
index 5b119ca5c68f997bacfbea6c60d5c965f9a7a54e..d4be7384dfffeeb068a13660004be138e62722a1 100644
--- a/modules/shader_compiler/include/vkcv/shader/Compiler.hpp
+++ b/modules/shader_compiler/include/vkcv/shader/Compiler.hpp
@@ -6,10 +6,12 @@
 
 #include <vkcv/Event.hpp>
 #include <vkcv/ShaderStage.hpp>
+#include <vkcv/ShaderProgram.hpp>
 
 namespace vkcv::shader {
 	
 	typedef typename event_function<ShaderStage, const std::filesystem::path&>::type ShaderCompiledFunction;
+	typedef typename event_function<ShaderProgram&>::type ShaderProgramCompiledFunction;
 	
 	class Compiler {
 	private:
@@ -25,6 +27,11 @@ namespace vkcv::shader {
 							 const ShaderCompiledFunction& compiled,
 							 const std::filesystem::path& includePath, bool update) = 0;
 		
+		void compileProgram(ShaderProgram& program,
+							const std::unordered_map<ShaderStage, const std::filesystem::path>& stages,
+							const ShaderProgramCompiledFunction& compiled,
+							const std::filesystem::path& includePath = "", bool update = false);
+		
 		std::string getDefine(const std::string& name) const;
 		
 		void setDefine(const std::string& name, const std::string& value);
diff --git a/modules/shader_compiler/src/vkcv/shader/Compiler.cpp b/modules/shader_compiler/src/vkcv/shader/Compiler.cpp
index f5ec0435ca8b82dc5f328921f43a39338d1be456..1467bf22616764e00399ada69d5f866b929416d5 100644
--- a/modules/shader_compiler/src/vkcv/shader/Compiler.cpp
+++ b/modules/shader_compiler/src/vkcv/shader/Compiler.cpp
@@ -3,6 +3,27 @@
 
 namespace vkcv::shader {
 	
+	void Compiler::compileProgram(ShaderProgram& program,
+								  const std::unordered_map<ShaderStage, const std::filesystem::path>& stages,
+								  const ShaderProgramCompiledFunction& compiled,
+								  const std::filesystem::path& includePath, bool update) {
+		for (const auto& stage : stages) {
+			compile(
+				stage.first,
+				stage.second,
+				[&program](ShaderStage shaderStage, const std::filesystem::path& path) {
+					program.addShader(shaderStage, path);
+				},
+				includePath,
+				update
+			);
+		}
+		
+		if (compiled) {
+			compiled(program);
+		}
+	}
+	
 	std::string Compiler::getDefine(const std::string &name) const {
 		return m_defines.at(name);
 	}
diff --git a/projects/bindless_textures/src/main.cpp b/projects/bindless_textures/src/main.cpp
index aa228b81cb18bfb3bc828a822561a26435cbf959..6aa0a9d6106dd06c08dd5219c9570430b49ce229 100644
--- a/projects/bindless_textures/src/main.cpp
+++ b/projects/bindless_textures/src/main.cpp
@@ -123,18 +123,12 @@ int main(int argc, const char** argv) {
 	vkcv::ShaderProgram firstMeshProgram;
 	vkcv::shader::GLSLCompiler compiler;
 	
-	compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/shader.vert"),
-					 [&firstMeshProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-		firstMeshProgram.addShader(shaderStage, path);
-	});
-	
-	compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/shader.frag"),
-					 [&firstMeshProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-		firstMeshProgram.addShader(shaderStage, path);
-	});
+	compiler.compileProgram(firstMeshProgram, {
+		{ vkcv::ShaderStage::VERTEX, "resources/shaders/shader.vert" },
+		{ vkcv::ShaderStage::FRAGMENT, "resources/shaders/shader.frag" }
+	}, nullptr);
  
 	auto& attributes = mesh.vertexGroups[0].vertexBuffer.attributes;
-
 	
 	std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) {
 		return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type);
diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp
index b825134279163b8aa22163077da9aca0c601bdda..3f4378a6a2187ba33b7965fd6d008577541f7351 100644
--- a/projects/first_mesh/src/main.cpp
+++ b/projects/first_mesh/src/main.cpp
@@ -73,18 +73,12 @@ int main(int argc, const char** argv) {
 	vkcv::ShaderProgram firstMeshProgram;
 	vkcv::shader::GLSLCompiler compiler;
 	
-	compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("assets/shaders/shader.vert"),
-					 [&firstMeshProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-		firstMeshProgram.addShader(shaderStage, path);
-	});
-	
-	compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("assets/shaders/shader.frag"),
-					 [&firstMeshProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-		firstMeshProgram.addShader(shaderStage, path);
-	});
+	compiler.compileProgram(firstMeshProgram, {
+		{ vkcv::ShaderStage::VERTEX, "assets/shaders/shader.vert" },
+		{ vkcv::ShaderStage::FRAGMENT, "assets/shaders/shader.frag" }
+	}, nullptr);
  
 	auto& attributes = mesh.vertexGroups[0].vertexBuffer.attributes;
-
 	
 	std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) {
 		return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type);
diff --git a/projects/first_scene/src/main.cpp b/projects/first_scene/src/main.cpp
index 3ec5b9764f70efa8911d8e772d6287e5efc8f056..3dd6fc40d516c6f1ae3358d60c02fc4ed219245a 100644
--- a/projects/first_scene/src/main.cpp
+++ b/projects/first_scene/src/main.cpp
@@ -61,15 +61,10 @@ int main(int argc, const char** argv) {
 	vkcv::ShaderProgram sceneShaderProgram;
 	vkcv::shader::GLSLCompiler compiler;
 	
-	compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("assets/shaders/shader.vert"),
-					 [&sceneShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-		sceneShaderProgram.addShader(shaderStage, path);
-	});
-	
-	compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("assets/shaders/shader.frag"),
-					 [&sceneShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-		sceneShaderProgram.addShader(shaderStage, path);
-	});
+	compiler.compileProgram(sceneShaderProgram, {
+		{ vkcv::ShaderStage::VERTEX, "assets/shaders/shader.vert" },
+		{ vkcv::ShaderStage::FRAGMENT, "assets/shaders/shader.frag" }
+	}, nullptr);
 
 	const std::vector<vkcv::VertexAttachment> vertexAttachments = sceneShaderProgram.getVertexAttachments();
 	std::vector<vkcv::VertexBinding> bindings;
diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp
index 1725b5c84dea931fe785880e3cd423cbb5f04a46..b4ba9046e07ecd3534844b493642098aa5847307 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -47,15 +47,10 @@ int main(int argc, const char** argv) {
 	vkcv::ShaderProgram triangleShaderProgram;
 	vkcv::shader::GLSLCompiler compiler;
 	
-	compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("shaders/shader.vert"),
-					 [&triangleShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-		 triangleShaderProgram.addShader(shaderStage, path);
-	});
-	
-	compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("shaders/shader.frag"),
-					 [&triangleShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-		triangleShaderProgram.addShader(shaderStage, path);
-	});
+	compiler.compileProgram(triangleShaderProgram, {
+		{vkcv::ShaderStage::VERTEX, "shaders/shader.vert"},
+		{ vkcv::ShaderStage::FRAGMENT, "shaders/shader.frag" }
+	}, nullptr);
 	
 	const auto swapchainExtent = core.getSwapchain(windowHandle).getExtent();
 
diff --git a/projects/wobble_bobble/src/main.cpp b/projects/wobble_bobble/src/main.cpp
index f9ebf9845470387fca3be3c57dd36e9f0310adbf..96c367f2f8e22ac65bf6ccf6a5c09eb238b5604d 100644
--- a/projects/wobble_bobble/src/main.cpp
+++ b/projects/wobble_bobble/src/main.cpp
@@ -433,57 +433,24 @@ int main(int argc, const char **argv) {
 	
 	vkcv::ShaderProgram gfxProgramGrid;
 	
-	compiler.compile(
-			vkcv::ShaderStage::VERTEX,
-			"shaders/grid.vert",
-			[&gfxProgramGrid](vkcv::ShaderStage stage, const std::filesystem::path& path) {
-				gfxProgramGrid.addShader(stage, path);
-			}
-	);
-	
-	compiler.compile(
-			vkcv::ShaderStage::FRAGMENT,
-			"shaders/grid.frag",
-			[&gfxProgramGrid](vkcv::ShaderStage stage, const std::filesystem::path& path) {
-				gfxProgramGrid.addShader(stage, path);
-			}
-	);
+	compiler.compileProgram(gfxProgramGrid, {
+			{ vkcv::ShaderStage::VERTEX, "shaders/grid.vert" },
+			{ vkcv::ShaderStage::FRAGMENT, "shaders/grid.frag" }
+	}, nullptr);
 	
 	vkcv::ShaderProgram gfxProgramParticles;
 	
-	compiler.compile(
-			vkcv::ShaderStage::VERTEX,
-			"shaders/particle.vert",
-			[&gfxProgramParticles](vkcv::ShaderStage stage, const std::filesystem::path& path) {
-				gfxProgramParticles.addShader(stage, path);
-			}
-	);
-	
-	compiler.compile(
-			vkcv::ShaderStage::FRAGMENT,
-			"shaders/particle.frag",
-			[&gfxProgramParticles](vkcv::ShaderStage stage, const std::filesystem::path& path) {
-				gfxProgramParticles.addShader(stage, path);
-			}
-	);
+	compiler.compileProgram(gfxProgramParticles, {
+		{ vkcv::ShaderStage::VERTEX, "shaders/particle.vert" },
+		{ vkcv::ShaderStage::FRAGMENT, "shaders/particle.frag" }
+	}, nullptr);
 	
 	vkcv::ShaderProgram gfxProgramLines;
 	
-	compiler.compile(
-			vkcv::ShaderStage::VERTEX,
-			"shaders/lines.vert",
-			[&gfxProgramLines](vkcv::ShaderStage stage, const std::filesystem::path& path) {
-				gfxProgramLines.addShader(stage, path);
-			}
-	);
-	
-	compiler.compile(
-			vkcv::ShaderStage::FRAGMENT,
-			"shaders/lines.frag",
-			[&gfxProgramLines](vkcv::ShaderStage stage, const std::filesystem::path& path) {
-				gfxProgramLines.addShader(stage, path);
-			}
-	);
+	compiler.compileProgram(gfxProgramLines, {
+			{ vkcv::ShaderStage::VERTEX, "shaders/lines.vert" },
+			{ vkcv::ShaderStage::FRAGMENT, "shaders/lines.frag" }
+	}, nullptr);
 	
 	vkcv::PassConfig passConfigGrid ({
 		vkcv::AttachmentDescription(