Smooth Animations on the Web Are Not Magic — They're Physics

Most janky animations in production have the same root cause: work happening on the main thread that should happen on the compositor. Understanding why fixes the whole class of problems.
Every frontend developer has dealt with a janky animation at some point. The dropdown that stutters. The modal that drags when it fades in. The scroll-linked effect that feels like it's lagging behind your finger. The usual response is to reach for a library, tweak the duration, or try a different easing. These fixes occasionally work, but they're addressing symptoms. The underlying cause of most animation performance problems is the same: something is triggering layout or paint during the animation, and the browser can't keep up at sixty frames per second.
Understanding why that happens — and how to avoid it — requires knowing a bit about how the browser renders a frame. It's not complicated, but it changes how you think about every animation you write.
A sixty-fps animation gives the browser 16.7 milliseconds to do everything needed to produce a frame. Layout, paint, composite. That's not a lot of time to waste.
The rendering pipeline: three stages you need to know
The browser renders each frame through three stages: layout (calculating the size and position of elements), paint (drawing pixels based on that layout), and composite (combining painted layers and sending them to the screen). These stages happen in order, and you can't skip an earlier stage if a later one depends on it.
Here's the important part: not all CSS properties trigger all stages. width and height trigger layout — the browser has to recalculate positions for potentially every element on the page. background-color skips layout but triggers paint. transform and opacity skip both layout and paint — they run entirely on the compositor thread, which runs independently of the main thread. That's why they're smooth even when the main thread is busy.
- Layout + Paint + Composite:
width,height,margin,padding,top/left - Paint + Composite only:
background-color,color,box-shadow,border-color - Composite only (fastest):
transform,opacity
The practical implication: animate transform, not position
This is the single change that fixes the majority of animation jank. Moving an element by animating left and top triggers layout on every frame — the browser recalculates the element's position in the document flow sixty times per second. The same visual movement, animated with transform: translate(), bypasses layout entirely.
will-change: a hint, not a guarantee
The will-change CSS property tells the browser to promote an element to its own compositor layer before the animation starts, so it's ready to animate without triggering layout or paint. It can make a meaningful difference for complex animations, but it has a real cost: promoted layers consume GPU memory. Using it on too many elements at once can actually hurt performance instead of helping it.
The rule of thumb: apply will-change: transform only when you have profiled and confirmed a specific element is causing jank, add it just before the animation starts (via JavaScript if needed), and remove it when the animation ends. Leaving it on all the time is wasteful.
When CSS isn't enough: the Web Animations API
For animations that need to be driven by JavaScript — scroll-linked effects, physics-based motion, complex sequencing — the Web Animations API is worth knowing. Unlike requestAnimationFrame, WAAPI runs off the main thread when the animated properties are compositor-only. That means scroll-linked animations stay smooth even when the main thread is busy processing other work.
Libraries like Framer Motion abstract over these details in React, but they follow the same underlying principles. The reason Framer Motion animations tend to be smooth is that it animates transform and opacity by default. When you override that to animate layout properties, you lose the performance benefit regardless of what library you're using.
Conclusion
Smooth animation is not about easing curves or duration tuning. It's about understanding what work the browser has to do to produce each frame, and minimizing the expensive parts. Once that mental model is in place, most animation performance problems become obvious: you can look at a janky animation and immediately identify the CSS property that's causing layout recalculation.
The same knowledge that makes animations smooth also makes you a better CSS developer in general. Understanding the rendering pipeline changes how you reason about every style you write, not just the animated ones.