diff --git a/projects/sph/CMakeLists.txt b/projects/sph/CMakeLists.txt
index da5c40c84be26bec55983d77c8d993ece2ab189b..0ddc0409364f1fa8c9857ff3c005f5dcfe4f0219 100644
--- a/projects/sph/CMakeLists.txt
+++ b/projects/sph/CMakeLists.txt
@@ -16,7 +16,9 @@ add_executable(sph
 		src/Particle.hpp 
 		src/Particle.cpp
 		src/BloomAndFlares.hpp
-		src/BloomAndFlares.cpp)
+		src/BloomAndFlares.cpp
+		src/PipelineInit.hpp
+		src/PipelineInit.cpp)
 
 # this should fix the execution path to load local files from the project (for MSVC)
 if(MSVC)
diff --git a/projects/sph/src/PipelineInit.cpp b/projects/sph/src/PipelineInit.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..91b8a99c84e93b2fe6a3dbe16a776a0190636607
--- /dev/null
+++ b/projects/sph/src/PipelineInit.cpp
@@ -0,0 +1,27 @@
+#include "PipelineInit.hpp"
+
+vkcv::DescriptorSetHandle PipelineInit::ComputePipelineInit(vkcv::Core *pCore, vkcv::ShaderStage shaderStage, std::filesystem::path includePath,
+                                vkcv::PipelineHandle &pipeline) {
+    vkcv::ShaderProgram shaderProgram;
+    vkcv::shader::GLSLCompiler compiler;
+    compiler.compile(shaderStage, includePath,
+                     [&](vkcv::ShaderStage shaderStage1, const std::filesystem::path& path1) {shaderProgram.addShader(shaderStage1, path1);
+    });
+    vkcv::DescriptorSetLayoutHandle descriptorSetLayout = pCore->createDescriptorSetLayout(
+            shaderProgram.getReflectedDescriptors().at(0));
+    vkcv::DescriptorSetHandle descriptorSet = pCore->createDescriptorSet(descriptorSetLayout);
+
+    const std::vector<vkcv::VertexAttachment> vertexAttachments = shaderProgram.getVertexAttachments();
+
+    std::vector<vkcv::VertexBinding> bindings;
+    for (size_t i = 0; i < vertexAttachments.size(); i++) {
+        bindings.push_back(vkcv::VertexBinding(i, { vertexAttachments[i] }));
+    }
+    const vkcv::VertexLayout layout(bindings);
+
+    pipeline = pCore->createComputePipeline(
+            shaderProgram,
+            { pCore->getDescriptorSetLayout(descriptorSetLayout).vulkanHandle });
+
+    return  descriptorSet;
+}
\ No newline at end of file
diff --git a/projects/sph/src/PipelineInit.hpp b/projects/sph/src/PipelineInit.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..63c060a2496b4cd2ecffbf2c89817702d7cc1ebb
--- /dev/null
+++ b/projects/sph/src/PipelineInit.hpp
@@ -0,0 +1,12 @@
+#pragma once
+#include <vkcv/Core.hpp>
+#include <vkcv/shader/GLSLCompiler.hpp>
+#include <fstream>
+
+class PipelineInit{
+public:
+    static vkcv::DescriptorSetHandle ComputePipelineInit(vkcv::Core *pCore,
+                                                         vkcv::ShaderStage shaderStage,
+                                                         std::filesystem::path includePath,
+                                                         vkcv::PipelineHandle& pipeline);
+};
diff --git a/projects/sph/src/main.cpp b/projects/sph/src/main.cpp
index 5308a1dc2dee4ef6056b1ef8c299a9df58aa04bf..2248746b26c27c5443ec8889c5a4932ed9bc7e5a 100644
--- a/projects/sph/src/main.cpp
+++ b/projects/sph/src/main.cpp
@@ -9,6 +9,7 @@
 #include <time.h>
 #include <vkcv/shader/GLSLCompiler.hpp>
 #include "BloomAndFlares.hpp"
+#include "PipelineInit.hpp"
 
 int main(int argc, const char **argv) {
     const char *applicationName = "SPH";
@@ -55,41 +56,15 @@ int main(int argc, const char **argv) {
         return EXIT_FAILURE;
     }
 	vkcv::shader::GLSLCompiler compiler;
-// comp shader 1
-    vkcv::ShaderProgram computeShaderProgram1{};
-    compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/shader_water1.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-        computeShaderProgram1.addShader(shaderStage, path);
-    });
-
-    vkcv::DescriptorSetLayoutHandle computeDescriptorSetLayout1 = core.createDescriptorSetLayout(computeShaderProgram1.getReflectedDescriptors().at(0));
-    vkcv::DescriptorSetHandle computeDescriptorSet1 = core.createDescriptorSet(computeDescriptorSetLayout1);
-
-    const std::vector<vkcv::VertexAttachment> computeVertexAttachments1 = computeShaderProgram1.getVertexAttachments();
-
-    std::vector<vkcv::VertexBinding> computeBindings1;
-    for (size_t i = 0; i < computeVertexAttachments1.size(); i++) {
-        computeBindings1.push_back(vkcv::VertexBinding(i, { computeVertexAttachments1[i] }));
-    }
-    const vkcv::VertexLayout computeLayout1(computeBindings1);
-	vkcv::PipelineHandle computePipeline1 = core.createComputePipeline(computeShaderProgram1, {core.getDescriptorSetLayout(computeDescriptorSetLayout1).vulkanHandle} );
 
+// comp shader 1
+    vkcv::PipelineHandle computePipeline1;
+    vkcv::DescriptorSetHandle computeDescriptorSet1 = PipelineInit::ComputePipelineInit(&core, vkcv::ShaderStage::COMPUTE,
+                                                                          "shaders/shader_water1.comp", computePipeline1);
 // comp shader 2
-	vkcv::ShaderProgram computeShaderProgram2{};
-	compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/shader_water2.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-		computeShaderProgram2.addShader(shaderStage, path);
-	});
-
-	vkcv::DescriptorSetLayoutHandle computeDescriptorSetLayout2 = core.createDescriptorSetLayout(computeShaderProgram2.getReflectedDescriptors().at(0));
-	vkcv::DescriptorSetHandle computeDescriptorSet2 = core.createDescriptorSet(computeDescriptorSetLayout2);
-
-	const std::vector<vkcv::VertexAttachment> computeVertexAttachments2 = computeShaderProgram2.getVertexAttachments();
-
-	std::vector<vkcv::VertexBinding> computeBindings2;
-	for (size_t i = 0; i < computeVertexAttachments2.size(); i++) {
-		computeBindings2.push_back(vkcv::VertexBinding(i, { computeVertexAttachments2[i] }));
-	}
-	const vkcv::VertexLayout computeLayout2(computeBindings2);
-	vkcv::PipelineHandle computePipeline2 = core.createComputePipeline(computeShaderProgram2, {core.getDescriptorSetLayout(computeDescriptorSetLayout2).vulkanHandle} );
+    vkcv::PipelineHandle computePipeline2;
+    vkcv::DescriptorSetHandle computeDescriptorSet2 = PipelineInit::ComputePipelineInit(&core, vkcv::ShaderStage::COMPUTE,
+                                                                                        "shaders/shader_water2.comp", computePipeline2);
 
 // shader
     vkcv::ShaderProgram particleShaderProgram{};
@@ -241,16 +216,11 @@ int main(int argc, const char **argv) {
         bloomAndFlares.updateImageDimensions(width, height);
     });
 
-    vkcv::ShaderProgram tonemappingShader;
-    compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/tonemapping.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-        tonemappingShader.addShader(shaderStage, path);
-    });
+    //tone mapping shader & pipeline
+    vkcv::PipelineHandle tonemappingPipe;
+    vkcv::DescriptorSetHandle tonemappingDescriptor = PipelineInit::ComputePipelineInit(&core, vkcv::ShaderStage::COMPUTE,
+                                                                                        "shaders/tonemapping.comp", tonemappingPipe);
 
-    vkcv::DescriptorSetLayoutHandle tonemappingDescriptorLayout = core.createDescriptorSetLayout(tonemappingShader.getReflectedDescriptors().at(0));
-    vkcv::DescriptorSetHandle tonemappingDescriptor = core.createDescriptorSet(tonemappingDescriptorLayout);
-    vkcv::PipelineHandle tonemappingPipe = core.createComputePipeline(
-        tonemappingShader, 
-        { core.getDescriptorSetLayout(tonemappingDescriptorLayout).vulkanHandle });
 
     while (window.isWindowOpen()) {
         vkcv::Window::pollEvents();