The Simulation Loop

Every physics-based animation you have ever seen — a cape rippling, a boulder tumbling, a thousand sparks arcing off a sword — is driven by the same tiny heartbeat repeated tens of thousands of times a second: look at where everything is and how fast it is moving, work out the forces, nudge everything forward a hair, repeat. That heartbeat is the simulation loop. It is astonishingly short — a handful of lines — yet almost every question that matters in an animation engine (Does it look stable? Does the replay match? Does it feel the same at 30 fps and 144 fps?) comes down to how you write it.

This page pins down the loop precisely: what state a simulation carries, the four things that happen every step, and the one professional pattern — a fixed timestep with an accumulator — that separates a toy that explodes when the frame rate changes from an engine you can ship. It builds on the numerical integration of ODEs, which supplies the integrator we drop into step 2.

State: two numbers per particle

A simulated system is described by its state — the smallest bundle of numbers from which the future can be predicted. For a point mass (a particle) that state is just its position \mathbf{x} and its velocity \mathbf{v}. Nothing else about the past matters: give me \mathbf{x}, \mathbf{v} and the forces, and I can tell you the next instant. This is Newton's second law written as two coupled first-order equations:

\dot{\mathbf{x}} = \mathbf{v}, \qquad \dot{\mathbf{v}} = \mathbf{a} = \frac{\mathbf{F}(\mathbf{x}, \mathbf{v})}{m}.

A whole cloth or fluid is just this repeated: an array of particles, each with its own \mathbf{x} and \mathbf{v}, coupled by forces (springs, pressure, gravity). The loop below runs identically whether there is one particle or a million — only the size of the arrays changes.

The four steps of one tick

Advancing the world by one physics step of duration \Delta t ("dt") is always the same four-beat cycle, in order:

The heart is step 2, and the cheapest integrator that actually behaves well is semi-implicit Euler (also called symplectic Euler): update the velocity first using the new acceleration, then move the position with that just-updated velocity.

\mathbf{v}_{n+1} = \mathbf{v}_n + \mathbf{a}_n\,\Delta t, \qquad \mathbf{x}_{n+1} = \mathbf{x}_n + \mathbf{v}_{n+1}\,\Delta t.

That one-line reordering (velocity before position, rather than the "explicit" Euler that uses the old velocity for both) is what keeps orbits and springs from spiralling out to infinity. It costs nothing extra and is the workhorse of real-time physics.

Worked example: a falling particle, two steps

Drop a particle from rest under gravity, a = -g = -9.81\ \text{m/s}^2 (down), with a fixed step \Delta t = \tfrac{1}{60}\ \text{s} \approx 0.01667\ \text{s}. Start at x_0 = 0, v_0 = 0. Semi-implicit Euler updates v first, then x.

Step 1. v_1 = 0 + (-9.81)(\tfrac{1}{60}) = -0.1635\ \text{m/s}, then x_1 = 0 + (-0.1635)(\tfrac{1}{60}) = -0.002725\ \text{m}.

Step 2. v_2 = -0.1635 + (-9.81)(\tfrac{1}{60}) = -0.3270\ \text{m/s}, then x_2 = -0.002725 + (-0.3270)(\tfrac{1}{60}) = -0.008175\ \text{m}.

step ntime t (s)velocity v_n (m/s)position x_n (m)
00.000000.00000.000000
10.01667−0.1635−0.002725
20.03333−0.3270−0.008175

Notice the velocity marches down in exact equal drops of g\,\Delta t = 0.1635\ \text{m/s} — that is constant acceleration, captured perfectly. The position accelerates its fall because each step uses the newly increased speed. This table is fully deterministic: run it again with the same numbers and you get the same rows to the last decimal — a property we will lean on heavily.

Why physics wants a fixed dt

The tempting thing is to step the physics by whatever real time elapsed since the last rendered frame — a variable \Delta t. Two things go wrong.

Stability. Explicit-ish integrators are only stable while \Delta t stays below a threshold set by the stiffest force. A spring of stiffness k and mass m needs roughly \Delta t \lesssim 2\sqrt{m/k}; push past it and the spring pumps energy into itself and explodes to infinity. If \Delta t tracks the frame time, a single hitch — the OS steals the CPU, the disk stalls — hands the integrator a giant step and the simulation detonates.

Determinism. Same inputs must give the same result — essential for networked lockstep games (each machine simulates locally and they must never diverge) and for replays. Floating-point results depend on the exact sequence of \Delta t values, and real frame times are never identical twice. Pin \Delta t to a constant and every run, every machine, every replay reproduces the exact same trajectory.

The fixed-timestep accumulator

So the physics wants a constant \Delta t, but the renderer delivers frames at whatever wobbly rate the hardware manages. The classic reconciliation — Glenn Fiedler's "Fix Your Timestep" — is an accumulator: bank the real elapsed time each frame, then spend it in whole fixed-size physics steps, carrying the remainder forward.

const DT = 1 / 120; // fixed physics step (seconds) let accumulator = 0; let t = 0; let prev = now(); function frame(): void { const current = now(); accumulator += current - prev; // bank real elapsed time prev = current; // Run as many WHOLE fixed steps as we can afford. while (accumulator >= DT) { prevState = state; // remember for interpolation state = integrate(state, t, DT); t += DT; accumulator -= DT; } // Leftover time < DT: blend the last two states so rendering is smooth. const alpha = accumulator / DT; render(lerp(prevState, state, alpha)); }

A slow frame that banked, say, 3.4\,\Delta t of time runs three fixed substeps and keeps 0.4\,\Delta t in the accumulator for next time. The physics only ever sees the constant \Delta t — stable and deterministic — while the render rate floats freely. The leftover fraction \alpha is used to interpolate between the previous and current physics states so the picture never stutters even though physics lands on a fixed grid.

See instability happen

Below is a particle dropped from a height and bouncing off the floor (each contact flips and slightly damps the velocity), integrated with semi-implicit Euler at the step size \Delta t you choose. The faint curve is the true, tiny-step trajectory for reference. Nudge \Delta t up from a small value: at small steps the bold curve hugs the truth, but as \Delta t grows the bounces overshoot, mistime and the whole thing starts to gain energy and climb — a fixed-timestep loop would keep \Delta t pinned safely on the left.

The lesson in one picture: \Delta t is not a cosmetic quality knob, it is a stability knob. Big steps do not merely look coarse — past a threshold they make the simulation diverge.

Because their authors coupled the simulation to the frame rate. Early DOS and console games advanced the world by a fixed amount per rendered frame — one "tick" of movement per redraw — assuming the machine ran at a known speed. Run that same code on hardware that renders ten times faster and the world advances ten times as fast: characters sprint, timers expire in a blink, the whole game becomes unplayable. It is the same bug as coupling \Delta t to the frame time, just in its crudest form. The fix, then and now, is identical: measure real elapsed time and step the simulation by a rate you control, not by how often the screen happens to refresh.

The single most common physics bug is feeding the renderer's variable frame time straight into the integrator as \Delta t. It looks fine on your dev machine and then betrays you everywhere else:

At 144 fps the steps are tiny, so springs behave stiffer and a jump arc that felt right becomes weak; at 30 fps the steps are large, so the same springs soften or explode, and a character can jump higher purely because the coarse integration overshoots. Two players on different monitors get different gameplay from identical code, and no replay ever reproduces. The cure is not a smaller step or clamping the frame time — it is the fixed timestep with an accumulator above: run the physics at a constant \Delta t and interpolate for display. Fix your timestep first; debug the forces second.

Where this goes next

The loop is the skeleton; the interesting flesh is the forces and the repair step. Next you can hang particle systems off it (emitters, lifetimes, a whole spray of these particles), swap in a sturdier Verlet integrator for cloth, and grow step 3 into full collision response. Every one of them plugs into the exact four beats you met here.