Skip to content
PerformanceCSSFrontendGuide

60 fps or nothing: what we learned polishing the performance of our own site

Pablo Huget · · 2 min read
60 fps or nothing: what we learned polishing the performance of our own site

Our site has WebGL smoke, a canvas full of orbits, infinite marquees, twinkling stars and a grainy background. And it still runs smooth. It wasn't free: along the way we went from 60 fps to 5 (yes, five) and came back. Here's what we learned — chapter and verse — so it doesn't happen to you.

The golden rule: transform and opacity, nothing else

The browser paints in layers. Animating transform or opacity only moves layers that are already painted (the GPU handles it, for free). Animating almost anything else (filter, box-shadow, width, background-position over large areas) forces a repaint on every frame. One small element gets a pass; twenty elements don't.

Trap #1

The review stars "breathed" by animating filter: drop-shadow. Every visible star was re-rasterized 60 times per second. With ~25 stars on screen: noticeable stuttering. Now they breathe with transform: scale() and the glow is static. Identical look, zero cost.

mix-blend-mode and filter: the silent killers

The first version of our hero used mix-blend-mode for the noise and filter: blur(90px) for the glows, on top of a canvas that repaints every frame. Result: 5 fps. The blend forces the whole screen to recompose and the blur re-blurs on every repaint. The fix: glows with pre-blurred radial-gradient (no filter) and zero blend modes over anything animated. From 5 fps to 40+ with the same look.

The rAF loops nobody sees

We had a ball of light that followed the cursor with smooth interpolation. Pretty, and a requestAnimationFrame loop running always, even when the mouse had been still for a minute. Every continuous effect has to answer three questions: does it pause off screen? (IntersectionObserver), does it pause when the tab is hidden? (visibilitychange), does it stop when idle? If any answer is no, it's stealing battery.

backdrop-filter: you pay on every scroll

A header with backdrop-filter: blur(16px) re-blurs whatever's behind it on every scroll frame. We dropped to 12px and lightened the background: same glass, smaller bill. If your site "stutters when you scroll", start here.

/* BAD: repaints every frame */
.star { animation: glow 2s infinite; }
@keyframes glow { 50% { filter: drop-shadow(0 0 9px gold); } }

/* GOOD: pure compositor, same effect */
.star { filter: drop-shadow(0 0 5px gold); /* static */ }
@keyframes glow { 50% { transform: scale(1.1); } }

How we measured it

No intuition: DevTools → Performance with CPU x4, the Paint flashing layer turned on (if something flashes green during an animation, you're repainting), and real recordings on a modest laptop. The site has to run smooth on your client's computer, not yours.

5→60
hero fps after removing blend+blur
0
filter/shadow animations when idle
x4
CPU throttling on every test

An animation that drops below 60 fps isn't an effect: it's a bug with glitter on it.

Pablo Huget, Web Developer at Intervolutions
Pablo Huget
Web Developer · Pixel artist
Web developer at Intervolutions. Interface, performance and detail: he reads a screen down to the 16×16 grid.
What we do about this when the project is yours

Ready to build something big?

Pick what you want to build, add a few extras, and watch your plan take shape in real time. No emails, no waiting. Go ahead — play.