Command Palette

Search for a command to run...

Hectal
Shots in this scene
SHOT 6.6Intermediate ~19 min · shot 6 of 7

Low-End Devices, Battery and Throttling

In one line

Most of your visitors have slower CPUs, less memory and smaller batteries than your laptop. Test with CPU throttling, pause animations nobody can see, and stop work in hidden tabs, because even a composite-only loop keeps the browser drawing every frame.

0/7 · 0%

Think of it like this

A car that is fast on an empty motorway can be slow in a crowded city. Lena's phone is the city. Drive your page there before you say it is fast.

Words you'll meet

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

CPU throttling
A DevTools setting that makes your CPU work slower (for example 4×) to imitate a cheaper device.
IntersectionObserver
A browser API that calls your code when an element enters or leaves a scroll area or the viewport.
visibilitychange
An event on document that fires when the tab becomes hidden or visible.
content-visibility: auto
A CSS property that lets the browser skip layout and paint for off-screen content.
refresh rate
How many times a second the screen can show a new frame: 60, 90 or 120 Hz on most phones.

Step by step

01Pause the invisible

"Pause what nobody sees" has 50 spinning squares in a small scroller. The observer pauses the ones out of view. The status line on the page shows the exact counts (4 to 5 playing, the rest paused). Press Scroll down and Back to top: the playing ones change.

02Check the fallback

If IntersectionObserver does not exist, the else branch leaves everything playing and says so. A missing optimisation must never break the page.

03Hide the tab

"A hidden tab" fakes the state and fires the event a browser would fire. The handler pauses all 3 animations and resumes them.

04Throttle in DevTools

Performance panel, CPU 4× or 6×, record. The Main track gets long and frames drop; compositor-only animations keep going.

Try it yourself

  1. 1

    Scroll and watch the status

    In "Pause what nobody sees" press Scroll down 400 px, then Back to top. The status line shows the playing count after each scroll.

  2. 2

    Pause in the timeline

    In "A hidden tab" press Pretend the tab is hidden: the three dots stop and the tracks show a paused state. Press Pretend it is visible: they carry on from where they were.

Code & diagrams

Pause what nobody sees

The exact counts are on the page (they depend on the frame size). The observer's `root` is the scroller, not the window.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.

Expected console output

at load: most items paused: true
scrolled: most items paused: true
A hidden tab

A preview cannot hide a real tab, so the page fakes `visibilityState` and fires `visibilitychange`. It resumes every animation; a real page should resume only those that were running (see the practice answer).

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.

Expected console output

hidden: paused 3
visible: resumed 3

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 to resume

In "Pause what nobody sees" replace the observer's e.target.classList.toggle("off", !e.isIntersecting); with if (!e.isIntersecting) e.target.classList.add("off");.

terminal
$ Press Scroll down 400 px, then Back to top
── what you'll see ──
at load: playing 4, paused 46 of 50
scrolled: playing 0, paused 50 of 50
top: playing 0, paused 50 of 50
Measured in headless Chromium. The status line shows these counts; the items at the top never start again.

Myth vs fact

Myth

A composite-only animation costs nothing, so I can loop it forever.

Fact

It uses no main thread, but the browser still draws a frame 60 or 120 times a second. In my trace: 90 draws in 1.5 s when playing, 0 when paused.

Remember this

  1. 1

    The problem this solves: the hero was smooth for Kofi and stuttered for Lena, and a looping animation that nobody can see still costs power. You need to feel the slow device on your fast machine, and to stop what is not needed.

  2. 2

    Throttle the CPU. In DevTools, Performance panel, set CPU to 4× or 6× slowdown, then record. It is a rough stand-in for a phone, not an exact one. In my runs the 200-row thrash of Topic 6.4 took 15 to 20 ms normally, 120 to 180 ms at 4× and 210 to 360 ms at 6×. Always confirm on one real cheap phone.

  3. 3

    An idle loop is not free. I traced 50 spinning transform animations (composite only, nothing on the main thread). The browser still drew 90 frames in 1.5 seconds, forever. With the same animations paused it drew 0. Drawing keeps the GPU awake, which costs battery, and a 120 Hz screen draws twice as often.

  4. 4

    Pause what nobody sees. An IntersectionObserver tells you when an element enters or leaves the view. Toggle animation-play-state: paused for the ones out of view, as in "Pause what nobody sees". The browser does not do this for you for CSS animations. content-visibility: auto goes further for big lists: it skips rendering off-screen sections altogether.

  5. 5

    Background tabs. A hidden tab does not draw frames, so requestAnimationFrame callbacks stop, and Chrome slows timers to about once a second, and after 5 minutes hidden it can slow chained timers on a silent page to once a minute (Chrome's documented rules). On return, a time-based animation jumps to where it should be, and a frame-counted one carries on from where it stopped (Topic 1.3). Listen to visibilitychange to pause what you control.

  6. 6

    Battery and refresh rate. Some devices lower the frame rate in power-saving modes (your page cannot set it), and a 120 Hz screen gives the main thread only 8.3 ms a frame. Keep decorative loops few, slow and short, and give anything over five seconds a pause control (Topic 7.4).

Explain it without notes

01

Why does a looping transform animation still use battery?

02

How do you stop animations that are off screen?

Practice

01

Write a function pauseWhenHidden(animation) that pauses a Web Animation when the tab is hidden.

02

Write the observer code that plays a hero animation only while the hero is visible.

Done when you can

  • You can slow the CPU in DevTools and read the result

  • You can pause off-screen animations with an observer, with a safe fallback

  • You can pause work when the tab is hidden

  • You know a looping composite animation still draws frames