The Clockmaker's First Un-synchronized Gear Train: On the Precise Tick of a `requestAnimationFrame` Loop Versus the Jittery Tock of `setInterval`
In the quiet workshop of animation, where we strive to bring interfaces to life, the choice of timing mechanism is everything. It’s the difference between the steady, reliable tick of a master clock and the erratic, unpredictable chime of a novelty timepiece. For years, the tool of convenience was `setInterval`, a simple promise to execute a function every *n* milliseconds. It felt straightforward, like setting a kitchen timer. But this simplicity is a facade, masking a fundamental misalignment with the very medium we work in: the browser’s refresh cycle.
The `setInterval` loop operates in its own world, blissfully unaware of the browser’s frantic efforts to paint a new frame to the screen roughly 60 times a second. It dutifully fires its callback on a strict schedule, but this schedule often falls out of sync with the display. A callback might fire right after a frame has been painted, forcing the browser to calculate changes that sit idle for over 15 milliseconds before the next paint. Worse, it might fire multiple times between frames, piling up work that gets clumsily batched into a single, janky render. It’s like a metronome ticking away in a different room from the orchestra; the tempo might be mathematically correct, but the musicians can’t follow it. The result is a perceived stutter, a subtle but tangible instability that feels amateurish.
The Synchronized Rhythm of the Display
Enter `requestAnimationFrame` (rAF). This API is not a timer; it is an invitation. It asks the browser, "Please call my function right before you are about to paint the next frame." This simple change in philosophy is profound. An rAF loop aligns our JavaScript animations perfectly with the browser’s rendering pipeline. The browser can batch style calculations, layout, and paint together efficiently, knowing that all the necessary updates have been prepared just in time.
The consequence is fluidity. There is no wasted effort, no frantic rushing to catch up, and crucially, no dropped frames when a tab is backgrounded (the browser naturally pauses rAF callbacks to conserve resources). It respects the user’s machine and the constraints of the medium. The animation code becomes a single, synchronized gear in the larger mechanism of the page, turning in lockstep with the display. The motion is smooth, confident, and integrated.
Of course, the `setInterval` approach still has its place for tasks that require strict, time-based logic completely independent of visual updates—a countdown timer, for instance. But for any manipulation of the DOM that the user is meant to see, from a sliding menu to a complex data visualization, `requestAnimationFrame` is the clear craftsman’s choice. It acknowledges that performance is not just about raw speed, but about graceful coordination. It’s the discipline of ensuring that every movement, no matter how small, contributes to the seamless illusion of a living application, rather than breaking the spell with a clumsy, un-synchronized jerk.
Notes & further reading
A few pages I came back to while writing this:
- The Stonemason's First Un-mortared Wall: On the Shifting Foundation of a Perfectly Fluid Layout Versus the Settled Stone of a Container Query
- The Calligrapher's First Un-blotted Nib: On the Precise Flow of a Perfectly Subsetted Font
- The Winter Solstice's First Un-insulated Pane: On the Frosted Glass of a Perfectly Un-throttled Main Thread