Command Palette

Search for a command to run...

Hectal
Course outline
Topic 9.5Intermediate ~20 min · step 5 of 6

Reduced Motion and Pausing

In one line

Motion can make people ill and distract those who read. Honour the reduced-motion setting, give a pause button to anything that moves for more than five seconds, and stop drawing when nothing changes.

0/6 · 0%

Think of it like this

A shop with a spinning neon sign. Most customers like it. One finds it hard to look at and asks the owner to switch it off. A good owner has a switch by the door, and does not make the customer ask twice.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

vestibular disorder
A problem of the inner ear and balance system. Screen motion can cause dizziness and nausea.
prefers-reduced-motion
The media feature that says the visitor asked the system to reduce movement.
WCAG 2.2.2
A rule (Level A) that automatic motion lasting over 5 seconds must have a way to pause, stop or hide it.
dirty flag
A true/false value that says "something changed, draw again".

Step by step

01One rule

mayMove(state) returns false if paused, false if reduced motion and the visitor has not pressed Play, and true otherwise. The log prints the table of all combinations.

02One button

The button's text says what it will do: "Pause animation", "Play animation" (reduced motion, not started) or "Resume animation". Its label is always the action, so a screen reader announces it correctly.

03Listen for changes

query.addEventListener("change", ...) updates the state and the label when the system setting changes while the page is open.

04Skip idle frames

The second example is a loop with a dirty flag, fed with 120 fixed steps for each case and counting its real render() calls.

Try it yourself

  1. 1

    Press Pause

    Click the button: the stand stops and the label reads "Resume animation". Click again and it turns on.

  2. 2

    Pretend to be a reduced-motion visitor

    In Chrome, open the developer tools, then the Rendering panel (from the three-dot menu, More tools) and set "Emulate CSS media feature prefers-reduced-motion" to "reduce". The change event fires, so the stand stops and the label reads "Play animation", with no reload. Press it: the label becomes "Pause animation" and the stand turns, because the visitor asked. Set the emulation back to "no emulation" afterwards. (The rule table above gives the same answers for each combination.)

  3. 3

    Count the draws

    In the second example delete the line dirty = true; // draw the final still frame once. The "paused for 2 s" row now prints draws: 0. Then also delete the camera move in the third case: that row prints draws: 0 too. A paused page that draws nothing costs the GPU nothing.

Code & diagrams

A pause button and the reduced-motion rule New tab

Press the button, or Tab to it and press Enter. The table is the rule for every combination. In the browser's developer tools you can emulate `prefers-reduced-motion: reduce` to see the other half.

idle

Console

Run the scene to see its console.

Expected console output

reduced  paused  started by user  moves?
false    false   false            true
false    true    false            false
true     false   false            false
true     false   true             true
true     true    true             false
A loop that draws only when something changed New tab

The camera is moved by hand at frame 30 of the third case. The count is the number of real `render()` calls.

idle

Console

Run the scene to see its console.

Expected console output

turning                                  frames:  120  draws:  120
paused for 2 s                           frames:  120  draws:    1
paused, camera moved at frame 30         frames:  120  draws:    1
playing again                            frames:  120  draws:  120

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Forget the keyboard

In the first example, change the <button> in the html to <div id="pause">Pause animation</div>.

terminal
$ Press Tab
── what you'll see ──
(no console output)
The element is not reachable with Tab and Enter does nothing, so a keyboard user cannot pause.

Myth vs fact

Myth

Reduced motion means no animation at all.

Fact

It means no large, automatic movement. Motion the visitor causes, and short, small transitions, are usually fine.

Myth

A pause button is only needed for video.

Fact

WCAG 2.2.2 covers any content that moves by itself for more than 5 seconds, including a 3D turntable.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Do not decide only once at load time. The setting can change (the visitor opens system settings), and matchMedia fires change.

  • ▸

    Camera animations should become instant jumps under reduced motion, as controls.js already does in Topic 7.5: goTo cuts instead of flying.

  • ▸

    Stopping the loop also stops OrbitControls damping, so the bike can freeze mid-glide. Either keep rendering while the controls have momentum, or set enableDamping = false on the idle loop.

Remember this

  1. 1

    The problem this solves: large or fast movement (spinning, zooming, parallax) can cause dizziness and nausea for people with vestibular disorders, and can pull attention away for people with attention difficulties. Ravi, our screen-reader tester, wants to read the price and hear the panel without the bike turning in the background. A 3D page that never stops is also a battery drain.

  2. 2

    **prefers-reduced-motion** is a CSS media feature that mirrors the system setting ("Reduce motion" on macOS and iOS, "Animation effects" switched off on Windows 11 ("Show animations in Windows" on Windows 10), "Remove animations" on Android). In JavaScript: matchMedia("(prefers-reduced-motion: reduce)").matches. Listen for its change event, because the setting can change while the page is open.

  3. 3

    What "reduce" means. Not "no movement ever". Motion that the visitor starts, such as dragging the bike, is fine. Motion that starts by itself, such as an idle turntable, should be off. Large camera flights should become cuts. Small, short fades are usually fine.

  4. 4

    WCAG 2.2.2, Pause, Stop, Hide (Level A): moving, blinking or scrolling content that starts automatically, lasts more than five seconds and is presented in parallel with other content needs a mechanism to pause, stop or hide it (unless the movement is essential). A turntable beside a price and a buy button is in parallel with other content. A visible button that is reachable with the keyboard and says what it does satisfies it. A 3D turntable that never stops needs one.

  5. 5

    The rule in one place. mayMove = !paused && (!reducedMotion || startedByUser). The pause button always wins. With reduced motion, nothing moves until the visitor asks, and pressing Play counts as asking. Keeping the rule in one function means the keyboard, the button and the system setting cannot disagree.

  6. 6

    Stop drawing when nothing changes. A still scene redrawn 60 times a second still uses the GPU. A loop can check a dirty flag, render only when something moved or the camera did, and skip the rest. The second example draws 120 frames while turning, 1 while paused for two seconds, 1 when the camera moves once, and 120 again after Play.

  7. 7

    A background tab stops requestAnimationFrame by itself. For a visible but idle page, renderer.setAnimationLoop(null) stops the loop and frees the GPU. The cost is that everything that needs a frame (damping on OrbitControls, an animating fade) must wake the loop again.

Explain it without notes

01

What does prefers-reduced-motion: reduce tell you, and what should a 3D page change because of it?

02

Which WCAG rule requires a pause button on a turntable, and what exactly must the button do?

Practice

01

Write mayMove for the rule in this topic.

02

Write the code that reacts when the visitor changes the system setting while the page is open.

Trade-offs

  • ↔

    Skipping idle frames saves battery but needs care: anything that animates (damping, a fade) must mark the scene dirty or it freezes.

Done when you can

  • Read the reduced-motion setting and react when it changes

  • Say what WCAG 2.2.2 asks of an automatic animation

  • Keep the "may anything move" rule in one function

  • Skip drawing frames when nothing changed