Understanding ammo.js stepSimulation and Accuracy
This article explains how the stepSimulation method
functions within the ammo.js physics engine and explores how its three
primary parameters control the accuracy, stability, and performance of
3D physics. By understanding how to configure these parameters,
developers can prevent issues like object tunneling, jittery movement,
and physics lag in their web-based games and simulations.
What is the stepSimulation Method?
In ammo.js (a JavaScript port of the Bullet physics engine), the
stepSimulation method is called within the main animation
loop to advance the physics world forward in time. Rather than moving
objects manually, you delegate the movement, collision detection, and
gravity calculations to ammo.js.
The method signature is defined as:
dynamicsWorld.stepSimulation(timeStep, maxSubSteps, fixedTimeStep);Each of these three parameters plays a critical role in balancing calculation accuracy with CPU performance.
Parameter 1: timeStep
The timeStep parameter represents the amount of
real-world time (in seconds) that has passed since the last time
stepSimulation was called. This is typically calculated as
the delta time between animation frames.
- Impact on Accuracy: This parameter does not
directly alter the internal precision of the physics math. Instead, it
tells the engine how much “catch-up” work it needs to perform. If
timeStepis highly variable (due to frame rate drops), the engine must perform more internal steps in a single frame to keep the physics synchronized with real-world time.
Parameter 2: maxSubSteps
Because rendering frame rates can fluctuate, ammo.js does not
calculate physics in one giant leap. Instead, it breaks the elapsed
timeStep down into smaller, fixed increments. The
maxSubSteps parameter defines the maximum number of these
smaller increments (substeps) the engine is allowed to take during a
single frame.
- Default Value: Usually
1. - Impact on Accuracy: If your frame rate drops and
timeStepincreases, the engine might need more substeps thanmaxSubStepsallows to catch up. If the required substeps exceedmaxSubSteps, the engine caps the calculations. This causes the physics simulation to “slow down” relative to real-time, resulting in lag. - Optimizing Accuracy: To ensure physics remain
accurate and synchronized during frame rate drops, increase
maxSubSteps(e.g., to10or more). However, setting this too high on low-end devices can cause a “spiral of death,” where the CPU spends too much time calculating physics substeps, causing the frame rate to drop even further.
Parameter 3: fixedTimeStep
The fixedTimeStep parameter determines the exact
duration (in seconds) of each internal substep. This is the actual
resolution of your physics simulation.
- Default Value:
1/60(approximately 0.0166 seconds, representing 60Hz). - Impact on Accuracy: This is the most critical
parameter for simulation precision.
- Smaller values (e.g.,
1/120or1/240): Increase accuracy significantly. Fast-moving objects are less likely to pass through thin walls (a phenomenon known as “tunneling”), joints and constraints behave more rigidly, and collisions resolve with greater realism. - Larger values (e.g.,
1/30): Decrease accuracy. Fast objects may glitch through boundaries, and stacked physical objects may jitter or collapse due to integration errors.
- Smaller values (e.g.,
- Performance Trade-off: Reducing the
fixedTimeSteprequires more CPU power because the engine must run more substeps per frame to cover the same amount of real-world time.
The Core Equation for Stability
For your simulation to run in consistent real-time without losing accuracy or lagging, your parameters must satisfy the following inequality:
\[\text{maxSubSteps} \times \text{fixedTimeStep} \ge \text{timeStep}\]
If the elapsed frame time (timeStep) is larger than the
maximum time the engine is allowed to simulate in one go (\(\text{maxSubSteps} \times
\text{fixedTimeStep}\)), the simulation will lose synchronization
with real time.
For high-precision simulations (e.g., fast-moving projectiles or
complex mechanical joints), it is best practice to lower the
fixedTimeStep to 1/120 or lower and scale up
the maxSubSteps accordingly to maintain a smooth, accurate
experience.