What actually happens between keypress and pixel
Five stages, one honest read on your rendering pipeline. Here is what each stage costs, what usually stalls it, and the single change that moves your numbers.
Browser engineers documented this pipeline long before anyone measured it in the field. Twenty years on it is still the fastest explanation for why a page feels slow when every chart says it is fine.
Most pages stall in one or two stages, and that is normal. What matters is which stage owns your time, and whether it has moved. A week at 3.5s after a year at 1.8s says more than any single trace ever will.
The five stages, briefly
Each stage reflects work the browser has to repeat. The more of the page a change touches, the more of the pipeline runs again, and the further down the list you pay for it.
| Stage | What it does | Typical cost | Read |
|---|---|---|---|
| Hydration | Re-running your app on the client | 200-900 ms | Expensive |
| Style | Matching every selector again | 60-400 ms | Expensive |
| Parse | Bytes into DOM and CSSOM | 10-60 ms | Cheap |
| Paint | Filling pixels for changed areas | 5-40 ms | Cheap |
| Layout | Measuring boxes after a change | 40-300 ms | Watch it |
| Long tasks | Any block over 50 ms on the main thread | Blocks input | Expensive |
| Thrash | Read and write loops inside one frame | Compounding | Expensive |
Measure the field, not your laptop
What actually moves the number
Four things move almost every site, in this order. Work down the list rather than doing all of them at once, or you will not know which one worked.
- Images first.Correct sizes and modern formats move most LCP scores within a day.
- Then fonts.Preload one weight, subset it, and let the fallback show text immediately.
- Then third-party scripts.Audit what each tag costs, and delete the two nobody can name.
- Then the framework.Only once the first three are done, and only with a number to beat.
A page responds to deleted code faster than it responds to configuration. Almost every tool on the shelf is trying to do what removing one script already does.
Sam Kettleborough, performance engineer
What to skip
- Micro-optimising loops. The main thread is not waiting on your array method.
- Adding a library to fix the last library. It works until the next release does not.
- Rewriting on a hunch. Change one thing, for two weeks, and measure it.
A rewrite is not a fix
Key takeaways
- Parse and paint are cheap; style, layout and hydration are where the time actually goes.
- Your field data across a week beats any single lab run.
- Change one thing at a time so you can tell what worked.
Watch: reading a trace in two minutes
The five stages, illustrated





Budget it for two sprints
Two sprints of honest measurement gives you a baseline you can argue with in planning. Drop the budget file into CI so the argument happens on the pull request instead.
If you would rather have it calculated for you, the action scores every build and averages the week. The scoring rules are open, and about as complicated as round(avg(kb_over_budget)) makes them sound.
score = 100 - (kb_over_budget * 0.4)
- (long_tasks * 6)
+ (cache_hit_rate * 10)
Comments
8 Oldest firstThe cost column finally explained why our redesign felt slower with fewer requests. We were paying for style recalculation on every scroll and nobody had looked there.
That one is the most common and the least discussed. Glad it landed, Rosa.
Migrating one component at a time instead of the whole layout was the difference between shipping and reverting. Wish this had been the first thing I read.
Any chance of a budget file for multi-page apps? Ours has twelve entry points and the single-bundle version does not map onto it.