Course outline
Time-Based Animation
In one line
Animate by how much time has passed, not by how many frames have been drawn. A wheel then turns at omega = v / r radians a second on a 30 fps phone and on a 144 Hz monitor alike.
Think of it like this
Two friends walk to school. One takes "ten steps a second", the other "five". If the rule is "ten steps, then stop", the first arrives sooner. If the rule is "walk at one metre a second", both arrive together, however fast their feet go. Frames are steps. Time is the clock on the wall.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- delta time
- The seconds since the previous frame. Multiply every speed by it so motion does not depend on the frame rate.
- angular speed (ω)
- How fast something turns, in radians per second. One turn is 2π radians (about 6.283).
- rolling radius
- The distance from the axle to the ground. The wheel moves one circumference (2π r) for each turn.
- frame rate (fps)
- How many pictures are drawn a second. Phones often draw 30 or 60, monitors 60 to 240.
- aliasing (wagon wheel)
- A fast, repeating motion sampled too slowly looks slow, stopped or reversed.
Step by step
01Pick the units first
Write speeds with their units: road speed in m/s, wheel speed in rad/s. A speed in "degrees per frame" has no meaning on another machine.
02Work out omega
20 km/h is 20 / 3.6 = 5.556 m/s. Divide by the radius 0.352 m: 15.78 rad/s. Divide by 2π for turns: 2.51 a second. One turn rolls the bike 2π × 0.352 = 2.212 m.
03Multiply by delta
Each frame: wheel.rotation.z -= omega * delta. The minus makes the top of the wheel move forward (towards +x), which is a clockwise turn when you look from +z.
04Test the edge: a long gap
The third example feeds the loop a 12 s gap, as a hidden tab would. Without the clamp the wheel turns 30 times in one frame. With it, a quarter turn.
Try it yourself
- 1
Change the speed
In the first example set
SPEED_KMHto 5, then 60. The log prints 1.389 m/s and 3.95 rad/s (0.63 turns a second) for 5 km/h, and 16.667 m/s and 47.35 rad/s (7.54 turns a second) for 60 km/h. At 60 the orange spoke looks stuck or reversed: that is the wagon-wheel effect from the second example. - 2
Use a slower clamp
In the third example set
MAX_DELTAto 0.05. Frames 1 to 3 are 0.016 s, so they do not change. Frame 4 now turns the wheel 0.13 turns instead of 0.25.
Code & diagrams
Look at the status bar: the fps changes with your machine, the wheel's speed does not. Change `SPEED_KMH` to 5, 60 or 0.
Console
Run the scene to see its console.Expected console output
road speed: 5.556 m/s
wheel speed: 15.78 rad/s = 2.51 turns/s
one turn rolls the bike: 2.212 mA 12-spoke wheel looks identical every 30 degrees. This computes what you see, and nothing is drawn.
Console
Run the scene to see its console.Expected console output
km/h fps degrees/frame looks like
5 30 7.5 forwards 7.5 deg
5 60 3.8 forwards 3.8 deg
5 144 1.6 forwards 1.6 deg
15 30 22.6 BACKWARDS 7.4 deg
15 60 11.3 forwards 11.3 deg
15 144 4.7 forwards 4.7 deg
40 30 60.3 standing still
40 60 30.1 standing still
40 144 12.6 forwards 12.6 deg
100 30 150.7 forwards 0.7 deg
100 60 75.4 BACKWARDS 14.6 deg
100 144 31.4 forwards 1.4 degPlain numbers, no canvas. Frame 4 arrives after a 12 s gap. Without the clamp the wheel would jump 30 turns.
Preview
Console
Nothing logged yet.Expected console output
frame delta s turns (raw) turns (clamped)
0 0.000 0.00 0.00
1 0.016 0.04 0.04
2 0.017 0.04 0.04
3 0.016 0.04 0.04
4 12.000 30.14 0.25
5 0.017 0.04 0.04
total turns: 30.30 raw, 0.42 clampedBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Per-frame motion
In the first example, replace radiansPerSecond * timer.getDelta() with 0.1.
Break #2
Forgetting the clamp
In a loop of your own, add delta straight to the angle with no Math.min.
Myth vs fact
Myth
requestAnimationFrame always runs at 60 fps.
Fact
It runs once per screen refresh: 30, 60, 90, 120, 144 or 240 a second, and it slows when the page is busy. It pauses completely in a hidden tab.
Myth
A wheel that looks backwards is turning backwards.
Fact
It is turning forwards. The eye sees only one picture per frame and picks the smallest turn that explains it.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Fixed time steps are the alternative to variable delta. A physics engine (Phase 16) steps in fixed slices (for example 1/60 s) and runs as many as the real time needs. This keeps results repeatable. Animation that only looks right does not need it.
- ▸
Adding
deltato an angle that grows for hours loses float precision slowly. For a wheel it does not matter. For a long clock, keep the angle modulo 2π:angle = (angle + step) % (2 * Math.PI). - ▸
The three.js
TimerreplacedClock(deprecated since r183) becauseClockreadperformance.now()itself and so could not be driven by the timestamprequestAnimationFramealready gives you. Passingtimeintotimer.update(time)keeps every system on the same frame time.
Remember this
- 1
The problem this solves:
wheel.rotation.z -= 0.1inside the loop turns the wheel 0.1 radians per frame. At 144 frames a second that is 14.4 rad/s. At 30 frames a second it is 3 rad/s. The speed depends on the screen, and so does everything built on it. - 2
Delta time is the seconds since the previous frame. Multiply every speed by it:
angle += speed * delta, withspeedin radians per second. You metTHREE.Timerin Topic 3.2.timer.update(time)thentimer.getDelta()gives the delta, and withtimer.connect(document)it is 0 while the tab is hidden. - 3
How fast should a wheel turn? A wheel that rolls without slipping moves the bike one circumference (2π r) per turn. At road speed
vmetres per second the wheel turns atω = v / rradians a second. Our tyre's rolling radius is 0.33 m (rim) + 0.022 m (tyre) = 0.352 m. At 20 km/h (5.556 m/s) that is 15.78 rad/s, or 2.51 turns a second. - 4
The wagon-wheel effect. A screen shows still pictures. Our wheel looks the same after every 30° (12 spokes), so a turn of 30° per frame looks like no movement, and 45° looks like 15° the wrong way. This is not a bug in the code. It is sampling, the same reason a real wheel on film can look slow or reversed. At 144 fps the turn per frame is small, so the wheel looks right.
- 5
Clamp the delta.
deltais large after a stall: a breakpoint, a heavy garbage collection, a slow page. If the tab was hidden for 12 seconds and the code adds 12 s of motion in one frame, a wheel jumps 30 turns. Cap it:Math.min(delta, 0.1). The world then slows down on a very slow device instead of lurching. Ourloop.jsdoes this in the build. - 6
Never log time. The examples below print values computed from fixed steps (
1 / 60), because real clocks differ on every run. That is also how you test animation code: call the update with a fixed delta and check the result.
Explain it without notes
Why does rotation.z += 0.1 give different speeds on different screens, and how do you fix it?
A bike rolls at 18 km/h on wheels with a 0.35 m rolling radius. How fast should the wheel turn, in radians and in turns a second?
Practice
Write wheelStep(speedKmh, radius, delta) that returns the radians to add this frame. What does it return for 36 km/h, radius 0.35 and delta 1/60?
A tab is hidden for 30 seconds and shown again. With Timer.connect(document) and a clamp of 0.1, how far can the wheel jump on the first frame?
Trade-offs
- ↔
A clamp keeps the world calm after a stall but makes the world run slow on a device that is always below 10 fps. Pick the clamp for the experience you want on a bad day.
Done when you can
Explain why per-frame motion depends on the screen
Work out omega from a road speed and a wheel radius
Clamp the delta and say what it protects against
Say why a fast wheel can look slow or reversed