diff --git a/projects/wobble_bobble/shaders/particle.frag b/projects/wobble_bobble/shaders/particle.frag
index deee9d4ec1b4b175aa8978fb2af79ddd0f118561..81c0a3594359e816a28b5e3f0301b47ce74a3cd7 100644
--- a/projects/wobble_bobble/shaders/particle.frag
+++ b/projects/wobble_bobble/shaders/particle.frag
@@ -1,14 +1,17 @@
 #version 450
 
 layout(location = 0) in vec2 passPos;
+layout(location = 1) in float passMass;
 
-layout(location = 0) out vec4 outColor;
+layout(location = 0) out vec3 outColor;
 
 void main()	{
     const float value = length(passPos);
 
+    float z = sqrt(0.25 - value * value);
+
     if (value < 0.5f) {
-        outColor = vec4(1.0f, 0.0f, 0.0f, 1.0f - value * 2.0f);
+        outColor = vec3(passPos.x + 0.5f, passPos.y + 0.5f, z * 2.0f);
     } else {
         discard;
     }
diff --git a/projects/wobble_bobble/shaders/particle.vert b/projects/wobble_bobble/shaders/particle.vert
index d593be419fa75d477cfbb1ae5935973e8bd6af1e..bb864e07ee39ed1d04a44c00e5c75f24d1be1baf 100644
--- a/projects/wobble_bobble/shaders/particle.vert
+++ b/projects/wobble_bobble/shaders/particle.vert
@@ -10,11 +10,15 @@ layout(set=0, binding=0, std430) buffer particleBuffer {
 layout(location = 0) in vec2 vertexPos;
 
 layout(location = 0) out vec2 passPos;
+layout(location = 1) out float passMass;
 
 void main()	{
     vec3 position = particles[gl_InstanceIndex].minimal.position;
     float size = particles[gl_InstanceIndex].minimal.size;
 
+    float mass = particles[gl_InstanceIndex].minimal.mass;
+
     passPos = vertexPos;
+    passMass = mass;
     gl_Position = vec4(position + vec3(vertexPos * size * 2.0f, 0), 1);
 }
\ No newline at end of file
diff --git a/projects/wobble_bobble/shaders/update_grid_velocities.comp b/projects/wobble_bobble/shaders/update_grid_velocities.comp
index 7277e586fc746006f121dd7a439e4ff46ead3971..39e3084169ed0bdeb285af6de8150c7fc38be308 100644
--- a/projects/wobble_bobble/shaders/update_grid_velocities.comp
+++ b/projects/wobble_bobble/shaders/update_grid_velocities.comp
@@ -1,6 +1,29 @@
 #version 450
+#extension GL_GOOGLE_include_directive : enable
 
-layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
+layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
+
+#include "particle.inc"
+
+layout(set=0, binding=0) uniform texture3D gridTextureIn;
+layout(set=0, binding=1) uniform sampler gridSampler;
+layout(set=0, binding=2, rgba32f) writeonly uniform image3D gridImageOut;
+
+layout( push_constant ) uniform constants {
+    float t;
+    float dt;
+};
 
 void main()	{
+    vec3 position = (vec3(gl_GlobalInvocationID) + vec3(0.5f)) / textureSize(sampler3D(gridTextureIn, gridSampler), 0);
+    vec4 gridCurrentSample = texture(sampler3D(gridTextureIn, gridSampler), position);
+
+    vec3 offset = position - gridCurrentSample.xyz * dt;
+    vec4 gridPreviousSample = texture(sampler3D(gridTextureIn, gridSampler), offset);
+
+    imageStore(
+        gridImageOut,
+        ivec3(gl_GlobalInvocationID),
+        gridPreviousSample
+    );
 }
\ No newline at end of file
diff --git a/projects/wobble_bobble/src/main.cpp b/projects/wobble_bobble/src/main.cpp
index ea6856072ffcdbd97da623a132a18c360cfa8543..494190a9731f0ecb01b50635edf708848158eadc 100644
--- a/projects/wobble_bobble/src/main.cpp
+++ b/projects/wobble_bobble/src/main.cpp
@@ -144,6 +144,19 @@ int main(int argc, const char **argv) {
 			true
 	);
 	
+	grid.switchLayout(vk::ImageLayout::eGeneral);
+	
+	vkcv::Image tmpGrid = core.createImage(
+			vk::Format::eR32G32B32A32Sfloat,
+			64,
+			64,
+			64,
+			false,
+			true
+	);
+	
+	tmpGrid.switchLayout(vk::ImageLayout::eGeneral);
+	
 	/* TODO: clear grid via compute shader?
 	std::vector<glm::vec4> grid_vec (grid.getWidth() * grid.getHeight() * grid.getDepth());
 	
@@ -198,6 +211,14 @@ int main(int argc, const char **argv) {
 			updateGridVelocitiesSets
 	);
 	
+	{
+		vkcv::DescriptorWrites writes;
+		writes.sampledImageWrites.push_back(vkcv::SampledImageDescriptorWrite(0, grid.getHandle()));
+		writes.samplerWrites.push_back(vkcv::SamplerDescriptorWrite(1, gridSampler));
+		writes.storageImageWrites.push_back(vkcv::StorageImageDescriptorWrite(2, tmpGrid.getHandle()));
+		core.writeDescriptorSet(updateGridVelocitiesSets[0], writes);
+	}
+	
 	std::vector<vkcv::DescriptorSetHandle> updateParticleDeformationSets;
 	vkcv::ComputePipelineHandle updateParticleDeformationPipeline = createComputePipeline(
 			core, compiler,
@@ -215,7 +236,7 @@ int main(int argc, const char **argv) {
 	{
 		vkcv::DescriptorWrites writes;
 		writes.storageBufferWrites.push_back(vkcv::BufferDescriptorWrite(0, particles.getHandle()));
-		writes.sampledImageWrites.push_back(vkcv::SampledImageDescriptorWrite(1, grid.getHandle()));
+		writes.sampledImageWrites.push_back(vkcv::SampledImageDescriptorWrite(1, tmpGrid.getHandle()));
 		writes.samplerWrites.push_back(vkcv::SamplerDescriptorWrite(2, gridSampler));
 		core.writeDescriptorSet(updateParticleVelocitiesSets[0], writes);
 	}
@@ -297,7 +318,7 @@ int main(int argc, const char **argv) {
 	vkcv::Buffer<glm::vec2> trianglePositions = core.createBuffer<glm::vec2>(vkcv::BufferType::VERTEX, 3);
 	trianglePositions.fill({
 		glm::vec2(-1.0f, -1.0f),
-		glm::vec2(+0.0f, +1.0f),
+		glm::vec2(+0.0f, +1.5f),
 		glm::vec2(+1.0f, -1.0f),
 	});
 	
@@ -365,9 +386,8 @@ int main(int argc, const char **argv) {
 		
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 		
-		const uint32_t dispatchSizeTransformParticlesToGrid [3] = { 16, 16, 16 };
-		const uint32_t dispatchSizeUpdateParticles [3] = { static_cast<uint32_t>(particles.getCount() + 63) / 64, 1, 1 };
-		const uint32_t dispatchSize [3] = { 1, 1, 1 };
+		const uint32_t dispatchSizeGrid [3] = { 16, 16, 16 };
+		const uint32_t dispatchSizeParticles [3] = { static_cast<uint32_t>(particles.getCount() + 63) / 64, 1, 1 };
 		
 		core.recordBeginDebugLabel(cmdStream, "TRANSFORM PARTICLES TO GRID", { 0.47f, 0.77f, 0.85f, 1.0f });
 		core.prepareImageForStorage(cmdStream, grid.getHandle());
@@ -375,7 +395,7 @@ int main(int argc, const char **argv) {
 		core.recordComputeDispatchToCmdStream(
 				cmdStream,
 				transformParticlesToGridPipeline,
-				dispatchSizeTransformParticlesToGrid,
+				dispatchSizeGrid,
 				{ vkcv::DescriptorSetUsage(
 						0, core.getDescriptorSet(transformParticlesToGridSets[0]).vulkanHandle
 				) },
@@ -390,7 +410,7 @@ int main(int argc, const char **argv) {
 			core.recordComputeDispatchToCmdStream(
 					cmdStream,
 					initParticleVolumesPipeline,
-					dispatchSize,
+					dispatchSizeParticles,
 					{},
 					vkcv::PushConstants(0)
 			);
@@ -402,39 +422,46 @@ int main(int argc, const char **argv) {
 		core.recordComputeDispatchToCmdStream(
 				cmdStream,
 				updateGridForcesPipeline,
-				dispatchSize,
+				dispatchSizeGrid,
 				{},
 				vkcv::PushConstants(0)
 		);
 		core.recordEndDebugLabel(cmdStream);
 		
 		core.recordBeginDebugLabel(cmdStream, "UPDATE GRID VELOCITIES", { 0.47f, 0.77f, 0.85f, 1.0f });
+		core.prepareImageForSampling(cmdStream, grid.getHandle());
+		core.prepareImageForStorage(cmdStream, tmpGrid.getHandle());
+		
 		core.recordComputeDispatchToCmdStream(
 				cmdStream,
 				updateGridVelocitiesPipeline,
-				dispatchSize,
-				{},
-				vkcv::PushConstants(0)
+				dispatchSizeGrid,
+				{ vkcv::DescriptorSetUsage(
+						0, core.getDescriptorSet(updateGridVelocitiesSets[0]).vulkanHandle
+				) },
+				timePushConstants
 		);
+		
+		core.recordImageMemoryBarrier(cmdStream, tmpGrid.getHandle());
 		core.recordEndDebugLabel(cmdStream);
 		
 		core.recordBeginDebugLabel(cmdStream, "UPDATE PARTICLE DEFORMATION", { 0.78f, 0.89f, 0.94f, 1.0f });
 		core.recordComputeDispatchToCmdStream(
 				cmdStream,
 				updateParticleDeformationPipeline,
-				dispatchSizeUpdateParticles,
+				dispatchSizeParticles,
 				{},
 				vkcv::PushConstants(0)
 		);
 		core.recordEndDebugLabel(cmdStream);
 		
 		core.recordBeginDebugLabel(cmdStream, "UPDATE PARTICLE VELOCITIES", { 0.78f, 0.89f, 0.94f, 1.0f });
-		core.prepareImageForSampling(cmdStream, grid.getHandle());
+		core.prepareImageForSampling(cmdStream, tmpGrid.getHandle());
 		
 		core.recordComputeDispatchToCmdStream(
 				cmdStream,
 				updateParticleVelocitiesPipeline,
-				dispatchSizeUpdateParticles,
+				dispatchSizeParticles,
 				{ vkcv::DescriptorSetUsage(
 						0, core.getDescriptorSet(updateParticleVelocitiesSets[0]).vulkanHandle
 				) },
@@ -448,7 +475,7 @@ int main(int argc, const char **argv) {
 		core.recordComputeDispatchToCmdStream(
 				cmdStream,
 				updateParticlePositionsPipeline,
-				dispatchSizeUpdateParticles,
+				dispatchSizeParticles,
 				{ vkcv::DescriptorSetUsage(
 						0, core.getDescriptorSet(updateParticlePositionsSets[0]).vulkanHandle
 				) },