Game Rig

Home/Blog/Fixed timestep

Fixed timestep or delta time?

The step size a game integrates with decides how high the jump goes, and the display gets a vote in it.

Every game on this site runs the same loop today: measure the time since the last frame in seconds, clamp it at 50 milliseconds, hand it to update. Drift Circuit is the clearest example and it drives well. The reason a fixed step is worth arguing for anyway is that those two lines are doing two jobs, and the clamp only does one of them.

The step is not a constant of the game, it is a property of the display. requestAnimationFrame fires at the panel's refresh rate, so a 60 Hz screen hands over steps of about 0.0167 seconds and a 144 Hz screen steps at about 0.0069. MDN warns about the consequence in as many words: something advanced by counting frames rather than by elapsed time will run faster on a high refresh-rate screen. Multiplying by the delta kills that whole class of bug and leaves a smaller one behind.

What is left behind is the integration itself. Multiplying a velocity by the step moves an object the right distance for that frame, and it does not make the sum of those movements the same curve. A force applied over 0.0069 seconds and the same force applied over 0.0167 seconds are different rectangles under the same curve, and the smaller one approximates it better. The answer therefore depends on which machine ran the frame.

How far apart the two answers are, in pixels

Take a character that leaves the ground at 700 pixels per second upward under gravity of 2000 pixels per second squared, integrated in two lines: velocity gains gravity times the step, then position gains velocity times the step. The continuous answer is 700 squared over twice 2000, which is 122.5 pixels of rise. The stepped answer is this.

Jump apex at 700 px/s upward under 2000 px/s per second of gravity, by simulation step
Simulation stepRefresh rate it matchesApex heightShortfall against 122.5 px
0.0333 s30 Hz111.1 px11.4 px
0.0167 s60 Hz116.7 px5.8 px
0.0069 s144 Hz120.1 px2.4 px
0.0042 s240 Hz121.0 px1.5 px

Those figures are arithmetic, not telemetry: the two-line integrator run until the height stops rising, with the apex printed at that point. The trend is the argument. A 144 Hz machine clears about 3.4 pixels more than a 60 Hz one, on every jump, in the same direction. Random error averages out over a session; a systematic 3.4 pixels does not, and a ledge whose clearance is thinner than that is a ledge one machine lands on and the other clips.

The error grows the other way as the step gets longer. Glenn Fiedler's article on the subject puts it as a bound rather than a preference: a simulation is well behaved only while the step stays below some maximum, and pushing a variable step toward a tenth of a second, one second or a hundred finds the breaking point — springs that blow up toward infinity, fast movers that tunnel through walls. A garbage collection pause, or a browser waking a backgrounded tab, hands the loop exactly that kind of step, which is what the 50 millisecond clamp above is defending against.

The accumulator: the renderer produces time, the simulation spends it

Stop asking the simulation to accept arbitrary time. Fix a step — 1/60 of a second is the usual pick — and let the frame loop deposit its elapsed time into an accumulator. Take one step of exactly that size out of the accumulator while at least one step still fits, then draw. Fiedler's framing is the one worth keeping: the renderer produces time and the simulation consumes it in discrete steps.

Every step is now the same size, so nothing downstream can tell which refresh rate produced it. The leftover in the accumulator is not discarded either; it stays there and a later frame spends it. There is one step size in the whole program, so gravity, jump impulse and friction are tuned against the same number everywhere.

The price is that a fast frame may want no physics step at all while a slow one wants three, so steps per drawn frame stops being one. It also means the drawn state is up to one step old.

Capping the catch-up so the loop cannot choke on itself

If a machine cannot simulate a sixtieth of a second of physics in less than a sixtieth of a second of real time, the accumulator never empties. Each frame asks for more steps than the last, the frame gets longer, and the backlog grows. Fiedler calls this the spiral of death, and the reason it kills a tab rather than merely slowing it is that the loop is feeding the condition it is waiting on.

His final loop carries two clamps. One caps how much time a single frame is allowed to contribute at all, at a quarter of a second in the sample code. The other caps the number of steps taken per frame. With both in place the simulation visibly slows under load instead of freezing, which is a poor outcome a player can at least still play through.

A backgrounded tab is the same failure arriving through a different door: no frames for thirty seconds, and then one enormous delta. Key events queue the same way and then arrive in a burst, which is the problem input buffering exists to absorb.

Each step still has to fit inside the same frame as the drawing, and halving the step doubles the number of simulation steps per second of game time.

Interpolation hides the fraction of a step left over

With a 1/60 step on a 144 Hz display, most drawn frames contain no new simulation state at all. The picture repeats for one or two frames and then jumps, which reads as stutter even though the physics underneath it is perfectly even. The accumulator already knows the size of the problem: it holds the age of the current state, somewhere between zero and one step.

Divide that age by the step and there is an alpha between 0 and 1. Keep the previous state alongside the current one and draw the two blended by alpha. A remainder of half a step means the truth is halfway between the two states, which is exactly what the blend draws.

For the games here the blend is cheap: two positions and two angles per object, lerped at draw time. The cost is still worth stating: interpolation adds a frame of lag on purpose, and it obliges every drawn object to keep its previous transform around. Where the drawn position is also the collision position, that lag is a real gameplay change, and skipping the blend is a legitimate call.

Determinism is the quieter reason for a fixed step

Speed is the visible symptom. Reproducibility is the deeper one. Fiedler's companion piece on deterministic lockstep presents an identical delta time as a precondition for identical results: the same starting conditions and the same inputs give exactly the same outcome only if both simulations step at the same rate, and a step derived from the display is by construction not that rate.

That piece is honest about the limit too: bit-for-bit agreement is hard even on one machine, because floating point behaviour differs between compilers, operating systems and instruction sets, and even a debug build and a release build of the same source can disagree. A fixed step is necessary for reproducibility and not sufficient for it.

For a single-player game with no replays to verify and no peers to keep in sync, the payoff is smaller than that framing suggests. What does land immediately is narrower and still worth having: a jump arc tuned once is the same arc on a 60 Hz monitor and a 240 Hz phone.

Pros and cons

Pros

  • The same inputs produce the same trajectory at every refresh rate, instead of 116.7 pixels of rise on one screen and 120.1 on another.
  • Constants are tuned against one step size, so gravity and jump impulse need no per-device adjustment.
  • The step itself becomes a knob: 1/60 for a forgiving platformer, 1/120 where fast objects must be caught before they tunnel.
  • Interpolation on top of the accumulator restores smooth motion at any refresh rate without the simulation knowing.

Cons

  • It is more code than multiplying by delta, and the loop now carries an accumulator plus a previous state instead of one timestamp.
  • Both clamps are mandatory, because an unclamped catch-up loop can spiral and lock the tab it is running in.
  • Interpolation deliberately adds a frame of lag and forces every drawn object to hold its previous transform.
  • A game with no integrator at all, such as grid movement or discrete turns, gains nothing from any of it.

Sources

  • Fix Your Timestep! — Glenn Fiedler on the accumulator, the semi-fixed loop, the spiral of death and the interpolation alpha.
  • Deterministic Lockstep — the same author on identical delta time as a precondition for identical results, and on why floating point blocks bit-for-bit agreement.
  • MDN: requestAnimationFrame — that the callback follows the display refresh rate, what its timestamp means, and the warning about counting frames.