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 timeStep is 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 timeStep increases, the engine might need more substeps than maxSubSteps allows to catch up. If the required substeps exceed maxSubSteps, 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., to 10 or 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/120 or 1/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.
  • Performance Trade-off: Reducing the fixedTimeStep requires 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.