Shots in this scene
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.
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
documentthat 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
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
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
The exact counts are on the page (they depend on the frame size). The observer's `root` is the scroller, not the window.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
at load: most items paused: true
scrolled: most items paused: trueA 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).
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
hidden: paused 3
visible: resumed 3Break 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");.
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
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
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
An idle loop is not free. I traced 50 spinning
transformanimations (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
Pause what nobody sees. An
IntersectionObservertells you when an element enters or leaves the view. Toggleanimation-play-state: pausedfor the ones out of view, as in "Pause what nobody sees". The browser does not do this for you for CSS animations.content-visibility: autogoes further for big lists: it skips rendering off-screen sections altogether. - 5
Background tabs. A hidden tab does not draw frames, so
requestAnimationFramecallbacks 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 tovisibilitychangeto pause what you control. - 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
Why does a looping transform animation still use battery?
How do you stop animations that are off screen?
Practice
Write a function pauseWhenHidden(animation) that pauses a Web Animation when the tab is hidden.
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