diff --git a/projects/wobble_bobble/shaders/update_particle_velocities.comp b/projects/wobble_bobble/shaders/update_particle_velocities.comp
index 53b2af964a9b53b97cf5a8fbda52a9efc3b02bf9..ecc3682e18e1fc16756d58a6788ae0fc3798d2d9 100644
--- a/projects/wobble_bobble/shaders/update_particle_velocities.comp
+++ b/projects/wobble_bobble/shaders/update_particle_velocities.comp
@@ -16,6 +16,7 @@ layout(set=0, binding=3) uniform sampler gridSampler;
 
 layout( push_constant ) uniform constants {
     float alpha;
+    float beta;
 };
 
 void main()	{
@@ -28,6 +29,9 @@ void main()	{
         ivec3 gridResolution = textureSize(sampler3D(gridImage, gridSampler), 0);
         ivec3 gridWindow = ivec3(minimal.size * 2.0f * gridResolution);
 
+        mat3 affine_D = mat3(0.0f);
+        mat3 affine_B = mat3(0.0f);
+
         vec3 velocity_pic = vec3(0.0f);
         vec3 velocity_flip = vec3(minimal.velocity);
 
@@ -44,15 +48,41 @@ void main()	{
                         vec4 gridOldSample = texture(sampler3D(gridOldImage, gridSampler), voxel);
 
                         float weight = voxel_particle_weight(voxel, minimal);
+                        vec3 velocity = gridSample.xyz * weight;
+
+                        affine_D += outerProduct(weight * offset, offset);
+                        affine_B += outerProduct(velocity, offset);
 
-                        velocity_pic += gridSample.xyz * weight;
+                        velocity_pic += velocity;
                         velocity_flip += (gridSample.xyz - gridOldSample.xyz) * weight;
                     }
                 }
             }
         }
 
-        particles[gl_GlobalInvocationID.x].minimal.velocity = mix(velocity_pic, velocity_flip, alpha);
+        mat3 affine_C = mat3(0.0f);
+        vec3 velocity_apic = vec3(0.0f);
+
+        if (abs(determinant(affine_D)) > 0.0f) {
+            affine_C = affine_B * inverse(affine_D);
+        }
+
+        for (i = -gridWindow.x; i <= gridWindow.x; i++) {
+            for (j = -gridWindow.y; j <= gridWindow.y; j++) {
+                for (k = -gridWindow.z; k <= gridWindow.z; k++) {
+                    vec3 offset = vec3(i, j, k) / gridResolution;
+
+                    if (length(offset) < minimal.size * 2.0f) {
+                        velocity_apic += affine_C * offset;
+                    }
+                }
+            }
+        }
+
+        vec3 velocity_alpha = mix(velocity_pic, velocity_flip, alpha);
+        vec3 velocity_beta = mix(velocity_alpha, velocity_apic, beta);
+
+        particles[gl_GlobalInvocationID.x].minimal.velocity = velocity_beta;
     }
 
     memoryBarrierBuffer();
diff --git a/projects/wobble_bobble/src/main.cpp b/projects/wobble_bobble/src/main.cpp
index ae846a9086cabd48f87f8895bacd5875f0ed08c0..8b7f334cc10e9012806f29d06214726985c95339 100644
--- a/projects/wobble_bobble/src/main.cpp
+++ b/projects/wobble_bobble/src/main.cpp
@@ -522,6 +522,7 @@ int main(int argc, const char **argv) {
 	bool initializedParticleVolumes = false;
 	bool renderGrid = true;
 	float alpha = 0.95f;
+	float beta = 0.95f;
 	
 	auto start = std::chrono::system_clock::now();
 	auto current = start;
@@ -564,6 +565,7 @@ int main(int argc, const char **argv) {
 		
 		vkcv::PushConstants tweakPushConstants (sizeof(float));
 		tweakPushConstants.appendDrawcall(static_cast<float>(alpha));
+		tweakPushConstants.appendDrawcall(static_cast<float>(beta));
 		
 		cameraManager.update(dt);
 
@@ -729,6 +731,7 @@ int main(int argc, const char **argv) {
 		
 		ImGui::Checkbox("Render Grid", &renderGrid);
 		ImGui::SliderFloat("Alpha (PIC -> FLIP)", &alpha, 0.0f, 1.0f);
+		ImGui::SliderFloat("Beta (Alpha -> APIC)", &beta, 0.0f, 1.0f);
 		
 		ImGui::End();