A beautiful site that stutters is a broken site. Users can't articulate why it feels cheap — they just feel it. So I treat 60fps as a design constraint from day one, the same way a print designer treats page size. You have 16.6 milliseconds per frame. Everything you draw has to fit.
Where the milliseconds go
The usual suspects, in order of how often they blow the budget: layout thrash from reading and writing the DOM in the same frame, paint storms from animating properties that aren't transform or opacity, and garbage collection pauses from allocating inside the render loop.
/* cheap: compositor-only, no layout, no paint */
.card { transition: transform 0.3s, opacity 0.3s; }
/* expensive: forces layout + paint every frame */
.card { transition: width 0.3s, top 0.3s, box-shadow 0.3s; }The dev machine lies. A fanless mid-range phone from three years ago tells the truth.
The testing ritual
- Chrome DevTools performance trace with 6x CPU throttle — before every merge.
- A real mid-range Android phone, warm from ten minutes of use.
- Lighthouse in CI: performance score can't drop below 95 without a conversation.
- The scroll test: flick as fast as physically possible, watch for dropped frames.
For WebGL work the budget gets stricter: the particle systems I ship compute motion in the vertex shader so the CPU does nothing per frame but upload a clock value. If the GPU is doing the acting, the CPU should just be holding the camera.
Performance isn't the tax you pay after the fun part. Done right, it is the fun part — the difference between a site people admire and a site people feel.