Motion is not what makes a site slow. Most of the time the cause is the way it gets built: animating the wrong CSS properties, loading a 3D library before the page can be read, or running a scene nobody can see. Website animation performance comes down to a small set of rules, and they are the same ones we follow on every motion and 3D build, including the figures on this site. Get them right and a page can move a lot and still feel instant on a mid range phone.
01How browsers draw a frame, and why it matters
Every frame a browser draws goes through up to three stages of work: layout (where everything sits), paint (filling in the pixels) and compositing (putting the painted layers together on screen). How expensive an animation is depends on which of those stages it forces the browser to repeat, up to 60 times a second.
According to web.dev's guide on compositor only properties, only two properties can change without triggering layout or paint: transform and opacity. Everything else costs more. Animate width, top or margin and the browser works out the layout of the page again on every frame. Animate a shadow or a blur and it repaints on every frame.
The difference is not academic. In web.dev's own high performance animation guide, the same movement built with top and left dropped half of its frames, while the transform version dropped about 1%.
| You want to | Animate this | Not this |
|---|---|---|
| Move something | transform: translate() |
top, left, margin |
| Resize something | transform: scale() |
width, height |
| Fade something in or out | opacity |
visibility steps, display |
| Reveal a panel | transform or clip-path on a fixed box |
height: auto transitions |
| Soften on hover | opacity on a pre-drawn shadow layer |
the box-shadow itself |
02A website animation performance checklist
This is the order we work through on a site with motion, from the cheapest fixes to the ones that need a decision.
CSS and UI motion
- Animate only
transformandopacitywherever the design allows it - Keep
will-changefor elements that are about to animate, and remove it afterwards; web.dev warns that promoting too many layers costs memory, which hurts most on low end phones - Never drive animation from
scrollevent handlers that read layout; use CSS, or an observer that only reports what changed - Keep durations short for interface feedback; a hover or a menu should feel like a response, not a show
The first screen
- Never start the hero heading at zero opacity; the page cannot count its largest content as painted until it is visible, so a one second fade costs a second of Largest Contentful Paint
- Keep the main text in plain HTML, outside any canvas or animation library
- Let decorative motion start after the content is there, not before
- Give every animated box a fixed size so nothing shifts when it starts
We go through the vitals themselves, and how to read them, in our Core Web Vitals checklist.
03Loading 3D and WebGL without blocking the page
A Three.js scene is often the heaviest thing on a marketing page: the library, the model files, the textures and the shader setup. None of it is needed to read the page, so none of it belongs in the first download.
The pattern we use:
- Ship the page without it. The heading, copy and calls to action are HTML and CSS. The 3D area is an empty box with a fixed aspect ratio, and a still image or a drawn poster where that helps.
- Load on interaction or idle. Import the 3D code with a dynamic
import()the first time the visitor scrolls, moves the pointer or taps, or when the browser reports idle time throughrequestIdleCallback. MDN marks that API as having limited availability, so feature detect it and fall back to a timer. - Load only what is close. Use the Intersection Observer API so a scene halfway down the page loads as the visitor gets near it, not on arrival.
- Keep scenes light. Compress models and textures, reuse materials, cap the pixel ratio on high density screens, and stop the render loop when nothing is changing.
Done this way, the page is readable and usable before a single byte of WebGL arrives, and the scene fades in over a box that was already reserved for it.
04Pause what nobody is looking at
An animation that keeps running off screen still costs battery, heat and main thread time, and it can make the next tap feel slow. Browsers help a little: MDN notes that requestAnimationFrame callbacks pause in most browsers when the tab is in the background. They do not pause when a scene has simply been scrolled past.
- Stop each render loop when its canvas leaves the viewport, using the same Intersection Observer
- Pause CSS animations on off screen sections with
animation-play-state, or remove the class that runs them - Base animation on the frame timestamp rather than counting frames, so it resumes in the right place
- Never run two render loops when one can draw both scenes
05Reduced motion is part of performance
Some visitors have told their device they want less motion. For people with vestibular disorders, large movement, parallax and zooming can cause dizziness and nausea. The prefers-reduced-motion media query exposes that setting from macOS, iOS, Windows, Android and Linux desktops, and every site with motion should read it.
Two WCAG 2.2 criteria set the bar:
- Pause, Stop, Hide (level A): anything that moves, blinks or scrolls automatically for more than five seconds alongside other content needs a way to pause, stop or hide it.
- Animation from Interactions (level AAA): motion triggered by the visitor, such as scroll driven effects, can be turned off unless it is essential.
In practice:
- Under
prefers-reduced-motion: reduce, replace movement with a simple fade or a still frame, rather than removing content - Do not load the 3D library at all for those visitors if the scene is purely decorative; show the poster instead
- Give looping or autoplaying motion a visible pause control
- Never animate the only copy of important information
The bonus: visitors who turn motion down get a lighter, faster page, which is exactly what many of them are after.
06Test on the phones your visitors actually use
A new laptop hides almost every animation problem. Plenty of your visitors will be on an older or mid range phone, on a patchy connection, with a dozen other tabs open.
- Test on a real mid range Android device, and with CPU throttling in Chrome DevTools
- Turn on Paint Flashing in the Rendering panel; anything that flashes on every frame is repainting
- Record a performance trace while scrolling and while the 3D area loads, and look for long tasks
- Check the field data in Search Console a few weeks after launch, not just the lab score
07Plan motion early, not at the end
Most animation performance problems are decided in the design, not the code. A brief that names where motion should help, a design system whose components already animate the cheap way, and a performance budget agreed before anyone opens a 3D tool will prevent most of what we end up fixing on other sites. If you are briefing a studio, put motion in the brief from the start; our guide to writing a website design brief covers what to include.
If a site you already have stutters, drops frames or fails its vitals after a motion redesign, that is the kind of thing our technical SEO and performance work finds and fixes, usually without taking the motion away.
Sources
- How to create high-performance CSS animationsweb.dev, checked
- Stick to compositor-only properties and manage layer countweb.dev, checked
- Window: requestIdleCallback() methodMDN Web Docs, checked
- Intersection Observer APIMDN Web Docs, checked
- Window: requestAnimationFrame() methodMDN Web Docs, checked
- prefers-reduced-motionMDN Web Docs, checked
- Understanding Success Criterion 2.2.2: Pause, Stop, HideW3C WAI, checked
- Understanding Success Criterion 2.3.3: Animation from InteractionsW3C WAI, checked


