Where the number comes from
Two quantities decide how much a page can weigh. Throughput sets how fast bytes arrive; latency sets how long you wait before any of them do. The budget is the time left over after latency, multiplied by throughput, and nothing else on this page is more complicated than that.
The latency term is the one people forget. Four round trips at 300 ms is 1.2 seconds gone before the first byte of HTML, which on a three second target is forty percent of the budget spent on nothing. That is why a redirect in the entry path or a font from a third-party host hurts out of proportion to its size — each one adds round trips, and round trips are paid in whole units regardless of how few bytes come back.
Why split the budget by type
A single total is easy to agree on and impossible to enforce, because nobody adding a feature knows how much of the total they are allowed. Splitting it means the person adding a carousel is spending the image budget and the person adding a date picker is spending the script budget, and both find out immediately rather than at the end of the quarter.
The default split here — images largest, then scripts — reflects how page weight is actually distributed on the median site, not a recommendation. Adjust it to your page. A documentation site with no images should not be reserving 45% for them; a photo gallery legitimately should.
The bytes are not the whole cost
This is worth being clear about because a weight budget can flatter JavaScript badly. An image and a script of the same size cost the same to download and then diverge completely: the image is decoded off the main thread and drawn, while the script has to be parsed, compiled and executed, and every millisecond of that blocks the page from responding to a tap. A 300 KB script and a 300 KB photo are not comparable expenses on a low-end phone, and the script is the more expensive one by a wide margin.
So a byte budget is a proxy. It is a good proxy, because there is a strong relationship between how much stuff a page has and how slow it is, and it has the enormous practical advantage of being checkable in a build step. Keep it, and keep a separate eye on main-thread time.
What to do when a page blows the budget
In rough order of return. Serve images at the size they are displayed and in a modern format, since oversized images are the single largest source of waste on most pages. Subset fonts to the characters you use and cut the number of weights. Split the JavaScript so the initial load carries only what the first screen needs. Defer anything below the fold. Then look at what is arriving from third parties, which is often a third of the weight and almost never budgeted for by anyone.
Compression is assumed throughout, not a lever you get to count twice: the figures here are transfer sizes over the wire, so text assets should already be compressed before you measure them. If you are pasting uncompressed sizes into the fields, the budget will look far worse than reality. The image compressor and the CSS minifier both run in the browser if you want to see what a specific file shrinks to.
Holding a budget
A budget only works if something checks it without being asked. Wire the numbers into your build so that exceeding them fails or at least warns, and record the sizes over time — the failure mode is never one big regression, it is fifteen small ones nobody objected to individually. Review the split when the page changes shape rather than defending a number set two years ago for a different design.
Neighbouring pieces on this site: the heading structure checker for the other half of a page audit, the meta tag generator for what a link preview needs, and the redirect rule generator if the round trip count above is high because requests are bouncing before they land.
Questions people ask
Is this measuring my page?
No, and it cannot. Nothing is fetched — every figure comes from the numbers you typed. It answers a design question: given these assumptions, how much can the page weigh. To find out what a real page weighs, open the network panel in your dev tools with the cache disabled and read the transfer column, then paste those totals in here.
What target time should I pick?
Pick it from your own data rather than from a round number. Look at what your slowest tenth of real visitors currently experience and set a target that would be a genuine improvement for them. A target chosen because it sounds good produces a budget nobody believes, and a budget nobody believes is not enforced.
Why does the model ignore parallel downloads?
Because adding them would make the output look more precise without making it more accurate. Real loading involves connection ramp-up, prioritisation, main-thread blocking and dependencies between resources, and a simple total-bytes-over-bandwidth figure is a defensible upper bound rather than a bad simulation. The moment you need more precision than that, you need a real trace, not a better formula.
Do the Core Web Vitals thresholds tell me what to aim for?
They are the metrics worth understanding — how fast the main content renders, how quickly the page responds to input, how much it shifts while loading — but the specific pass thresholds are published by Google and have been revised more than once, so quoting a number from memory is how people end up optimising against last year's definition. Read the current documentation, and note that none of those metrics is measured in bytes.
Should the budget cover third-party scripts?
Yes, and this is the part that gets negotiated away. Tags, analytics, chat widgets and embeds routinely account for a large share of page weight and main-thread time, and they are the assets you have the least control over. Give them a line in the budget from the start, because a budget that only covers code your team wrote will be met while the page gets slower.