Ammo.js CCD Configuration for Fast Objects
In high-speed physics simulations, fast-moving rigid bodies like projectiles often suffer from “tunneling,” an issue where an object passes completely through solid obstacles between simulation frames. This article explains how to successfully configure Continuous Collision Detection (CCD) in ammo.js (the Emscripten port of the Bullet physics engine) to prevent tunneling, focusing on the specific API parameters and math formulas required to guarantee reliable collision detection.
The Core CCD Parameters in Ammo.js
Ammo.js utilizes a “swept sphere” approximation for continuous collision detection. Instead of continuously sweeping the object’s actual complex 3D mesh, it sweeps an embedded sphere along the movement path to predict upcoming collisions.
To enable CCD on a fast-moving btRigidBody, you must
configure two key properties: CCD Motion Threshold and
CCD Swept Sphere Radius.
1. CCD Motion
Threshold (setCcdMotionThreshold)
This property defines the velocity threshold (measured in units per simulation step) above which CCD is activated. If the object moves a distance less than this threshold in a single frame, ammo.js defaults to standard discrete collision detection to save CPU cycles.
- Best Configuration: Set this value to the radius or half-extent of your moving object.
- Formula: If your object is a sphere of radius
R, set the motion threshold toR. If it is a box, set it to the smallest dimension divided by 2. - Example: For a bullet with a thickness of
0.2units, a threshold of0.1is ideal.
2. CCD Swept
Sphere Radius (setCcdSweptSphereRadius)
This property defines the radius of the simplified sphere embedded inside the rigid body used for the continuous sweeping test.
- Best Configuration: This radius must be smaller than the object’s actual physical dimensions to prevent false positive collisions, but large enough to accurately represent its volume.
- Formula: Set this value to roughly
0.2to0.5times the smallest dimension of your collision shape. It must never be larger than the object itself. - Example: For a projectile shape with a radius of
0.2units, set the swept sphere radius to0.1.
JavaScript Configuration Example
Here is the standard implementation code to apply these settings to a fast-moving rigid body in ammo.js:
// Assume 'body' is an instantiated Ammo.btRigidBody
// and the object is roughly 0.4 units in size.
const objectSize = 0.4;
// 1. Set the Swept Sphere Radius (must fit inside the object)
const sweptSphereRadius = objectSize * 0.25; // 0.1 units
body.setCcdSweptSphereRadius(sweptSphereRadius);
// 2. Set the Motion Threshold (trigger CCD if moving more than half its size per step)
const motionThreshold = objectSize * 0.5; // 0.2 units
body.setCcdMotionThreshold(motionThreshold);Optimizing the Physics World Step
Configuring the rigid body is only half the solution. If your physics world update rate is too low, even CCD can fail during extreme lag spikes. You must ensure your simulation steps are configured to handle high-speed math.
When calling stepSimulation, increase the maximum number
of substeps. This allows ammo.js to split a single frame’s physics
calculations into smaller, manageable increments:
// Standard step: dynamicsWorld.stepSimulation( timeElapsed, maxSubsteps, fixedTimeStep );
// Increase maxSubsteps (second parameter) to allow more precise calculations under load
dynamicsWorld.stepSimulation(deltaTime, 10, 1 / 120);By combining a high substep rate with a correctly proportioned swept sphere radius and motion threshold, you will eliminate tunneling entirely for fast-moving objects in your ammo.js project.