Ammo.js Step Simulation and Server Tick Rate

In multiplayer game development, synchronizing physics across the client and server is a critical challenge. This article examines whether the step simulation in Ammo.js—the JavaScript port of the Bullet physics engine—should be strictly tied to the server tick rate. We will explore the mechanics of the Ammo.js stepSimulation function, the consequences of direct coupling, and the best practices for maintaining a stable, synchronized multiplayer physics environment.

The Mechanics of stepSimulation

To understand the relationship between Ammo.js and server ticks, you must first understand how Ammo.js processes time. The stepSimulation method typically takes three parameters:

physicsWorld.stepSimulation(timeElapsed, maxSubSteps, fixedTimeStep);

Ammo.js uses these parameters to run a fixed-timestep simulation. If timeElapsed is larger than fixedTimeStep, the engine will run multiple sub-steps (up to maxSubSteps) to ensure the simulation remains accurate and consistent.

Why Strict Coupling is a Bad Idea

Strictly tying the physics step to the server tick rate—meaning you force the physics engine to calculate exactly one step per server tick using a variable delta time—is generally bad practice for several reasons.

1. Physics Instability

Physics engines rely on a constant, fixed timestep to calculate forces, gravity, and collisions accurately. If your server tick rate dips due to CPU spikes, the delta time (timeElapsed) increases. If you force Ammo.js to simulate a single, large step (e.g., 100ms instead of 16ms), the collision detection will fail. Fast-moving objects will “tunnel” through walls, and constraint calculations will explode, causing objects to fly apart.

2. Variable Network Rates

Servers often run at lower tick rates than physics engines to save bandwidth. For example, a server might tick at 20Hz or 30Hz, while physics engines require at least 60Hz to remain stable. Forcing the physics engine to run at 20Hz directly will result in a sluggish, inaccurate simulation.

Instead of strictly tying the simulation to the tick rate, you should use Ammo.js’s built-in sub-stepping. This decouples the server’s update loop from the internal physics calculations.

On every server tick, you pass the actual elapsed time since the last tick to stepSimulation:

// Server tick rate: 30Hz (approx. 33.33ms)
// Internal physics step: 60Hz (approx. 16.66ms)
physicsWorld.stepSimulation(deltaTimeSeconds, 10, 1 / 60);

In this scenario, if the server ticks exactly every 33.3ms, Ammo.js will automatically perform exactly two internal sub-steps of 16.6ms. If a network tick is delayed and takes 50ms, Ammo.js will perform three sub-steps. This keeps the physics simulation stable and accurate, regardless of fluctuations in the server tick rate.

The Exception: Deterministic Rollback Architecture

There is one major exception where physics steps must be strictly tied to the game tick: lockstep or rollback multiplayer systems (such as fighting games or competitive RTS games).

In a deterministic rollback system, the client and server must simulate the exact same state at the exact same tick. If you use sub-stepping with variable delta times, slight floating-point differences will accumulate, leading to desynchronization (desyncs).

To implement determinism with Ammo.js: 1. Disable variable delta times: Do not pass the real-world elapsed time to stepSimulation. 2. Force a single step: Pass the exact same fixed timestep on every tick (e.g., physicsWorld.stepSimulation(1/60, 0, 1/60)). 3. Strict tick-by-tick execution: Run exactly one physics step per game tick. If the server ticks at 60Hz, the physics engine steps exactly once per tick. If the server ticks at 30Hz, the physics steps exactly once per tick (though you must design your game assets to be stable at 30Hz).

Conclusion

For most multiplayer games, you should not strictly tie the Ammo.js step simulation to the server tick rate. Instead, use a fixed internal physics timestep (usually 60Hz) and allow Ammo.js to sub-step using the server’s elapsed delta time. This preserves collision integrity and simulation stability. Only enforce a strict 1:1 relationship between ticks and physics steps if you are building a fully deterministic, input-synchronized rollback system.