Time, Frames and Sampling

The world moves continuously: a thrown ball traces one unbroken arc, a spinning wheel turns through every angle in between. A screen cannot. A screen can only show you a rapid slideshow of still pictures — frames — and trust your eye to stitch them back into motion. Every animated film, every video game, every physics simulation you have ever seen is really a sequence of frozen snapshots, taken at regular ticks of a clock, played back fast enough to fool the brain.

That act of freezing continuous motion into evenly-spaced snapshots is sampling, and it is the hidden foundation under all of computer animation. Get it right and motion looks smooth and alive. Get it wrong and wheels spin backwards, fast objects stutter, and — in the worst case — your physics engine detonates a character across the level. This page is about time: how it turns into frames, how sampling can lie to you, and why the clock a simulation runs on is not the clock your display runs on.

Frames, frame rate, and time as the independent variable

An animation is a function of one variable: time. Everything else — a character's position, a joint's angle, a colour — is an output driven by time. Formally the motion of a channel is a continuous signal x(t), and the timeline you scrub in any animation tool is just the t-axis of that function.

To display it we chop time into equal slices and take one snapshot per slice. Each snapshot is a frame; the number of frames shown per second is the frame rate, measured in frames per second (fps). The time between consecutive frames is simply the reciprocal:

\Delta t_{\text{frame}} = \frac{1}{\text{fps}}.

At 24 fps a frame lasts 1/24 \approx 41.7 ms; at 60 fps it lasts 16.7 ms. Frame n is the sample taken at time t_n = n\,\Delta t_{\text{frame}}, so the pictures you actually see are the values x(t_0), x(t_1), x(t_2), \dots — the signal read off at the clock ticks.

Film gets away with 24 fps partly because a real camera's shutter is open for a slice of each frame, so fast objects smear across the picture — natural motion blur. A game rendering an instant, razor-sharp snapshot each frame has no such smear, which is one reason games want higher frame rates to look smooth.

Worked example: how far per frame?

Take an object gliding at a steady v = 10 m/s. How far does it travel between two frames? That distance is the per-frame spacing — the jump the object makes from one snapshot to the next:

d_{\text{frame}} = v \cdot \Delta t_{\text{frame}} = \frac{v}{\text{fps}}.

Same physical speed, but the higher frame rate takes smaller steps, so each jump is gentler and the motion reads as smoother. The total distance over a second is identical (10 m either way) — frame rate changes the granularity of the sampling, not the underlying motion.

Now a spin. A wheel turning at f = 20 revolutions per second, sampled at 24 fps, advances 20/24 \approx 0.833 of a turn each frame — that is 300^\circ of rotation between snapshots. The eye cannot tell 300^\circ forward from 60^\circ backward: the wheel appears to crawl slowly in reverse. This is not a rendering glitch — it is sampling telling a lie, and it has a precise cause.

Temporal aliasing: when sampling lies

If a motion repeats or moves too fast for the frame rate to keep up, the samples can trace out a different, slower motion that was never really there. This false motion is temporal aliasing. Its everyday faces are judder and strobing (a fast pan breaking into visible stutter) and the famous wagon-wheel effect — spoked wheels in old Westerns spinning backwards or standing still while the stagecoach races forward.

The governing rule is the same one that governs sampling any signal: the Nyquist limit. To capture a motion honestly you must sample it more than twice per cycle.

The fix is the film camera's trick: motion blur. Instead of an instantaneous snapshot, integrate the motion over the frame's exposure window, smearing a fast object into a streak. Blur acts as a low-pass filter before sampling — it removes the high-frequency detail that would otherwise alias, trading crisp-but-lying frames for soft-but-honest ones. It is why a spinning wheel with proper motion blur reads as a believable blur rather than a backward crawl.

Because mains lighting samples too. A fluorescent or LED lamp on AC flickers at twice the mains frequency (100 or 120 Hz), briefly lighting the wheel in strobe-like pulses. If the wheel advances almost a whole spoke-spacing between flashes, each pulse catches it a hair short of where the last spoke was — and your eye reconstructs a slow backward creep. Same aliasing maths, no film required: any pulsed sampling of a periodic motion can do it. Under steady daylight (continuous illumination) the effect vanishes, because there is nothing doing the sampling.

See it alias

Below, the faint curve is the true continuous motion — a sine wave x(t)=\sin(2\pi f t) at a fixed frequency of f = 3 Hz (three full cycles a second). The bold dots are the samples your chosen frame rate actually captures, joined by straight lines — this is what the animation would look like. Drag the frame-rate slider down and watch the sampled motion peel away from the truth: below the Nyquist rate of 2f = 6 fps the dots trace a slow, wrong, aliased wave with the wrong frequency — even sliding backwards. Push the rate high and the samples hug the real curve.

Notice there is a threshold, not a gradual fade: cross below roughly 6 fps and the reconstructed motion suddenly reports the wrong frequency entirely. That cliff is the Nyquist limit made visible. No amount of clever playback recovers the lost cycles — the information simply was not sampled.

Display frame rate is not the simulation timestep

Here is the distinction that separates people who ship stable animation from people who chase mystery bugs. There are two clocks:

These need not be equal, and usually should not be. A physics engine might take several small sub-steps per rendered frame: to draw at 60 fps while integrating at a rock-steady 240 Hz, you run 4 simulation steps of \Delta t_{\text{sim}} = 1/240 s for every frame you show. Smaller \Delta t_{\text{sim}} means more accurate, more stable integration — a fast projectile no longer tunnels straight through a wall between snapshots because the simulation checked several positions in between.

This is why the professional pattern is a fixed timestep with an accumulator: bank the real time that has elapsed, then spend it in fixed-size chunks of \Delta t_{\text{sim}}, no matter how fast or slow the display is rendering.

let accumulator = 0; const dt = 1 / 240; // fixed simulation step — never changes function frame(realElapsed: number): void { accumulator += realElapsed; // bank however long the last frame took while (accumulator >= dt) { stepPhysics(dt); // advance the world in fixed chunks accumulator -= dt; } render(); // draw whenever the display is ready }

Fixed vs variable timestep — why physics demands fixed

The tempting shortcut is a variable timestep: measure how long the last frame took and advance the simulation by exactly that. It keeps motion looking real-time on any hardware, and for purely kinematic motion (moving something at a known velocity) it is fine — x \mathrel{+}= v\,\Delta t is correct for any \Delta t.

Physics is a different animal. Numerical integrators (Euler, Verlet, and friends) are only stable when the step is small enough; feed a stiff spring or a fast collision a large, wobbly \Delta t and the numbers overshoot, feed back on themselves, and diverge to infinity. A variable step also makes the simulation non-deterministic: run the same scene twice on two machines and you get two different results, which wrecks replays, networked games and reproducible tests. A fixed \Delta t_{\text{sim}} keeps the integrator in its stable range and makes every run bit-identical.

The single most common beginner mistake in game physics is stepping the simulation with the display frame time — literally passing the measured frame duration straight into the integrator:

// DON'T: physics step = whatever the last render took function frame(realElapsed: number): void { stepPhysics(realElapsed); // dt is now at the mercy of the frame rate render(); }

Now the timestep is hostage to performance. When the machine is fast, frames are short and physics is stable. But the instant the frame rate drops — a heavy scene, a background download, a debugger pause — \Delta t balloons, the integrator overshoots, and stiff forces feed back on themselves: characters launch through walls, ragdolls fling into orbit, the sim "explodes". Even without a crash, gameplay literally runs at different speeds on different hardware. The fix is the accumulator loop above: step physics at a fixed \Delta t_{\text{sim}} and let only the rendering ride the variable frame rate. Never couple the two.

Interpolation: reading between the samples

Once motion lives as samples, you often need a value between two of them — for slow-motion, for retiming a shot, or (with the accumulator above) to draw the world at a display time that falls partway through a simulation step. You interpolate. Given samples x_n and x_{n+1} a fraction \alpha \in [0,1] of the way from one to the next, the simplest estimate is linear:

x(\alpha) = (1-\alpha)\,x_n + \alpha\,x_{n+1}.

Want a 4\times slow-motion clip? Generate three interpolated in-between frames for every pair of original frames. Retiming — speeding a shot up or slowing it down — is the same idea: resample the motion at new, non-integer frame times and interpolate to fill the gaps. Good slow-motion tools use smarter, motion-aware interpolation than plain linear, but the principle is identical: the continuous signal was only ever sampled, so any in-between value is a reconstruction, and how well it looks depends on how good that reconstruction is — which loops us right back to Nyquist. If the original sampling aliased, no interpolation can invent the motion that was never captured.

Because you are asking the reconstruction to do work the sampling never supported. A clip shot at 30 fps has one snapshot every 33 ms; stretch it 8\times and you must invent seven frames between every real pair. Linear interpolation just cross-fades, so a fast-moving object smears or ghosts. That is exactly why sports and nature filmmakers shoot at 240, 1000 or more fps up front: they oversample the motion so that when it is played back slowly, every "slow" frame is a real sample, not a guess. Capture the cycles you will need — you cannot add them back later.