Prevent Ammo.js Floating-Point Drift for Stationary Objects
In physics simulations using ammo.js, stationary dynamic rigid bodies can experience subtle, unwanted movement over time due to accumulated floating-point inaccuracies. This article explains why this “floating-point drift” occurs and provides practical solutions, such as configuring sleeping thresholds, zeroing forces, and utilizing static or kinematic body states, to keep your stationary physics objects perfectly still.
Why Floating-Point Drift Occurs
Ammo.js, which is a port of the Bullet Physics engine, uses iterative constraint solvers to calculate collisions and forces. Even when an object appears to be at rest on a surface, the solver continuously calculates tiny micro-collisions and gravity vectors. Because of the limited precision of 32-bit floating-point numbers, these calculations introduce rounding errors. Over thousands of frames, these micro-errors accumulate, manifesting as a slow, creeping drift or jitter.
Solution 1: Leverage Rigidbody Sleeping (Deactivation)
The most efficient way to stop drift is to allow ammo.js to put the rigid body to sleep. When a body is deactivated (sleeping), the physics solver stops calculating forces for it until another active object collides with it.
By default, objects should sleep automatically. However, if your object continues to drift, you may need to adjust the sleeping thresholds:
// Set linear and angular velocity thresholds below which the object sleeps
// Arguments: (linearVelocityThreshold, angularVelocityThreshold)
rigidBody.setSleepingThresholds(0.8, 1.0);To manually force an object to sleep once it reaches its designated stationary position:
// 2 represents ISLAND_SLEEPING
rigidBody.setActivationState(2); Avoid using DISABLE_DEACTIVATION (state 4) on objects
that you want to remain stationary, as this forces the solver to
calculate physics for them indefinitely.
Solution 2: Set the Mass to Zero (Static Bodies)
If the object is meant to remain permanently stationary (such as ground, walls, or heavy pillars), it should be defined as a static body rather than a dynamic one. Static bodies have a mass of zero and are completely ignored by the gravity and velocity solvers, eliminating drift entirely.
var localInertia = new Ammo.btVector3(0, 0, 0);
var mass = 0; // Zero mass defines a static body
var startTransform = new Ammo.btTransform();
startTransform.setIdentity();
startTransform.setOrigin(new Ammo.btVector3(x, y, z));
var motionState = new Ammo.btDefaultMotionState(startTransform);
var rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, shape, localInertia);
var body = new Ammo.btRigidBody(rbInfo);Solution 3: Convert to a Kinematic Object
If the object needs to move occasionally (like an elevator or a sliding door) but must remain perfectly still otherwise, configure it as a kinematic body. Kinematic bodies are not affected by gravity or collisions from other objects, meaning they will never drift.
// Enable the kinematic flag
body.setCollisionFlags(body.getCollisionFlags() | 2); // 2 is CF_KINEMATIC_OBJECT
// Disable sleeping so you can still move it programmatically
body.setActivationState(4); // 4 is DISABLE_DEACTIVATIONWhen you need to stop the kinematic object, simply stop updating its transform. It will remain perfectly frozen in place.
Solution 4: Manually Zero Velocities and Forces
If the object must remain dynamic (capable of being pushed by other forces) but you want to lock it in place when it comes to a stop, you can manually clear its velocities in your physics update loop when they fall below a certain threshold.
const velocity = rigidBody.getLinearVelocity();
const speed = velocity.length();
if (speed < 0.01) {
const zeroVector = new Ammo.btVector3(0, 0, 0);
rigidBody.setLinearVelocity(zeroVector);
rigidBody.setAngularVelocity(zeroVector);
rigidBody.clearForces();
// Clean up WebAssembly memory
Ammo.destroy(zeroVector);
}Applying high linear and angular damping can also help absorb micro-velocities before they cause visible drift:
// Set high damping to resist micro-movements (linear, angular)
rigidBody.setDamping(0.9, 0.9);