diff --git a/.gitmodules b/.gitmodules
index d3b0a1404e8b1bb1a7b29154759d006adc8615e4..e1d38a91697947d5b87f0767f231f1dee48ec4ed 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -46,3 +46,6 @@
 [submodule "lib/VulkanMemoryAllocator"]
 	path = lib/VulkanMemoryAllocator
 	url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git
+[submodule "modules/tone_mapping/lib/glsl-tone-map"]
+	path = modules/tone_mapping/lib/glsl-tone-map
+	url = https://github.com/dmnsgn/glsl-tone-map.git
diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt
index fd3c4cfdfedff17e6952d8b2222aca37d0510d24..2d96cfe2e17c0d7276bf43ad9038054b924be88c 100644
--- a/modules/CMakeLists.txt
+++ b/modules/CMakeLists.txt
@@ -13,6 +13,7 @@ add_subdirectory(material)
 add_subdirectory(meshlet)
 add_subdirectory(scene)
 add_subdirectory(shader_compiler)
+add_subdirectory(tone_mapping)
 add_subdirectory(upscaling)
 
 message(STATUS "Modules:")
diff --git a/modules/effects/CMakeLists.txt b/modules/effects/CMakeLists.txt
index 94bb819d2a00443827ed40f5e015cc0745f61667..ba5a4ff4369adb4290c0ba253688ad4ddf461e16 100644
--- a/modules/effects/CMakeLists.txt
+++ b/modules/effects/CMakeLists.txt
@@ -14,6 +14,9 @@ set(vkcv_effects_sources
 		
 		${vkcv_effects_include}/vkcv/effects/BloomAndFlaresEffect.hpp
 		${vkcv_effects_source}/vkcv/effects/BloomAndFlaresEffect.cpp
+		
+		${vkcv_effects_include}/vkcv/effects/GammaCorrectionEffect.hpp
+		${vkcv_effects_source}/vkcv/effects/GammaCorrectionEffect.cpp
 )
 
 filter_headers(vkcv_effects_sources ${vkcv_effects_include} vkcv_effects_headers)
@@ -23,16 +26,19 @@ set(vkcv_effects_shaders ${PROJECT_SOURCE_DIR}/shaders)
 include_shader(${vkcv_effects_shaders}/bloomDownsample.comp ${vkcv_effects_include} ${vkcv_effects_source})
 include_shader(${vkcv_effects_shaders}/bloomFlaresComposite.comp ${vkcv_effects_include} ${vkcv_effects_source})
 include_shader(${vkcv_effects_shaders}/bloomUpsample.comp ${vkcv_effects_include} ${vkcv_effects_source})
+include_shader(${vkcv_effects_shaders}/gammaCorrection.comp ${vkcv_effects_include} ${vkcv_effects_source})
 include_shader(${vkcv_effects_shaders}/lensFlares.comp ${vkcv_effects_include} ${vkcv_effects_source})
 
 list(APPEND vkcv_effects_sources ${vkcv_effects_source}/bloomDownsample.comp.cxx)
 list(APPEND vkcv_effects_sources ${vkcv_effects_source}/bloomFlaresComposite.comp.cxx)
 list(APPEND vkcv_effects_sources ${vkcv_effects_source}/bloomUpsample.comp.cxx)
+list(APPEND vkcv_effects_sources ${vkcv_effects_source}/gammaCorrection.comp.cxx)
 list(APPEND vkcv_effects_sources ${vkcv_effects_source}/lensFlares.comp.cxx)
 
 list(APPEND vkcv_effects_sources ${vkcv_effects_include}/bloomDownsample.comp.hxx)
 list(APPEND vkcv_effects_sources ${vkcv_effects_include}/bloomFlaresComposite.comp.hxx)
 list(APPEND vkcv_effects_sources ${vkcv_effects_include}/bloomUpsample.comp.hxx)
+list(APPEND vkcv_effects_sources ${vkcv_effects_include}/gammaCorrection.comp.hxx)
 list(APPEND vkcv_effects_sources ${vkcv_effects_include}/lensFlares.comp.hxx)
 
 # adding source files to the project
@@ -40,10 +46,21 @@ add_library(vkcv_effects ${vkcv_build_attribute} ${vkcv_effects_sources})
 set_target_properties(vkcv_effects PROPERTIES PUBLIC_HEADER "${vkcv_effects_headers}")
 
 # link the required libraries to the module
-target_link_libraries(vkcv_effects ${vkcv_effects_libraries} vkcv vkcv_shader_compiler vkcv_camera vkcv_asset_loader)
+target_link_libraries(vkcv_effects
+		${vkcv_effects_libraries}
+		vkcv
+		vkcv_shader_compiler
+		vkcv_camera
+		vkcv_asset_loader)
 
 # including headers of dependencies and the VkCV framework
-target_include_directories(vkcv_effects SYSTEM BEFORE PRIVATE ${vkcv_effects_includes} ${vkcv_include} ${vkcv_includes} ${vkcv_shader_compiler_include} ${vkcv_camera_include} {vkcv_asset_loader_include})
+target_include_directories(vkcv_effects SYSTEM BEFORE PRIVATE
+		${vkcv_effects_includes}
+		${vkcv_include}
+		${vkcv_includes}
+		${vkcv_shader_compiler_include}
+		${vkcv_camera_include}
+		${vkcv_asset_loader_include})
 
 # add the own include directory for public headers
 target_include_directories(vkcv_effects BEFORE PUBLIC ${vkcv_effects_include})
diff --git a/modules/effects/include/vkcv/effects/BloomAndFlaresEffect.hpp b/modules/effects/include/vkcv/effects/BloomAndFlaresEffect.hpp
index f4a9d03d8afcaf3532570a0807b246a0d641e02d..71b8d1e4d9e13189a37bc06b6cd7e7ab4a58db8d 100644
--- a/modules/effects/include/vkcv/effects/BloomAndFlaresEffect.hpp
+++ b/modules/effects/include/vkcv/effects/BloomAndFlaresEffect.hpp
@@ -7,6 +7,11 @@
 
 namespace vkcv::effects {
 	
+	/**
+     * @addtogroup vkcv_effects
+     * @{
+     */
+	
 	class BloomAndFlaresEffect : public Effect {
 	private:
 		bool m_advanced;
@@ -57,8 +62,8 @@ namespace vkcv::effects {
 							   const ImageHandle &output);
 		
 	public:
-		BloomAndFlaresEffect(Core& core,
-							 bool advanced = false);
+		explicit BloomAndFlaresEffect(Core& core,
+									  bool advanced = false);
 		
 		void recordEffect(const CommandStreamHandle &cmdStream,
 						  const ImageHandle &input,
@@ -70,4 +75,6 @@ namespace vkcv::effects {
 		
 	};
 	
+	/** @} */
+	
 }
diff --git a/modules/effects/include/vkcv/effects/Effect.hpp b/modules/effects/include/vkcv/effects/Effect.hpp
index d068fb59404080aa421e49f6a183844e731c1c5e..f81d3d7b9e4190834f6fa40632b631131341121c 100644
--- a/modules/effects/include/vkcv/effects/Effect.hpp
+++ b/modules/effects/include/vkcv/effects/Effect.hpp
@@ -5,6 +5,12 @@
 
 namespace vkcv::effects {
 	
+	/**
+     * @defgroup vkcv_effects Effects Module
+     * A module to apply certain post-processing effects to an image in realtime.
+     * @{
+     */
+	
 	class Effect {
 	protected:
 		Core& m_core;
@@ -20,4 +26,6 @@ namespace vkcv::effects {
 		
 	};
 	
+	/** @} */
+	
 }
diff --git a/modules/effects/include/vkcv/effects/GammaCorrectionEffect.hpp b/modules/effects/include/vkcv/effects/GammaCorrectionEffect.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1c41bdd3cd43b3c696ea6daf74df8e4aa5278fa5
--- /dev/null
+++ b/modules/effects/include/vkcv/effects/GammaCorrectionEffect.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "Effect.hpp"
+
+namespace vkcv::effects {
+	
+	/**
+     * @addtogroup vkcv_effects
+     * @{
+     */
+	
+	class GammaCorrectionEffect : public Effect {
+	private:
+		float m_gamma;
+		
+		ComputePipelineHandle m_pipeline;
+		
+		DescriptorSetLayoutHandle m_descriptorSetLayout;
+		
+		DescriptorSetHandle m_descriptorSet;
+	
+	public:
+		explicit GammaCorrectionEffect(Core& core);
+		
+		void recordEffect(const CommandStreamHandle& cmdStream,
+						  const ImageHandle& input,
+						  const ImageHandle& output) override;
+		
+		void setGamma(float gamma);
+		
+		[[nodiscard]]
+		float getGamma() const;
+		
+	};
+	
+	/** @} */
+	
+}
diff --git a/modules/effects/shaders/gammaCorrection.comp b/modules/effects/shaders/gammaCorrection.comp
new file mode 100644
index 0000000000000000000000000000000000000000..392ac8e0c80cff5ac821cadb6a23d981f21420e5
--- /dev/null
+++ b/modules/effects/shaders/gammaCorrection.comp
@@ -0,0 +1,27 @@
+#version 450
+
+layout(set=0, binding=0, rgba8) restrict readonly uniform image2D inImage;
+layout(set=0, binding=1, rgba8) restrict writeonly uniform image2D outImage;
+
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+
+layout( push_constant ) uniform constants {
+    float gamma;
+};
+
+void main() {
+    if (any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(inImage)))) {
+        return;
+    }
+
+    if (any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage)))) {
+        return;
+    }
+
+    ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
+    vec3 color = imageLoad(inImage, uv).xyz;
+
+    color = pow(color, vec3(1.0f / gamma));
+
+    imageStore(outImage, uv, vec4(color, 0.f));
+}
\ No newline at end of file
diff --git a/modules/effects/src/vkcv/effects/GammaCorrectionEffect.cpp b/modules/effects/src/vkcv/effects/GammaCorrectionEffect.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..088a3dbd97ae9654cdbfe6355b4837310c51bddc
--- /dev/null
+++ b/modules/effects/src/vkcv/effects/GammaCorrectionEffect.cpp
@@ -0,0 +1,100 @@
+
+#include "vkcv/effects/GammaCorrectionEffect.hpp"
+
+#include <vkcv/shader/GLSLCompiler.hpp>
+
+#include "gammaCorrection.comp.hxx"
+
+namespace vkcv::effects {
+	
+	static DescriptorBindings getDescriptorBindings() {
+		DescriptorBindings descriptorBindings = {};
+		
+		auto binding_0 = DescriptorBinding {
+				0,
+				DescriptorType::IMAGE_STORAGE,
+				1,
+				ShaderStage::COMPUTE,
+				false,
+				false
+		};
+		
+		auto binding_1 = DescriptorBinding {
+				1,
+				DescriptorType::IMAGE_STORAGE,
+				1,
+				ShaderStage::COMPUTE,
+				false,
+				false
+		};
+		
+		descriptorBindings.insert(std::make_pair(0, binding_0));
+		descriptorBindings.insert(std::make_pair(1, binding_1));
+		
+		return descriptorBindings;
+	}
+	
+	GammaCorrectionEffect::GammaCorrectionEffect(Core &core)
+	: Effect(core), m_gamma(2.2f), m_descriptorSetLayout(), m_descriptorSet(), m_pipeline() {
+		vkcv::shader::GLSLCompiler compiler;
+		ShaderProgram program;
+		
+		compiler.compileSource(
+			ShaderStage::COMPUTE,
+			GAMMACORRECTION_COMP_SHADER.c_str(),
+			[&program](ShaderStage stage, const std::filesystem::path &path) {
+				program.addShader(stage, path);
+			}
+		);
+		
+		m_descriptorSetLayout = m_core.createDescriptorSetLayout(getDescriptorBindings());
+		m_descriptorSet = m_core.createDescriptorSet(m_descriptorSetLayout);
+		m_pipeline = m_core.createComputePipeline({
+			program,
+			{ m_descriptorSetLayout }
+		});
+	}
+	
+	void GammaCorrectionEffect::recordEffect(const CommandStreamHandle &cmdStream,
+											 const ImageHandle &input,
+											 const ImageHandle &output) {
+		m_core.recordBeginDebugLabel(cmdStream, "Gamma Correction", std::array<float, 4>{
+				0.95f, 0.95f, 0.95f, 1.0f
+		});
+		
+		m_core.prepareImageForStorage(cmdStream, input);
+		m_core.prepareImageForStorage(cmdStream, output);
+		
+		vkcv::DescriptorWrites writes;
+		
+		writes.writeStorageImage(0, input);
+		writes.writeStorageImage(1, output);
+		
+		m_core.writeDescriptorSet(m_descriptorSet, writes);
+		
+		const uint32_t width = m_core.getImageWidth(output);
+		const uint32_t height = m_core.getImageHeight(output);
+		
+		m_core.recordComputeDispatchToCmdStream(
+				cmdStream,
+				m_pipeline,
+				dispatchInvocations(
+						DispatchSize(width, height),
+						DispatchSize(8, 8)
+				),
+				{ useDescriptorSet(0, m_descriptorSet) },
+				pushConstants<float>(m_gamma)
+		);
+		
+		m_core.recordEndDebugLabel(cmdStream);
+	}
+	
+	void GammaCorrectionEffect::setGamma(float gamma) {
+		m_gamma = std::max(gamma, std::numeric_limits<float>::epsilon());
+	}
+	
+	float GammaCorrectionEffect::getGamma() const {
+		return m_gamma;
+	}
+	
+}
diff --git a/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp b/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp
index face4cba350122f22feee6bae878397dccf4a070..0cb3a39436675e722f818643808b769189165793 100644
--- a/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp
+++ b/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp
@@ -76,7 +76,7 @@ namespace vkcv::shader {
          */
 		bool compileSource(ShaderStage shaderStage, const char* shaderSource,
 						   const ShaderCompiledFunction& compiled,
-						   const std::filesystem::path& includePath) override;
+						   const std::filesystem::path& includePath = "") override;
 
         /**
          * Compile a GLSL shader from a specific file path for a target stage with
diff --git a/modules/tone_mapping/CMakeLists.txt b/modules/tone_mapping/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a63d74650b22716c49eda3842b7fcea6dba4b9cd
--- /dev/null
+++ b/modules/tone_mapping/CMakeLists.txt
@@ -0,0 +1,83 @@
+cmake_minimum_required(VERSION 3.16)
+project(vkcv_tone_mapping)
+
+# setting c++ standard for the project
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(vkcv_tone_mapping_source ${PROJECT_SOURCE_DIR}/src)
+set(vkcv_tone_mapping_include ${PROJECT_SOURCE_DIR}/include)
+
+set(vkcv_tone_mapping_sources
+		${vkcv_tone_mapping_include}/vkcv/tone/ToneMapping.hpp
+		${vkcv_tone_mapping_source}/vkcv/tone/ToneMapping.cpp
+		
+		${vkcv_tone_mapping_include}/vkcv/tone/ACESToneMapping.hpp
+		${vkcv_tone_mapping_source}/vkcv/tone/ACESToneMapping.cpp
+		
+		${vkcv_tone_mapping_include}/vkcv/tone/FilmicToneMapping.hpp
+		${vkcv_tone_mapping_source}/vkcv/tone/FilmicToneMapping.cpp
+		
+		${vkcv_tone_mapping_include}/vkcv/tone/LottesToneMapping.hpp
+		${vkcv_tone_mapping_source}/vkcv/tone/LottesToneMapping.cpp
+		
+		${vkcv_tone_mapping_include}/vkcv/tone/ReinhardToneMapping.hpp
+		${vkcv_tone_mapping_source}/vkcv/tone/ReinhardToneMapping.cpp
+		
+		${vkcv_tone_mapping_include}/vkcv/tone/Reinhard2ToneMapping.hpp
+		${vkcv_tone_mapping_source}/vkcv/tone/Reinhard2ToneMapping.cpp
+		
+		${vkcv_tone_mapping_include}/vkcv/tone/UchimuraToneMapping.hpp
+		${vkcv_tone_mapping_source}/vkcv/tone/UchimuraToneMapping.cpp
+		
+		${vkcv_tone_mapping_include}/vkcv/tone/Uncharted2ToneMapping.hpp
+		${vkcv_tone_mapping_source}/vkcv/tone/Uncharted2ToneMapping.cpp
+		
+		${vkcv_tone_mapping_include}/vkcv/tone/UnrealToneMapping.hpp
+		${vkcv_tone_mapping_source}/vkcv/tone/UnrealToneMapping.cpp
+)
+
+filter_headers(vkcv_tone_mapping_sources ${vkcv_tone_mapping_include} vkcv_tone_mapping_headers)
+
+# Setup some path variables to load libraries
+set(vkcv_tone_mapping_lib lib)
+set(vkcv_tone_mapping_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_tone_mapping_lib})
+
+# Check and load glsl-tone-map
+include(config/GLSL_TONE_MAP.cmake)
+
+# Add compile definitions depending on the build context of the module
+add_compile_definitions(${vkcv_tone_mapping_definitions})
+
+# adding source files to the project
+add_library(vkcv_tone_mapping ${vkcv_build_attribute} ${vkcv_tone_mapping_sources})
+set_target_properties(vkcv_tone_mapping PROPERTIES PUBLIC_HEADER "${vkcv_tone_mapping_headers}")
+
+# link the required libraries to the module
+target_link_libraries(vkcv_tone_mapping
+		${vkcv_tone_mapping_libraries}
+		vkcv
+		vkcv_shader_compiler
+)
+
+# including headers of dependencies and the VkCV framework
+target_include_directories(vkcv_tone_mapping SYSTEM BEFORE PRIVATE
+		${vkcv_tone_mapping_includes}
+		${vkcv_include}
+		${vkcv_includes}
+		${vkcv_shader_compiler_include}
+)
+
+# add the own include directory for public headers
+target_include_directories(vkcv_tone_mapping BEFORE PUBLIC ${vkcv_tone_mapping_include})
+
+if (vkcv_parent_scope)
+	list(APPEND vkcv_modules_includes ${vkcv_tone_mapping_include})
+	list(APPEND vkcv_modules_libraries vkcv_tone_mapping)
+	
+	set(vkcv_modules_includes ${vkcv_modules_includes} PARENT_SCOPE)
+	set(vkcv_modules_libraries ${vkcv_modules_libraries} PARENT_SCOPE)
+endif()
+
+install(TARGETS vkcv_tone_mapping PUBLIC_HEADER DESTINATION
+		${CMAKE_INSTALL_INCLUDEDIR}/vkcv/tone)
diff --git a/modules/tone_mapping/config/GLSL_TONE_MAP.cmake b/modules/tone_mapping/config/GLSL_TONE_MAP.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..814344132d7e9f14e3e6df58544b9d7e88ee3677
--- /dev/null
+++ b/modules/tone_mapping/config/GLSL_TONE_MAP.cmake
@@ -0,0 +1,54 @@
+
+use_git_submodule("${vkcv_tone_mapping_lib_path}/glsl-tone-map" glsl_tone_map_status)
+
+if (${glsl_tone_map_status})
+	include_shader(${vkcv_tone_mapping_lib_path}/glsl-tone-map/aces.glsl
+			${vkcv_tone_mapping_include}
+			${vkcv_tone_mapping_source})
+	
+	include_shader(${vkcv_tone_mapping_lib_path}/glsl-tone-map/filmic.glsl
+			${vkcv_tone_mapping_include}
+			${vkcv_tone_mapping_source})
+	
+	include_shader(${vkcv_tone_mapping_lib_path}/glsl-tone-map/lottes.glsl
+			${vkcv_tone_mapping_include}
+			${vkcv_tone_mapping_source})
+	
+	include_shader(${vkcv_tone_mapping_lib_path}/glsl-tone-map/reinhard.glsl
+			${vkcv_tone_mapping_include}
+			${vkcv_tone_mapping_source})
+	
+	include_shader(${vkcv_tone_mapping_lib_path}/glsl-tone-map/reinhard2.glsl
+			${vkcv_tone_mapping_include}
+			${vkcv_tone_mapping_source})
+	
+	include_shader(${vkcv_tone_mapping_lib_path}/glsl-tone-map/uchimura.glsl
+			${vkcv_tone_mapping_include}
+			${vkcv_tone_mapping_source})
+	
+	include_shader(${vkcv_tone_mapping_lib_path}/glsl-tone-map/uncharted2.glsl
+			${vkcv_tone_mapping_include}
+			${vkcv_tone_mapping_source})
+	
+	include_shader(${vkcv_tone_mapping_lib_path}/glsl-tone-map/unreal.glsl
+			${vkcv_tone_mapping_include}
+			${vkcv_tone_mapping_source})
+	
+	list(APPEND vkcv_tone_mapping_sources ${vkcv_tone_mapping_source}/aces.glsl.cxx)
+	list(APPEND vkcv_tone_mapping_sources ${vkcv_tone_mapping_source}/filmic.glsl.cxx)
+	list(APPEND vkcv_tone_mapping_sources ${vkcv_tone_mapping_source}/lottes.glsl.cxx)
+	list(APPEND vkcv_tone_mapping_sources ${vkcv_tone_mapping_source}/reinhard.glsl.cxx)
+	list(APPEND vkcv_tone_mapping_sources ${vkcv_tone_mapping_source}/reinhard2.glsl.cxx)
+	list(APPEND vkcv_tone_mapping_sources ${vkcv_tone_mapping_source}/uchimura.glsl.cxx)
+	list(APPEND vkcv_tone_mapping_sources ${vkcv_tone_mapping_source}/uncharted2.glsl.cxx)
+	list(APPEND vkcv_tone_mapping_sources ${vkcv_tone_mapping_source}/unreal.glsl.cxx)
+	
+	list(APPEND vkcv_tone_mapping_sources ${vkcv_tone_mapping_include}/aces.glsl.hxx)
+	list(APPEND vkcv_tone_mapping_sources ${vkcv_tone_mapping_include}/filmic.glsl.hxx)
+	list(APPEND vkcv_tone_mapping_sources ${vkcv_tone_mapping_include}/lottes.glsl.hxx)
+	list(APPEND vkcv_tone_mapping_sources ${vkcv_tone_mapping_include}/reinhard.glsl.hxx)
+	list(APPEND vkcv_tone_mapping_sources ${vkcv_tone_mapping_include}/reinhard2.glsl.hxx)
+	list(APPEND vkcv_tone_mapping_sources ${vkcv_tone_mapping_include}/uchimura.glsl.hxx)
+	list(APPEND vkcv_tone_mapping_sources ${vkcv_tone_mapping_include}/uncharted2.glsl.hxx)
+	list(APPEND vkcv_tone_mapping_sources ${vkcv_tone_mapping_include}/unreal.glsl.hxx)
+endif()
diff --git a/modules/tone_mapping/include/vkcv/tone/ACESToneMapping.hpp b/modules/tone_mapping/include/vkcv/tone/ACESToneMapping.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..de1c02c5d9efa070aadeff5003c7e15e232af7ee
--- /dev/null
+++ b/modules/tone_mapping/include/vkcv/tone/ACESToneMapping.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "ToneMapping.hpp"
+
+namespace vkcv::tone {
+	
+	/**
+     * @addtogroup vkcv_tone
+     * @{
+     */
+	
+	class ACESToneMapping : public ToneMapping {
+	private:
+		void initToneMapping() override;
+		
+	public:
+		explicit ACESToneMapping(Core& core, bool normalize = false);
+		
+	};
+	
+	/** @} */
+	
+}
diff --git a/modules/tone_mapping/include/vkcv/tone/FilmicToneMapping.hpp b/modules/tone_mapping/include/vkcv/tone/FilmicToneMapping.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c25e1b74f7d1f78f74e84399bccc0beb063c7c12
--- /dev/null
+++ b/modules/tone_mapping/include/vkcv/tone/FilmicToneMapping.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "ToneMapping.hpp"
+
+namespace vkcv::tone {
+	
+	/**
+     * @addtogroup vkcv_tone
+     * @{
+     */
+	
+	class FilmicToneMapping : public ToneMapping {
+	private:
+		void initToneMapping() override;
+	
+	public:
+		explicit FilmicToneMapping(Core& core, bool normalize = false);
+		
+	};
+	
+	/** @} */
+	
+}
diff --git a/modules/tone_mapping/include/vkcv/tone/LottesToneMapping.hpp b/modules/tone_mapping/include/vkcv/tone/LottesToneMapping.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..37dad90397bdf8f3ae1e359a5961b65d085d651b
--- /dev/null
+++ b/modules/tone_mapping/include/vkcv/tone/LottesToneMapping.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "ToneMapping.hpp"
+
+namespace vkcv::tone {
+	
+	/**
+     * @addtogroup vkcv_tone
+     * @{
+     */
+	
+	class LottesToneMapping : public ToneMapping {
+	private:
+		void initToneMapping() override;
+	
+	public:
+		explicit LottesToneMapping(Core& core, bool normalize = false);
+		
+	};
+	
+	/** @} */
+	
+}
diff --git a/modules/tone_mapping/include/vkcv/tone/Reinhard2ToneMapping.hpp b/modules/tone_mapping/include/vkcv/tone/Reinhard2ToneMapping.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9bcf9f9e28a734b7de481e9dd90ffae3e98254bd
--- /dev/null
+++ b/modules/tone_mapping/include/vkcv/tone/Reinhard2ToneMapping.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "ToneMapping.hpp"
+
+namespace vkcv::tone {
+	
+	/**
+     * @addtogroup vkcv_tone
+     * @{
+     */
+	
+	class Reinhard2ToneMapping : public ToneMapping {
+	private:
+		void initToneMapping() override;
+	
+	public:
+		explicit Reinhard2ToneMapping(Core& core, bool normalize = false);
+		
+	};
+	
+	/** @} */
+	
+}
diff --git a/modules/tone_mapping/include/vkcv/tone/ReinhardToneMapping.hpp b/modules/tone_mapping/include/vkcv/tone/ReinhardToneMapping.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ac7fb5c9e3856de5aeb5f743793a0668c55b2bf1
--- /dev/null
+++ b/modules/tone_mapping/include/vkcv/tone/ReinhardToneMapping.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "ToneMapping.hpp"
+
+namespace vkcv::tone {
+	
+	/**
+     * @addtogroup vkcv_tone
+     * @{
+     */
+	
+	class ReinhardToneMapping : public ToneMapping {
+	private:
+		void initToneMapping() override;
+	
+	public:
+		explicit ReinhardToneMapping(Core& core, bool normalize = false);
+		
+	};
+	
+	/** @} */
+	
+}
diff --git a/modules/tone_mapping/include/vkcv/tone/ToneMapping.hpp b/modules/tone_mapping/include/vkcv/tone/ToneMapping.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..00e8c287c8bb2bca503361e2e249d0a19b4edbf4
--- /dev/null
+++ b/modules/tone_mapping/include/vkcv/tone/ToneMapping.hpp
@@ -0,0 +1,93 @@
+#pragma once
+
+#include <vkcv/Core.hpp>
+
+namespace vkcv::tone {
+	
+	/**
+     * @defgroup vkcv_tone Tone Mapping Module
+     * A module to apply tone mapping to an image in realtime.
+     * @{
+     */
+	
+	class ToneMapping {
+	private:
+		/**
+         * Reference to the current Core instance.
+         */
+		Core& m_core;
+		
+		/**
+		 * The name of the tone mapping instance.
+		 */
+		std::string m_name;
+		
+		/**
+		 * Flag whether tone mapping should normalize before mapping.
+		 */
+		bool m_normalize;
+		
+		/**
+		 * The compute pipeline of the tone mapping instance.
+		 */
+		ComputePipelineHandle m_pipeline;
+		
+		/**
+         * The descriptor set layout of the tone mapping pipeline.
+         */
+		DescriptorSetLayoutHandle m_descriptorSetLayout;
+		
+		/**
+		 * The descriptor set for the tone mapping pipeline.
+		 */
+		DescriptorSetHandle m_descriptorSet;
+		
+	protected:
+		ShaderProgram compileShaderProgram(const std::string& functionName,
+										   const std::string& functionSource);
+		
+		void buildComputePipeline(const std::string& functionName,
+								  const std::string& functionSource);
+		
+		virtual void initToneMapping() = 0;
+		
+	public:
+		/**
+         * Constructor to create an tone mapping instance.
+         *
+         * @param[in,out] core Reference to a Core instance
+         * @param[in] name Name of the tone mapping function
+         * @param[in] normalize (Optional) Flag to normalize color values
+         */
+		explicit ToneMapping(Core& core,
+							 const std::string& name,
+							 bool normalize = false);
+	
+		~ToneMapping() = default;
+		
+		/**
+		 * Return name of the tone mapping instance.
+		 *
+		 * @return Name of the tone mapping
+		 */
+		[[nodiscard]]
+		const std::string& getName() const;
+		
+		/**
+		 * Record the commands of the given tone mapping instance to
+		 * process the image of the input handle mapping its colors into
+		 * the regarding output image handle.
+		 *
+		 * @param[in] cmdStream Command stream handle to record commands
+		 * @param[in] input Input image handle
+		 * @param[in] output Output image handle
+		 */
+		void recordToneMapping(const CommandStreamHandle& cmdStream,
+							   const ImageHandle& input,
+							   const ImageHandle& output);
+		
+	};
+	
+	/** @} */
+	
+}
diff --git a/modules/tone_mapping/include/vkcv/tone/UchimuraToneMapping.hpp b/modules/tone_mapping/include/vkcv/tone/UchimuraToneMapping.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..83afa0c2cfa2038c64bf78a74b8886fd5aa2d54e
--- /dev/null
+++ b/modules/tone_mapping/include/vkcv/tone/UchimuraToneMapping.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "ToneMapping.hpp"
+
+namespace vkcv::tone {
+	
+	/**
+     * @addtogroup vkcv_tone
+     * @{
+     */
+	
+	class UchimuraToneMapping : public ToneMapping {
+	private:
+		void initToneMapping() override;
+	
+	public:
+		explicit UchimuraToneMapping(Core& core, bool normalize = false);
+		
+	};
+	
+	/** @} */
+	
+}
diff --git a/modules/tone_mapping/include/vkcv/tone/Uncharted2ToneMapping.hpp b/modules/tone_mapping/include/vkcv/tone/Uncharted2ToneMapping.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..fc1db5b4b5e66ff584df9679cc9b8a0ddad89ec4
--- /dev/null
+++ b/modules/tone_mapping/include/vkcv/tone/Uncharted2ToneMapping.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "ToneMapping.hpp"
+
+namespace vkcv::tone {
+	
+	/**
+     * @addtogroup vkcv_tone
+     * @{
+     */
+	
+	class Uncharted2ToneMapping : public ToneMapping {
+	private:
+		void initToneMapping() override;
+	
+	public:
+		explicit Uncharted2ToneMapping(Core& core, bool normalize = false);
+		
+	};
+	
+	/** @} */
+	
+}
diff --git a/modules/tone_mapping/include/vkcv/tone/UnrealToneMapping.hpp b/modules/tone_mapping/include/vkcv/tone/UnrealToneMapping.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..10fb9cc6b52feb543fe1b8fb61f5b0b91e6e5acc
--- /dev/null
+++ b/modules/tone_mapping/include/vkcv/tone/UnrealToneMapping.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "ToneMapping.hpp"
+
+namespace vkcv::tone {
+	
+	/**
+     * @addtogroup vkcv_tone
+     * @{
+     */
+	
+	class UnrealToneMapping : public ToneMapping {
+	private:
+		void initToneMapping() override;
+	
+	public:
+		explicit UnrealToneMapping(Core& core, bool normalize = false);
+		
+	};
+	
+	/** @} */
+	
+}
diff --git a/modules/tone_mapping/lib/glsl-tone-map b/modules/tone_mapping/lib/glsl-tone-map
new file mode 160000
index 0000000000000000000000000000000000000000..e3d822adee738de936da5d9541dff266f899ed46
--- /dev/null
+++ b/modules/tone_mapping/lib/glsl-tone-map
@@ -0,0 +1 @@
+Subproject commit e3d822adee738de936da5d9541dff266f899ed46
diff --git a/modules/tone_mapping/src/vkcv/tone/ACESToneMapping.cpp b/modules/tone_mapping/src/vkcv/tone/ACESToneMapping.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4835fdcd6ef2acd22d4d867e284f90fc5ba6546b
--- /dev/null
+++ b/modules/tone_mapping/src/vkcv/tone/ACESToneMapping.cpp
@@ -0,0 +1,17 @@
+
+#include "vkcv/tone/ACESToneMapping.hpp"
+
+#include "aces.glsl.hxx"
+
+namespace vkcv::tone {
+	
+	void ACESToneMapping::initToneMapping() {
+		buildComputePipeline("aces", ACES_GLSL_SHADER);
+	}
+	
+	ACESToneMapping::ACESToneMapping(Core &core, bool normalize)
+	: ToneMapping(core, "ACES Tone Mapping", normalize) {
+		initToneMapping();
+	}
+	
+}
diff --git a/modules/tone_mapping/src/vkcv/tone/FilmicToneMapping.cpp b/modules/tone_mapping/src/vkcv/tone/FilmicToneMapping.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3d93ada18d39fcea59cc0258360fbea79338a631
--- /dev/null
+++ b/modules/tone_mapping/src/vkcv/tone/FilmicToneMapping.cpp
@@ -0,0 +1,17 @@
+
+#include "vkcv/tone/FilmicToneMapping.hpp"
+
+#include "filmic.glsl.hxx"
+
+namespace vkcv::tone {
+	
+	void FilmicToneMapping::initToneMapping() {
+		buildComputePipeline("filmic", FILMIC_GLSL_SHADER);
+	}
+	
+	FilmicToneMapping::FilmicToneMapping(Core &core, bool normalize)
+	: ToneMapping(core, "Filmic Tone Mapping", normalize) {
+		initToneMapping();
+	}
+
+}
diff --git a/modules/tone_mapping/src/vkcv/tone/LottesToneMapping.cpp b/modules/tone_mapping/src/vkcv/tone/LottesToneMapping.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9ae38055831164933e009229d0e6510aaf99d1e5
--- /dev/null
+++ b/modules/tone_mapping/src/vkcv/tone/LottesToneMapping.cpp
@@ -0,0 +1,17 @@
+
+#include "vkcv/tone/LottesToneMapping.hpp"
+
+#include "lottes.glsl.hxx"
+
+namespace vkcv::tone {
+	
+	void LottesToneMapping::initToneMapping() {
+		buildComputePipeline("lottes", LOTTES_GLSL_SHADER);
+	}
+	
+	LottesToneMapping::LottesToneMapping(Core &core, bool normalize)
+	: ToneMapping(core, "Lottes Tone Mapping", normalize) {
+		initToneMapping();
+	}
+
+}
diff --git a/modules/tone_mapping/src/vkcv/tone/Reinhard2ToneMapping.cpp b/modules/tone_mapping/src/vkcv/tone/Reinhard2ToneMapping.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e81b952654337b49e310111ab08f345cf2106bcb
--- /dev/null
+++ b/modules/tone_mapping/src/vkcv/tone/Reinhard2ToneMapping.cpp
@@ -0,0 +1,17 @@
+
+#include "vkcv/tone/Reinhard2ToneMapping.hpp"
+
+#include "reinhard2.glsl.hxx"
+
+namespace vkcv::tone {
+	
+	void Reinhard2ToneMapping::initToneMapping() {
+		buildComputePipeline("reinhard2", REINHARD2_GLSL_SHADER);
+	}
+	
+	Reinhard2ToneMapping::Reinhard2ToneMapping(Core &core, bool normalize)
+	: ToneMapping(core, "Reinhard2 Tone Mapping", normalize) {
+		initToneMapping();
+	}
+
+}
diff --git a/modules/tone_mapping/src/vkcv/tone/ReinhardToneMapping.cpp b/modules/tone_mapping/src/vkcv/tone/ReinhardToneMapping.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..258550d80c6b099e84cf93541aa01aa157a3645c
--- /dev/null
+++ b/modules/tone_mapping/src/vkcv/tone/ReinhardToneMapping.cpp
@@ -0,0 +1,17 @@
+
+#include "vkcv/tone/ReinhardToneMapping.hpp"
+
+#include "reinhard.glsl.hxx"
+
+namespace vkcv::tone {
+	
+	void ReinhardToneMapping::initToneMapping() {
+		buildComputePipeline("reinhard", REINHARD_GLSL_SHADER);
+	}
+	
+	ReinhardToneMapping::ReinhardToneMapping(Core &core, bool normalize)
+	: ToneMapping(core, "Reinhard Tone Mapping", normalize) {
+		initToneMapping();
+	}
+
+}
diff --git a/modules/tone_mapping/src/vkcv/tone/ToneMapping.cpp b/modules/tone_mapping/src/vkcv/tone/ToneMapping.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3323d8797c34afd2890c433c5849bd8fdf9c807e
--- /dev/null
+++ b/modules/tone_mapping/src/vkcv/tone/ToneMapping.cpp
@@ -0,0 +1,144 @@
+
+#include "vkcv/tone/ToneMapping.hpp"
+
+#include <vkcv/shader/GLSLCompiler.hpp>
+
+#include <sstream>
+
+namespace vkcv::tone {
+	
+	static DescriptorBindings getDescriptorBindings() {
+		DescriptorBindings descriptorBindings = {};
+		
+		auto binding_0 = DescriptorBinding {
+				0,
+				DescriptorType::IMAGE_STORAGE,
+				1,
+				ShaderStage::COMPUTE,
+				false,
+				false
+		};
+		
+		auto binding_1 = DescriptorBinding {
+				1,
+				DescriptorType::IMAGE_STORAGE,
+				1,
+				ShaderStage::COMPUTE,
+				false,
+				false
+		};
+		
+		descriptorBindings.insert(std::make_pair(0, binding_0));
+		descriptorBindings.insert(std::make_pair(1, binding_1));
+		
+		return descriptorBindings;
+	}
+	
+	ShaderProgram ToneMapping::compileShaderProgram(const std::string &functionName,
+													const std::string &functionSource) {
+		vkcv::shader::GLSLCompiler compiler;
+		ShaderProgram program;
+		
+		std::ostringstream stream;
+		stream << "#version 450" << std::endl;
+		stream << "layout(set=0, binding=0, rgba16f) restrict readonly uniform image2D inImage;" << std::endl;
+		stream << "layout(set=0, binding=1, rgba8) restrict writeonly uniform image2D outImage;" << std::endl;
+		stream << "layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;" << std::endl;
+		stream << functionSource << std::endl;
+		stream << "void main() {" << std::endl;
+		stream << "  if (any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(inImage)))) {" << std::endl;
+		stream << "    return;" << std::endl;
+		stream << "  }" << std::endl;
+		stream << "  if (any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage)))) {" << std::endl;
+		stream << "    return;" << std::endl;
+		stream << "  }" << std::endl;
+		stream << "  ivec2 uv = ivec2(gl_GlobalInvocationID.xy);" << std::endl;
+		stream << "  vec4 color = imageLoad(inImage, uv);" << std::endl;
+		
+		if (m_normalize) {
+			stream << "  color /= color.w;" << std::endl;
+		}
+		
+		stream << "  color = vec4(" << functionName << "(color.xyz), color.w);" << std::endl;
+		stream << "  imageStore(outImage, uv, color);" << std::endl;
+		stream << "}" << std::endl;
+		
+		compiler.compileSource(
+				ShaderStage::COMPUTE,
+				stream.str().c_str(),
+				[&](ShaderStage stage, const std::filesystem::path &path) {
+					program.addShader(stage, path);
+				}
+		);
+		
+		return program;
+	}
+	
+	void ToneMapping::buildComputePipeline(const std::string &functionName,
+										   const std::string &functionSource) {
+		const ShaderProgram program = compileShaderProgram(
+				functionName,
+				functionSource
+		);
+		
+		m_descriptorSetLayout = m_core.createDescriptorSetLayout(
+				getDescriptorBindings()
+		);
+		
+		m_descriptorSet = m_core.createDescriptorSet(m_descriptorSetLayout);
+		
+		m_pipeline = m_core.createComputePipeline({
+			program,
+			{ m_descriptorSetLayout }
+		});
+	}
+	
+	ToneMapping::ToneMapping(Core &core,
+							 const std::string &name,
+							 bool normalize)
+	: m_core(core),
+	  m_name(name),
+	  m_normalize(normalize),
+	  m_pipeline(),
+	  m_descriptorSetLayout(),
+	  m_descriptorSet() {}
+	
+	const std::string &ToneMapping::getName() const {
+		return m_name;
+	}
+	
+	void ToneMapping::recordToneMapping(const CommandStreamHandle& cmdStream,
+										const ImageHandle& input,
+										const ImageHandle& output) {
+		m_core.recordBeginDebugLabel(cmdStream, m_name, std::array<float, 4>{
+			0.75f, 0.75f, 0.75f, 1.0f
+		});
+		
+		m_core.prepareImageForStorage(cmdStream, input);
+		m_core.prepareImageForStorage(cmdStream, output);
+		
+		vkcv::DescriptorWrites writes;
+		
+		writes.writeStorageImage(0, input);
+		writes.writeStorageImage(1, output);
+		
+		m_core.writeDescriptorSet(m_descriptorSet, writes);
+		
+		const uint32_t width = m_core.getImageWidth(output);
+		const uint32_t height = m_core.getImageHeight(output);
+		
+		m_core.recordComputeDispatchToCmdStream(
+				cmdStream,
+				m_pipeline,
+				dispatchInvocations(
+						DispatchSize(width, height),
+						DispatchSize(8, 8)
+				),
+				{ useDescriptorSet(0, m_descriptorSet) },
+				PushConstants(0)
+		);
+		
+		m_core.recordEndDebugLabel(cmdStream);
+	}
+	
+}
diff --git a/modules/tone_mapping/src/vkcv/tone/UchimuraToneMapping.cpp b/modules/tone_mapping/src/vkcv/tone/UchimuraToneMapping.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f063569ad35f7b2ccdb7b1a75df3a0ef3c122ef9
--- /dev/null
+++ b/modules/tone_mapping/src/vkcv/tone/UchimuraToneMapping.cpp
@@ -0,0 +1,17 @@
+
+#include "vkcv/tone/UchimuraToneMapping.hpp"
+
+#include "uchimura.glsl.hxx"
+
+namespace vkcv::tone {
+	
+	void UchimuraToneMapping::initToneMapping() {
+		buildComputePipeline("uchimura", UCHIMURA_GLSL_SHADER);
+	}
+	
+	UchimuraToneMapping::UchimuraToneMapping(Core &core, bool normalize)
+	: ToneMapping(core, "Uchimura Tone Mapping", normalize) {
+		initToneMapping();
+	}
+
+}
diff --git a/modules/tone_mapping/src/vkcv/tone/Uncharted2ToneMapping.cpp b/modules/tone_mapping/src/vkcv/tone/Uncharted2ToneMapping.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c83ad5d01632072869655a56fcba217df43f35ca
--- /dev/null
+++ b/modules/tone_mapping/src/vkcv/tone/Uncharted2ToneMapping.cpp
@@ -0,0 +1,17 @@
+
+#include "vkcv/tone/Uncharted2ToneMapping.hpp"
+
+#include "uncharted2.glsl.hxx"
+
+namespace vkcv::tone {
+	
+	void Uncharted2ToneMapping::initToneMapping() {
+		buildComputePipeline("uncharted2", UNCHARTED2_GLSL_SHADER);
+	}
+	
+	Uncharted2ToneMapping::Uncharted2ToneMapping(Core &core, bool normalize)
+	: ToneMapping(core, "Uncharted2 Tone Mapping", normalize) {
+		initToneMapping();
+	}
+
+}
diff --git a/modules/tone_mapping/src/vkcv/tone/UnrealToneMapping.cpp b/modules/tone_mapping/src/vkcv/tone/UnrealToneMapping.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d41c9e3fadce145765d2d237175eb7622792672d
--- /dev/null
+++ b/modules/tone_mapping/src/vkcv/tone/UnrealToneMapping.cpp
@@ -0,0 +1,17 @@
+
+#include "vkcv/tone/UnrealToneMapping.hpp"
+
+#include "unreal.glsl.hxx"
+
+namespace vkcv::tone {
+	
+	void UnrealToneMapping::initToneMapping() {
+		buildComputePipeline("unreal", UNREAL_GLSL_SHADER);
+	}
+	
+	UnrealToneMapping::UnrealToneMapping(Core &core, bool normalize)
+	: ToneMapping(core, "Unreal Tone Mapping", normalize) {
+		initToneMapping();
+	}
+
+}
diff --git a/projects/fire_works/CMakeLists.txt b/projects/fire_works/CMakeLists.txt
index 7f9fd1fdd30bff0b331138821f10035f4b7dc64d..4907967793dd843a4016e9577ffb67dee1d780d4 100644
--- a/projects/fire_works/CMakeLists.txt
+++ b/projects/fire_works/CMakeLists.txt
@@ -16,6 +16,7 @@ target_include_directories(fire_works SYSTEM BEFORE PRIVATE
 		${vkcv_camera_include}
 		${vkcv_gui_include}
 		${vkcv_shader_compiler_include}
+		${vkcv_tone_mapping_include}
 		${vkcv_effects_include})
 
 # linking with libraries from all dependencies and the VkCV framework
@@ -24,4 +25,5 @@ target_link_libraries(fire_works
 		vkcv_camera
 		vkcv_gui
 		vkcv_shader_compiler
+		vkcv_tone_mapping
 		vkcv_effects)
diff --git a/projects/fire_works/shaders/tonemapping.comp b/projects/fire_works/shaders/tonemapping.comp
deleted file mode 100644
index 5e6cc8412a77939888fc8e961ea5e9ef29534a81..0000000000000000000000000000000000000000
--- a/projects/fire_works/shaders/tonemapping.comp
+++ /dev/null
@@ -1,21 +0,0 @@
-#version 440
-
-layout(set=0, binding=0, rgba16f) readonly uniform image2D inImage;
-layout(set=0, binding=1, rgba8) writeonly uniform image2D outImage;
-
-
-layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
-
-void main() {
-    if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(inImage)))){
-        return;
-    }
-
-    ivec2 uv            = ivec2(gl_GlobalInvocationID.xy);
-    vec3 linearColor    = imageLoad(inImage, uv).rgb;
-
-    vec3 tonemapped     = linearColor / (dot(linearColor, vec3(0.21, 0.71, 0.08)) + 1); // reinhard tonemapping
-    vec3 gammaCorrected = pow(tonemapped, vec3(1.f / 2.2f));
-
-    imageStore(outImage, uv, vec4(gammaCorrected, 0.f));
-}
\ No newline at end of file
diff --git a/projects/fire_works/src/main.cpp b/projects/fire_works/src/main.cpp
index 2bec78711c12a221fd7e82da06af6d87ee5be0fe..aa23f74a75f4c4deab44632bdf7f7c53f543098e 100644
--- a/projects/fire_works/src/main.cpp
+++ b/projects/fire_works/src/main.cpp
@@ -8,9 +8,11 @@
 #include <vkcv/Sampler.hpp>
 
 #include <vkcv/camera/CameraManager.hpp>
-#include <vkcv/shader/GLSLCompiler.hpp>
-#include <vkcv/gui/GUI.hpp>
 #include <vkcv/effects/BloomAndFlaresEffect.hpp>
+#include <vkcv/effects/GammaCorrectionEffect.hpp>
+#include <vkcv/gui/GUI.hpp>
+#include <vkcv/shader/GLSLCompiler.hpp>
+#include <vkcv/tone/ReinhardToneMapping.hpp>
 
 struct particle_t {
 	glm::vec3 position;
@@ -910,17 +912,8 @@ int main(int argc, const char **argv) {
 		{ addDescriptorLayout, generationDescriptorLayout }
 	});
 	
-	vkcv::ShaderProgram tonemappingShader;
-	compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/tonemapping.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-		tonemappingShader.addShader(shaderStage, path);
-	});
-	
-	vkcv::DescriptorSetLayoutHandle tonemappingDescriptorLayout = core.createDescriptorSetLayout(tonemappingShader.getReflectedDescriptors().at(0));
-	vkcv::DescriptorSetHandle tonemappingDescriptor = core.createDescriptorSet(tonemappingDescriptorLayout);
-	vkcv::ComputePipelineHandle tonemappingPipe = core.createComputePipeline({
-		tonemappingShader,
-		{ tonemappingDescriptorLayout }
-	});
+	vkcv::tone::ReinhardToneMapping toneMapping (core);
+	vkcv::effects::GammaCorrectionEffect gammaCorrection (core);
 	
 	vkcv::ImageHandle swapchainImage = vkcv::ImageHandle::createSwapchainImageHandle();
 	
@@ -1273,29 +1266,8 @@ int main(int argc, const char **argv) {
 		core.recordEndDebugLabel(cmdStream);
 		
 		bloomAndFlares.recordEffect(cmdStream, colorBuffers.back(), colorBuffers.back());
-		
-		core.recordBeginDebugLabel(cmdStream, "Tonemapping", { 0.0f, 1.0f, 0.0f, 1.0f });
-		core.prepareImageForStorage(cmdStream, colorBuffers.back());
-		core.prepareImageForStorage(cmdStream, swapchainImage);
-		
-		vkcv::DescriptorWrites tonemappingDescriptorWrites;
-		tonemappingDescriptorWrites.writeStorageImage(
-			0, colorBuffers.back()
-		).writeStorageImage(
-			1, swapchainImage
-		);
-		
-		core.writeDescriptorSet(tonemappingDescriptor, tonemappingDescriptorWrites);
-		
-		core.recordComputeDispatchToCmdStream(
-			cmdStream,
-			tonemappingPipe,
-			colorDispatchCount,
-			{ vkcv::useDescriptorSet(0, tonemappingDescriptor) },
-			vkcv::PushConstants(0)
-		);
-		
-		core.recordEndDebugLabel(cmdStream);
+		toneMapping.recordToneMapping(cmdStream, colorBuffers.back(), colorBuffers.back());
+		gammaCorrection.recordEffect(cmdStream, colorBuffers.back(), swapchainImage);
 		
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
@@ -1321,6 +1293,11 @@ int main(int argc, const char **argv) {
 		
 		bool colorChanged = ImGui::ColorPicker3("Color", (float*) & color);
 		
+		float gamma = gammaCorrection.getGamma();
+		if (ImGui::SliderFloat("Gamma", &gamma, 0.0f, 10.0f)) {
+			gammaCorrection.setGamma(gamma);
+		}
+		
 		ImGui::End();
 		gui.endGUI();
 		
diff --git a/projects/particle_simulation/CMakeLists.txt b/projects/particle_simulation/CMakeLists.txt
index ed0874fb071aadf801b03be62b7b1b8e8da47aaa..0f4e0acd2d7e1e14a82ed2f045be0e2e326b6330 100644
--- a/projects/particle_simulation/CMakeLists.txt
+++ b/projects/particle_simulation/CMakeLists.txt
@@ -19,6 +19,7 @@ target_include_directories(particle_simulation SYSTEM BEFORE PRIVATE
 		${vkcv_includes}
 		${vkcv_camera_include}
 		${vkcv_shader_compiler_include}
+		${vkcv_tone_mapping_include}
 		${vkcv_effects_include})
 
 # linking with libraries from all dependencies and the VkCV framework
@@ -26,4 +27,5 @@ target_link_libraries(particle_simulation
 		vkcv
 		vkcv_camera
 		vkcv_shader_compiler
+		vkcv_tone_mapping
 		vkcv_effects)
diff --git a/projects/particle_simulation/shaders/tonemapping.comp b/projects/particle_simulation/shaders/tonemapping.comp
deleted file mode 100644
index 26f0232d66e3475afdd1266c0cc6288b47ed1c38..0000000000000000000000000000000000000000
--- a/projects/particle_simulation/shaders/tonemapping.comp
+++ /dev/null
@@ -1,19 +0,0 @@
-#version 440
-
-layout(set=0, binding=0, rgba16f)   uniform image2D inImage;
-layout(set=0, binding=1, rgba8)     uniform image2D outImage;
-
-
-layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
-
-void main(){
-
-    if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(inImage)))){
-        return;
-    }
-    ivec2 uv            = ivec2(gl_GlobalInvocationID.xy);
-    vec3 linearColor    = imageLoad(inImage, uv).rgb;
-    vec3 tonemapped     = linearColor / (dot(linearColor, vec3(0.21, 0.71, 0.08)) + 1); // reinhard tonemapping
-    vec3 gammaCorrected = pow(tonemapped, vec3(1.f / 2.2f));
-    imageStore(outImage, uv, vec4(gammaCorrected, 0.f));
-}
\ No newline at end of file
diff --git a/projects/particle_simulation/src/main.cpp b/projects/particle_simulation/src/main.cpp
index 7f270706eb2e385c539af4cdd6cb7eca3236b138..09cb702809f9f7c48105d8b95b4f398f2bbeb014 100644
--- a/projects/particle_simulation/src/main.cpp
+++ b/projects/particle_simulation/src/main.cpp
@@ -4,13 +4,12 @@
 #include <vkcv/Pass.hpp>
 #include <GLFW/glfw3.h>
 #include <vkcv/camera/CameraManager.hpp>
-#include <chrono>
 #include "ParticleSystem.hpp"
 #include <random>
-#include <glm/gtc/matrix_access.hpp>
-#include <ctime>
 #include <vkcv/shader/GLSLCompiler.hpp>
 #include <vkcv/effects/BloomAndFlaresEffect.hpp>
+#include <vkcv/effects/GammaCorrectionEffect.hpp>
+#include <vkcv/tone/ReinhardToneMapping.hpp>
 
 int main(int argc, const char **argv) {
     const std::string applicationName = "Particlesystem";
@@ -222,18 +221,9 @@ int main(int argc, const char **argv) {
 	
 	vkcv::effects::BloomAndFlaresEffect bloomAndFlares (core);
 	bloomAndFlares.setUpsamplingLimit(3);
-
-    vkcv::ShaderProgram tonemappingShader;
-    compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/tonemapping.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-        tonemappingShader.addShader(shaderStage, path);
-    });
-
-    vkcv::DescriptorSetLayoutHandle tonemappingDescriptorLayout = core.createDescriptorSetLayout(tonemappingShader.getReflectedDescriptors().at(0));
-    vkcv::DescriptorSetHandle tonemappingDescriptor = core.createDescriptorSet(tonemappingDescriptorLayout);
-    vkcv::ComputePipelineHandle tonemappingPipe = core.createComputePipeline({
-        tonemappingShader, 
-        { tonemappingDescriptorLayout }
-	});
+	
+	vkcv::tone::ReinhardToneMapping toneMapping (core);
+	vkcv::effects::GammaCorrectionEffect gammaCorrection (core);
 
     std::uniform_real_distribution<float> rdm = std::uniform_real_distribution<float>(0.95f, 1.05f);
     std::default_random_engine rdmEngine;
@@ -295,31 +285,8 @@ int main(int argc, const char **argv) {
 		);
 	
 		bloomAndFlares.recordEffect(cmdStream, colorBuffer, colorBuffer);
-
-        core.prepareImageForStorage(cmdStream, colorBuffer);
-        core.prepareImageForStorage(cmdStream, swapchainInput);
-
-        vkcv::DescriptorWrites tonemappingDescriptorWrites;
-        tonemappingDescriptorWrites.writeStorageImage(
-				0, colorBuffer
-		).writeStorageImage(
-				1, swapchainInput
-		);
-		
-        core.writeDescriptorSet(tonemappingDescriptor, tonemappingDescriptorWrites);
-
-        const auto tonemappingDispatchCount = vkcv::dispatchInvocations(
-				vkcv::DispatchSize(swapchainWidth, swapchainHeight),
-				vkcv::DispatchSize(8, 8)
-		);
-
-        core.recordComputeDispatchToCmdStream(
-            cmdStream, 
-            tonemappingPipe, 
-            tonemappingDispatchCount, 
-            { vkcv::useDescriptorSet(0, tonemappingDescriptor) },
-            vkcv::PushConstants(0)
-		);
+		toneMapping.recordToneMapping(cmdStream, colorBuffer, colorBuffer);
+		gammaCorrection.recordEffect(cmdStream, colorBuffer, swapchainInput);
 
         core.prepareSwapchainImageForPresent(cmdStream);
         core.submitCommandStream(cmdStream);
diff --git a/projects/path_tracer/CMakeLists.txt b/projects/path_tracer/CMakeLists.txt
index 03e5eec5ddbada873297d53d224d57f97b4436a9..e0287b28f177708b7ef9bd0935475de365e421fb 100644
--- a/projects/path_tracer/CMakeLists.txt
+++ b/projects/path_tracer/CMakeLists.txt
@@ -14,7 +14,9 @@ target_include_directories(path_tracer SYSTEM BEFORE PRIVATE
 		${vkcv_includes}
 		${vkcv_asset_loader_include}
 		${vkcv_camera_include}
+		${vkcv_effects_include}
 		${vkcv_shader_compiler_include}
+		${vkcv_tone_mapping_include}
 		${vkcv_gui_include})
 
 # linking with libraries from all dependencies and the VkCV framework
@@ -23,5 +25,7 @@ target_link_libraries(path_tracer
 		vkcv_asset_loader
 		${vkcv_asset_loader_libraries}
 		vkcv_camera
+		vkcv_effects
 		vkcv_shader_compiler
+		vkcv_tone_mapping
 		vkcv_gui)
diff --git a/projects/path_tracer/shaders/presentImage.comp b/projects/path_tracer/shaders/presentImage.comp
deleted file mode 100644
index a52159c0c6173779b091e5d4153b15b0a6361780..0000000000000000000000000000000000000000
--- a/projects/path_tracer/shaders/presentImage.comp
+++ /dev/null
@@ -1,23 +0,0 @@
-#version 440
-#extension GL_GOOGLE_include_directive : enable
-
-layout(set=0, binding=0, rgba32f) 	uniform image2D inImage;
-layout(set=0, binding=1, rgba8) 	uniform image2D outImage;
-
-layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
-
-void main(){
-
-    ivec2 outImageRes = imageSize(outImage);
-    ivec2 coord       = ivec2(gl_GlobalInvocationID.xy);
-
-    if(any(greaterThanEqual(coord, outImageRes)))
-        return;
-   
-    vec4 colorRaw 				= imageLoad(inImage, coord);
-	vec3 colorNormalized 		= colorRaw.rgb / colorRaw.a;
-	vec3 colorTonemapped		= colorNormalized / (1 + dot(colorNormalized, vec3(0.71, 0.21, 0.08)));	// reinhard tonemapping
-	vec3 colorGammaCorrected 	= pow(colorTonemapped, vec3(1.f / 2.2));
-
-    imageStore(outImage, coord, vec4(colorGammaCorrected, 0));
-}
\ No newline at end of file
diff --git a/projects/path_tracer/src/main.cpp b/projects/path_tracer/src/main.cpp
index 75ee36b2ac232e20d7baf29db34909dee67cca00..1a87015e58aee80feebb4bc5fafac0a482da5743 100644
--- a/projects/path_tracer/src/main.cpp
+++ b/projects/path_tracer/src/main.cpp
@@ -1,11 +1,11 @@
 #include <vkcv/Buffer.hpp>
 #include <vkcv/Core.hpp>
-#include <vkcv/Pass.hpp>
 #include <vkcv/camera/CameraManager.hpp>
 #include <vkcv/asset/asset_loader.hpp>
+#include <vkcv/effects/GammaCorrectionEffect.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
-#include "vkcv/gui/GUI.hpp"
-#include <chrono>
+#include <vkcv/tone/ReinhardToneMapping.hpp>
+#include <vkcv/gui/GUI.hpp>
 #include <vector>
 
 int main(int argc, const char** argv) {
@@ -81,6 +81,11 @@ int main(int argc, const char** argv) {
 		vk::Format::eR32G32B32A32Sfloat,
 		imageConfig
 	);
+	
+	vkcv::ImageHandle mappedImage = core.createImage(
+			vk::Format::eR8G8B8A8Unorm,
+			imageConfig
+	);
 
 	vkcv::shader::GLSLCompiler compiler;
 
@@ -113,21 +118,9 @@ int main(int argc, const char** argv) {
 	vkcv::DescriptorWrites imageCombineDescriptorWrites;
 	imageCombineDescriptorWrites.writeStorageImage(0, outputImage).writeStorageImage(1, meanImage);
 	core.writeDescriptorSet(imageCombineDescriptorSet, imageCombineDescriptorWrites);
-
-	// image present shader
-	vkcv::ShaderProgram presentShaderProgram{};
-
-	compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/presentImage.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-		presentShaderProgram.addShader(shaderStage, path);
-	});
-
-	const vkcv::DescriptorBindings& presentDescriptorBindings   = presentShaderProgram.getReflectedDescriptors().at(0);
-	vkcv::DescriptorSetLayoutHandle presentDescriptorSetLayout  = core.createDescriptorSetLayout(presentDescriptorBindings);
-	vkcv::DescriptorSetHandle       presentDescriptorSet        = core.createDescriptorSet(presentDescriptorSetLayout);
-	vkcv::ComputePipelineHandle     presentPipeline             = core.createComputePipeline({
-		presentShaderProgram,
-		{ presentDescriptorSetLayout }
-	});
+	
+	vkcv::tone::ReinhardToneMapping toneMapping (core, true);
+	vkcv::effects::GammaCorrectionEffect gammaCorrection (core);
 
 	// clear shader
 	vkcv::ShaderProgram clearShaderProgram{};
@@ -253,13 +246,18 @@ int main(int argc, const char** argv) {
 
 			// resize images
 			outputImage = core.createImage(
-				vk::Format::eR32G32B32A32Sfloat,
-				imageConfig
+					vk::Format::eR32G32B32A32Sfloat,
+					imageConfig
 			);
 
 			meanImage = core.createImage(
-				vk::Format::eR32G32B32A32Sfloat,
-				imageConfig
+					vk::Format::eR32G32B32A32Sfloat,
+					imageConfig
+			);
+			
+			mappedImage = core.createImage(
+					vk::Format::eR8G8B8A8Unorm,
+					imageConfig
 			);
 
 			// update descriptor sets
@@ -379,23 +377,9 @@ int main(int argc, const char** argv) {
 
 		// present image
 		const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
-
-		vkcv::DescriptorWrites presentDescriptorWrites;
-		presentDescriptorWrites.writeStorageImage(
-				0, meanImage
-		).writeStorageImage(
-				1, swapchainInput
-		);
 		
-		core.writeDescriptorSet(presentDescriptorSet, presentDescriptorWrites);
-
-		core.prepareImageForStorage(cmdStream, swapchainInput);
-
-		core.recordComputeDispatchToCmdStream(cmdStream,
-			presentPipeline,
-			fullscreenDispatchCount,
-			{ vkcv::useDescriptorSet(0, presentDescriptorSet) },
-			vkcv::PushConstants(0));
+		toneMapping.recordToneMapping(cmdStream, meanImage, mappedImage);
+		gammaCorrection.recordEffect(cmdStream, mappedImage, swapchainInput);
 
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
diff --git a/projects/sph/CMakeLists.txt b/projects/sph/CMakeLists.txt
index 01a6e083116622965b9f58c2837f6287871da50e..46e83abfbe1913ea69038db7320d370e114caa3d 100644
--- a/projects/sph/CMakeLists.txt
+++ b/projects/sph/CMakeLists.txt
@@ -19,6 +19,7 @@ target_include_directories(sph SYSTEM BEFORE PRIVATE
 		${vkcv_includes}
 		${vkcv_camera_include}
 		${vkcv_shader_compiler_include}
+		${vkcv_tone_mapping_include}
 		${vkcv_effects_include})
 
 # linking with libraries from all dependencies and the VkCV framework
@@ -26,4 +27,5 @@ target_link_libraries(sph
 		vkcv
 		vkcv_camera
 		vkcv_shader_compiler
+		vkcv_tone_mapping
 		vkcv_effects)
diff --git a/projects/sph/shaders/tonemapping.comp b/projects/sph/shaders/tonemapping.comp
deleted file mode 100644
index 26f0232d66e3475afdd1266c0cc6288b47ed1c38..0000000000000000000000000000000000000000
--- a/projects/sph/shaders/tonemapping.comp
+++ /dev/null
@@ -1,19 +0,0 @@
-#version 440
-
-layout(set=0, binding=0, rgba16f)   uniform image2D inImage;
-layout(set=0, binding=1, rgba8)     uniform image2D outImage;
-
-
-layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
-
-void main(){
-
-    if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(inImage)))){
-        return;
-    }
-    ivec2 uv            = ivec2(gl_GlobalInvocationID.xy);
-    vec3 linearColor    = imageLoad(inImage, uv).rgb;
-    vec3 tonemapped     = linearColor / (dot(linearColor, vec3(0.21, 0.71, 0.08)) + 1); // reinhard tonemapping
-    vec3 gammaCorrected = pow(tonemapped, vec3(1.f / 2.2f));
-    imageStore(outImage, uv, vec4(gammaCorrected, 0.f));
-}
\ No newline at end of file
diff --git a/projects/sph/src/main.cpp b/projects/sph/src/main.cpp
index 904d9e8965e41914271afb0ac31853b1581f6ca7..89f2df703ca14f7e622521e728e3b0216aa4624b 100644
--- a/projects/sph/src/main.cpp
+++ b/projects/sph/src/main.cpp
@@ -3,11 +3,13 @@
 #include <vkcv/Buffer.hpp>
 #include <vkcv/Pass.hpp>
 #include <vkcv/camera/CameraManager.hpp>
-#include <chrono>
 #include <random>
 #include <ctime>
 #include <vkcv/shader/GLSLCompiler.hpp>
 #include <vkcv/effects/BloomAndFlaresEffect.hpp>
+#include <vkcv/effects/GammaCorrectionEffect.hpp>
+#include <vkcv/tone/ReinhardToneMapping.hpp>
+
 #include "PipelineInit.hpp"
 #include "Particle.hpp"
 
@@ -237,15 +239,9 @@ int main(int argc, const char **argv) {
 	
 	vkcv::effects::BloomAndFlaresEffect bloomAndFlares (core);
 	bloomAndFlares.setUpsamplingLimit(3);
-
-    //tone mapping shader & pipeline
-    vkcv::ComputePipelineHandle tonemappingPipe;
-    vkcv::DescriptorSetHandle tonemappingDescriptor = PipelineInit::ComputePipelineInit(
-			&core,
-			vkcv::ShaderStage::COMPUTE,
-			"shaders/tonemapping.comp",
-			tonemappingPipe
-	);
+	
+	vkcv::tone::ReinhardToneMapping toneMapping (core);
+	vkcv::effects::GammaCorrectionEffect gammaCorrection (core);
 	
 	core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt,
 				 uint32_t swapchainWidth, uint32_t swapchainHeight) {
@@ -395,31 +391,8 @@ int main(int argc, const char **argv) {
 		);
 	
 		bloomAndFlares.recordEffect(cmdStream, colorBuffer, colorBuffer);
-
-        core.prepareImageForStorage(cmdStream, colorBuffer);
-        core.prepareImageForStorage(cmdStream, swapchainInput);
-
-        vkcv::DescriptorWrites tonemappingDescriptorWrites;
-        tonemappingDescriptorWrites.writeStorageImage(
-				0, colorBuffer
-		).writeStorageImage(
-				1, swapchainInput
-		);
-		
-        core.writeDescriptorSet(tonemappingDescriptor, tonemappingDescriptorWrites);
-
-        const auto tonemappingDispatchCount = vkcv::dispatchInvocations(
-				vkcv::DispatchSize(swapchainWidth, swapchainHeight),
-				vkcv::DispatchSize(8, 8)
-		);
-
-        core.recordComputeDispatchToCmdStream(
-            cmdStream, 
-            tonemappingPipe, 
-            tonemappingDispatchCount, 
-            { vkcv::useDescriptorSet(0, tonemappingDescriptor) },
-            vkcv::PushConstants(0)
-		);
+		toneMapping.recordToneMapping(cmdStream, colorBuffer, colorBuffer);
+		gammaCorrection.recordEffect(cmdStream, colorBuffer, swapchainInput);
 
         core.prepareSwapchainImageForPresent(cmdStream);
         core.submitCommandStream(cmdStream);
diff --git a/projects/voxelization/CMakeLists.txt b/projects/voxelization/CMakeLists.txt
index 34178021a517485e54ef8dcafe0f170a834e5186..f8735582caa27ceaf111c969d8ae716679dee1e5 100644
--- a/projects/voxelization/CMakeLists.txt
+++ b/projects/voxelization/CMakeLists.txt
@@ -21,6 +21,7 @@ target_include_directories(voxelization SYSTEM BEFORE PRIVATE
 		${vkcv_asset_loader_include}
 		${vkcv_camera_include}
 		${vkcv_shader_compiler_include}
+		${vkcv_tone_mapping_include}
 		${vkcv_gui_include}
 		${vkcv_upscaling_include}
 		${vkcv_effects_include}
@@ -34,6 +35,7 @@ target_link_libraries(voxelization
 		${vkcv_asset_loader_libraries}
 		vkcv_camera
 		vkcv_shader_compiler
+		vkcv_tone_mapping
 		vkcv_gui
 		vkcv_upscaling
 		vkcv_effects
diff --git a/projects/voxelization/assets/shaders/tonemapping.comp b/projects/voxelization/assets/shaders/tonemapping.comp
deleted file mode 100644
index ffadc9a71e207f97fec9a8815aa1c61bc709c369..0000000000000000000000000000000000000000
--- a/projects/voxelization/assets/shaders/tonemapping.comp
+++ /dev/null
@@ -1,34 +0,0 @@
-#version 440
-#extension GL_GOOGLE_include_directive : enable
-
-layout(set=0, binding=0)        uniform texture2D   inTexture;
-layout(set=0, binding=1)        uniform sampler     textureSampler;
-layout(set=0, binding=2, rgba8) uniform image2D     outImage;
-
-layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
-
-// from: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
-vec3 ACESFilm(vec3 x)
-{
-    float a = 2.51f;
-    float b = 0.03f;
-    float c = 2.43f;
-    float d = 0.59f;
-    float e = 0.14f;
-    return clamp((x*(a*x+b))/(x*(c*x+d)+e), 0, 1);
-}
-
-void main(){
-
-    if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage)))){
-        return;
-    }
-    ivec2   textureRes  = textureSize(sampler2D(inTexture, textureSampler), 0);
-    ivec2   coord       = ivec2(gl_GlobalInvocationID.xy);
-    vec2    uv          = vec2(coord) / textureRes;
-
-    vec3 linearColor    = texture(sampler2D(inTexture, textureSampler), uv).rgb;
-    vec3 tonemapped     = ACESFilm(linearColor);
-
-    imageStore(outImage, coord, vec4(tonemapped, 0.f));
-}
\ No newline at end of file
diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp
index 9b9bf39b205cd65b8a0f5813b7f4376ee68152a9..d9d9aedcc5a32552b83e2d9fe48733410ec2f9c7 100644
--- a/projects/voxelization/src/main.cpp
+++ b/projects/voxelization/src/main.cpp
@@ -4,7 +4,6 @@
 #include <vkcv/Sampler.hpp>
 #include <GLFW/glfw3.h>
 #include <vkcv/camera/CameraManager.hpp>
-#include <chrono>
 #include <vkcv/asset/asset_loader.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
 #include "Voxelization.hpp"
@@ -15,6 +14,7 @@
 #include <vkcv/upscaling/NISUpscaling.hpp>
 #include <vkcv/effects/BloomAndFlaresEffect.hpp>
 #include <vkcv/algorithm/SinglePassDownsampler.hpp>
+#include <vkcv/tone/ACESToneMapping.hpp>
 
 int main(int argc, const char** argv) {
 	const std::string applicationName = "Voxelization";
@@ -474,21 +474,6 @@ int main(int argc, const char** argv) {
 			renderUI = !renderUI;
 		}
 	});
-
-	// tonemapping compute shader
-	vkcv::ShaderProgram tonemappingProgram;
-	compiler.compile(vkcv::ShaderStage::COMPUTE, "assets/shaders/tonemapping.comp", 
-		[&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-		tonemappingProgram.addShader(shaderStage, path);
-	});
-
-	vkcv::DescriptorSetLayoutHandle tonemappingDescriptorSetLayout = core.createDescriptorSetLayout(
-	        tonemappingProgram.getReflectedDescriptors().at(0));
-	vkcv::DescriptorSetHandle tonemappingDescriptorSet = core.createDescriptorSet(tonemappingDescriptorSetLayout);
-	vkcv::ComputePipelineHandle tonemappingPipeline = core.createComputePipeline({
-		tonemappingProgram,
-		{ tonemappingDescriptorSetLayout }
-	});
 	
 	// tonemapping compute shader
 	vkcv::ShaderProgram postEffectsProgram;
@@ -609,6 +594,8 @@ int main(int argc, const char** argv) {
 	);
 	
 	core.writeDescriptorSet(forwardShadingDescriptorSet, forwardDescriptorWrites);
+	
+	vkcv::tone::ACESToneMapping acesToneMapping (core);
 
 	vkcv::upscaling::FSRUpscaling upscaling (core);
 	uint32_t fsrWidth = swapchainExtent.width, fsrHeight = swapchainExtent.height;
@@ -729,14 +716,6 @@ int main(int argc, const char** argv) {
 					swapBufferConfig
 			);
 		}
-
-		// update descriptor sets which use swapchain image
-		vkcv::DescriptorWrites tonemappingDescriptorWrites;
-		tonemappingDescriptorWrites.writeSampledImage(0, resolvedColorBuffer);
-		tonemappingDescriptorWrites.writeSampler(1, colorSampler);
-		tonemappingDescriptorWrites.writeStorageImage(2, swapBuffer);
-
-		core.writeDescriptorSet(tonemappingDescriptorSet, tonemappingDescriptorWrites);
 		
 		// update descriptor sets which use swapchain image
 		vkcv::DescriptorWrites postEffectsDescriptorWrites;
@@ -892,22 +871,10 @@ int main(int argc, const char** argv) {
 		
 		bloomFlares.updateCameraDirection(cameraManager.getActiveCamera());
 		bloomFlares.recordEffect(cmdStream, resolvedColorBuffer, resolvedColorBuffer);
-
-		core.prepareImageForStorage(cmdStream, swapBuffer);
-		core.prepareImageForSampling(cmdStream, resolvedColorBuffer);
-		
-		core.recordBeginDebugLabel(cmdStream, "Tonemapping", { 1, 1, 1, 1 });
-		core.recordComputeDispatchToCmdStream(
-				cmdStream,
-				tonemappingPipeline,
-				fullscreenDispatchCount,
-				{ vkcv::useDescriptorSet(0, tonemappingDescriptorSet) },
-				vkcv::PushConstants(0)
-		);
+		acesToneMapping.recordToneMapping(cmdStream, resolvedColorBuffer, swapBuffer);
 		
 		core.prepareImageForStorage(cmdStream, swapBuffer2);
 		core.prepareImageForSampling(cmdStream, swapBuffer);
-		core.recordEndDebugLabel(cmdStream);
 		
 		switch (upscalingMode) {
 			case 0:
@@ -1011,23 +978,6 @@ int main(int argc, const char** argv) {
 				}
 			}
 			
-			if (ImGui::Button("Reload tonemapping")) {
-				vkcv::ShaderProgram newProgram;
-				compiler.compile(vkcv::ShaderStage::COMPUTE, std::filesystem::path("assets/shaders/tonemapping.comp"),
-					[&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-					newProgram.addShader(shaderStage, path);
-				});
-
-				vkcv::ComputePipelineHandle newPipeline = core.createComputePipeline({
-					newProgram,
-					{ tonemappingDescriptorSetLayout }
-				});
-
-				if (newPipeline) {
-					tonemappingPipeline = newPipeline;
-				}
-			}
-			
 			ImGui::End();
 		}