Course outline
The Fixed Timestep
In one line
Physics must step by the same amount of time every time, whatever the frame rate. An accumulator collects real time and runs as many fixed steps as fit. Steps are jumps, so fast objects can skip past thin ones; engines add tricks against that, and you still test it.
Think of it like this
A train timetable. Trains leave every 10 minutes exactly, however long the queue at the ticket office was. If you are late, you wait for the next one; if you are very late, two trains may have left. Physics steps are the trains; frames are the queue.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- fixed timestep
- Advancing physics by the same small time every step (here 1/60 s), whatever the frame rate.
- accumulator
- A running total of real time not yet simulated; each full step's worth is spent on one step.
- interpolation alpha
- The leftover time divided by the step: how far between two physics states to draw.
- tunnelling
- A fast object passing through a thin one because no step catches them overlapping.
- CCD
- Continuous collision detection: checking the path between steps, not only where a body lands.
Step by step
01Count steps per screen
Run the first example and read the three screen lines. Same one second of real time; 59 or 60 steps; up to 15 in one frame after a stall.
02Watch a hand-made step fail
The last three lines move a ball with plain arithmetic and check for overlap after each move. At 1 m/s and at 40 m/s no step lands inside the 2 cm floor.
03Ask the engine
The second example drops a 1 cm bolt onto a 2 cm shelf at 40, 200 and 1,000 m/s, with CCD off and on. It stops every time: speculative contacts catch it a step ahead.
04Decide what to protect
List your fast, small things (a thrown part, a projectile). Turn CCD on for those, then test them at their real top speed against your thinnest colliders.
Try it yourself
- 1
Remove the cap
In the first example, change
Math.min(frame, 0.25)toframe. The stall line now runs 60 steps with up to 30 in one frame: on a slow phone, that one frame takes 30 times longer to compute, which makes the next frame late too. - 2
A smaller step
In the first example, set
STEPto1 / 240. All three hand-made drops now stop (measured): a step at 40 m/s is still 0.17 m, much more than the 4 cm in which the overlap test can catch the ball, so it is luck that one lands inside. Smaller steps make it likelier, not certain; the stall line now needs up to 59 steps in one frame.
Code & diagrams
Simulated frame times, so the counts are exact. The floating-point detail on the 144 Hz line is real JavaScript arithmetic.
Console
(nothing was printed)Expected console output
144 Hz monitor 144 frames -> 59 physics steps (up to 1 in one frame), 16.7 ms left over, alpha 1.00
30 fps phone 30 frames -> 60 physics steps (up to 2 in one frame), 0.0 ms left over, alpha 0.00
60 fps with one 0.5 s stall 31 frames -> 45 physics steps (up to 15 in one frame), 0.0 ms left over, alpha 0.00
hand-made step, 1 m/s: fell through, y = -5.0
hand-made step, 5 m/s: stopped at y = 0.017
hand-made step, 40 m/s: fell through, y = -44.0Rapier 0.21.0 with its default settings. Against a fixed shelf, its speculative contacts stop the bolt at every speed tried.
Console
(nothing was printed)Expected console output
40 m/s, CCD off: after 1 s y = 0.02 -> stopped on the shelf
40 m/s, CCD on : after 1 s y = 0.02 -> stopped on the shelf
200 m/s, CCD off: after 1 s y = 0.02 -> stopped on the shelf
200 m/s, CCD on : after 1 s y = 0.02 -> stopped on the shelf
1000 m/s, CCD off: after 1 s y = 0.02 -> stopped on the shelf
1000 m/s, CCD on : after 1 s y = 0.02 -> stopped on the shelfBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Step by the frame's own time
In a render loop, call world.timestep = delta; world.step(); with each frame's delta instead of fixed steps.
Myth vs fact
Myth
Turning on CCD makes tunnelling impossible.
Fact
CCD helps a lot for fast bodies against static geometry, but has limits: measured here, a small bolt at 200 m/s still passed a thin moving plate with CCD on. Test your real cases at their real speeds.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Physics in a Web Worker keeps a heavy simulation off the main thread; you send inputs in and poses out each step (Topic 17.6). Rapier's WebAssembly runs in workers too.
- ▸
Pausing a background tab stops
requestAnimationFrame, so the accumulator would get a huge delta on return: the 0.25 s cap (or three.js'sTimerwithconnect(document), Phase 3) keeps the return smooth.
Remember this
- 1
The problem this solves: if physics stepped by each frame's own time, a 144 Hz monitor and a 30 fps phone would run different simulations: jumps would reach different heights and stacks would behave differently. Larger steps are also less stable. A fixed step makes the simulation the same everywhere.
- 2
The accumulator. Each frame, add the frame's time to an accumulator; while it holds at least one step (1/60 s), run a step and subtract it. The first example runs one simulated second on three screens: the 30 fps phone runs 60 steps, two per frame; the 144 Hz monitor runs 59 with 16.7 ms left over, because 144 additions of 1/144 add up to a hair less than one second in floating point, and the next frame takes the 60th step. A 0.5 s stall (a background tab, a slow load) is capped at 0.25 s, so one frame runs 15 steps instead of 30: otherwise a slow device falls further behind every frame (the "spiral of death").
- 3
Interpolation. What is left in the accumulator, divided by the step, is alpha (0 to 1): how far the real time is between the last step and the next. Drawing each mesh at
lerp(previous, current, alpha)removes the small stutter you see when a 144 Hz screen shows the same physics state for two or three frames. The build skips it to stay short; games with fast motion use it. - 4
Tunnelling. A step is a jump. Hand-written physics that moves an object and then checks for overlap misses a thin floor whenever a step jumps over it: the first example's own physics lets a ball through at 1 m/s and at 40 m/s, and stops it at 5 m/s only because one step happened to land inside the floor. Engines do better: measured here, Rapier stopped a 1 cm bolt dropped at 1,000 m/s onto a 2 cm fixed shelf, with or without CCD, because it predicts contacts one step ahead (speculative contacts).
- 5
CCD, and testing. Continuous collision detection (CCD,
RigidBodyDesc.setCcdEnabled(true)) sweeps a body along its motion instead of checking only where it lands. Turn it on for small, fast bodies such as projectiles. But measure your own case: in a test for this topic, a 1 cm bolt at 200 m/s passed through a thin moving plate in Rapier 0.21 with CCD on as well as off, while at 20 m/s it was caught. Fast, small, moving objects are where engines are weakest: cap speeds, thicken colliders, or use a raycast for bullets.
Explain it without notes
Why does physics use a fixed timestep, and how does the accumulator make it work at any frame rate?
What is tunnelling, and what can you do about it?
Practice
Write createFixedStepper(step, onStep) returning { tick(delta) } with the accumulator and the cap.
Extend it to return alpha from tick for interpolation.
Done when you can
You step physics by a fixed amount with an accumulator and a cap
You can explain alpha and interpolation
You can explain tunnelling and when to turn on CCD
You test fast objects at their real speed