The Conductor's First Un-battened Score: On the Slipping Tempo of a Perfectly Optimized Animation Without a `requestAnimationFrame`
There’s a piece of received wisdom in our craft that goes something like this: if you want something to move, you reach for CSS. It’s the declarative, performant, browser-optimized choice. JavaScript, with its mutable state and potential for jank, is the clunky, imperative fallback. CSS animations are the gold standard; JS-driven ones are, at best, silver. This is a tidy story. It is also, in a crucial way, incomplete.
We’ve all been taught to treat `setInterval` or `setTimeout` for animation as a kind of professional malpractice, and rightly so. Handing a timing loop to a system that doesn’t care about the frame rate of the browser is like asking a metronome to conduct an orchestra. It will keep a steady beat, oblivious to whether the violins are ready, the brass has taken a breath, or the entire performance is about to stutter into a chaotic mess. The beat goes on, but the music falls apart.
So we retreat to CSS, to `@keyframes` and `transition: transform 0.3s ease-out`. And for many things, this is exactly the right move. It is clean, efficient, and wonderfully abstracted. But this wholesale retreat has bred a subtle complacency. It has allowed us to believe that the problem of JavaScript-driven animation is solved simply by not doing it. We’ve conflated the tool with the technique.
The real wisdom wasn’t to avoid JavaScript for animation; it was to avoid *bad timing mechanisms* for animation. The antidote to `setInterval` was never solely CSS—it was `requestAnimationFrame`. This is the conductor’s baton. It doesn’t just keep time; it listens. It waits for the precise moment the browser is ready to paint the next frame, and only then does it execute your callback. It syncs your motion to the very heartbeat of the rendering engine.
By dismissing all JS animation, we inadvertently limit ourselves. We forgo the ability to create animations that respond to complex user input, that choreograph intricate sequences impossible with pure CSS, or that build physics-based interactions that feel truly organic. We sacrifice nuance for the sake of a perceived purity.
The perfectly optimized page, then, isn’t one that has banished JavaScript from its animations. It’s one that understands the precise role of each tool. It uses CSS for its raw, declarative power where appropriate, and it employs `requestAnimationFrame` for its nuanced, imperative control where necessary. It knows that a conductor is not a metronome, and that true performance isn’t just about speed—it’s about rhythm, timing, and harmony with the entire system.
Notes & further reading
A few pages I came back to while writing this:
- The Tailor's First Unlined Garment: On the Hidden Fraying of a Perfectly Optimized Page Without a `preload`
- The Sculptor's First Un-anchored Base: On the Shifting Foundation of a Perfectly Optimized Hero Without a `min-height`
- The Compositor's First Un-justified Line: On the Ragged Edge of a Perfectly Optimized Text Block Without `hyphens`