How to set a font byte budget for lcp with the variable-vs-static projection
- Step 1Identify the font on your LCP path — Find the font your largest above-the-fold text actually uses — that's the one whose bytes block your LCP.
- Step 2Upload the served artifact — Upload the **WOFF2 you serve** (`.woff2`), not a build-time TTF, so the byte numbers match real transfer. TTF/OTF/WOFF are also accepted but will report their own larger sizes.
- Step 3Note variable_file_bytes — This is your variable-strategy budget line — the single file you'd transfer if you ship the variable font.
- Step 4Find your weight-count row for the static budget — In `projections`, read `estimated_static_total_bytes` at the row matching the weights your critical text needs. That's the static-strategy budget line.
- Step 5Pick the lighter line as your budget — Compare the two and take the smaller. `variable_wins` tells you which it is; `delta_bytes` quantifies the saving you'd bank by choosing it.
- Step 6Ship the chosen strategy on the fast path — Whichever wins, get it off the critical path: preload it with the [Preload Tag Builder](/font-tools/preload-tag-builder), set `font-display` via [Font Display Strategy](/font-tools/font-display-strategy), and inline tiny faces with [Font to Base64](/font-tools/font-to-base64) if appropriate.
Mapping output fields to budget lines
Two strategies, two budget lines; choose the smaller for the LCP path.
| Budget line | Output field | Note |
|---|---|---|
| Variable strategy | variable_file_bytes | Single file; upload the served WOFF2 for a true number |
| Static strategy | projections[n-1].estimated_static_total_bytes | n = weights on your critical text |
| Saving from picking right | projections[n-1].delta_bytes | Magnitude of bytes you'd avoid |
| Coverage warning | glyph_count | High count -> subset before budgeting |
Worked budget for a 4-weight hero
Variable file 250 KB; per-weight estimate 100 KB. Critical text uses 4 weights.
| Strategy | Budget bytes | On LCP path? | Decision |
|---|---|---|---|
| Variable (one file) | 250 KB | Yes (preloaded) | Pick this (variable_wins true at 4) |
| Static x4 | 400 KB | Yes | 150 KB heavier — avoid |
| Static x2 (trim to 2 weights) | 200 KB | Yes | Lighter still IF design can drop to 2 weights |
| Static x1 | 100 KB | Yes | Lightest, only if one weight is truly enough |
Cookbook
Budget read-outs for real LCP scenarios. Numbers follow from the uploaded file size and the fixed 40% per-weight model — scale to your own font.
Hero text in 4 weights — variable is the budget
ExampleA homepage hero rendering four weights above the fold.
Upload: Brand-Variable.woff2 (variable_file_bytes 250000) estimated_static_per_weight_bytes: 100000 projections[3] (4 weights): estimated_static_total_bytes: 400000 variable_wins: true delta_bytes: 150000 Budget line: 250 KB (variable). Preload it.
Body-only blog — static is the budget
ExampleA reading-focused blog whose LCP element is a single-weight headline.
projections[0] (1 weight): estimated_static_total_bytes: 100000 variable_bytes: 250000 variable_wins: false delta_bytes: -150000 Budget line: 100 KB (freeze the one weight).
Glyph count flags a coverage problem
ExampleA font is heavy because it carries thousands of glyphs you don't render above the fold.
variable_file_bytes: 1200000 (1.17 MB) glyph_count: 4800 axes: 2 # 1.17 MB on the LCP path is a fail. Subset to your real charset first.
TTF uploaded by mistake inflates the budget
ExampleUploading the build-time TTF instead of the served WOFF2 overstates the variable line.
variable_file_bytes: 620000 (uncompressed TTF) # vs the 250 KB WOFF2 you actually serve. # Re-upload the served file or your budget is 2.5x too high.
Turning the row into a budget note
ExampleA one-liner you can drop in a perf ticket.
Font LCP budget: 250 KB (variable WOFF2, preloaded) Alt static x4 would be 400 KB (+150 KB) -> rejected Source: variable_wins=true at 4 weights
Edge cases and what actually happens
Every row below was probed against the live API. Some documented requirements (alphabetical axis order, numerical tuple order) are not actually enforced in practice — useful to know if you've been blaming the wrong thing for a 400.
No file uploaded
Error: Upload a variable font.The handler requires a file; it throws otherwise. Upload the font on your LCP path.
Uploaded an uncompressed TTF, not the served WOFF2
Inflated budgetvariable_file_bytes is the uploaded size verbatim. An uncompressed variable TTF can be 2-3x the WOFF2 you serve, blowing up the budget line. Always upload the served artifact.
Static total is a 40% heuristic, not measured
Heuristic onlyestimated_static_total_bytes derives from a flat 40%-of-variable per weight. Use it as a planning ceiling; confirm the chosen strategy's real bytes before sign-off.
High glyph_count font
Subset firstA large glyph_count means the file carries coverage you may not render above the fold. The tool doesn't subset; trim with the Font Subsetter before budgeting.
Font over free 5 MB limit
Rejected (tier limit)Free tier caps uploads at 5 MB. A 1 MB+ variable on the LCP path is already a perf smell; over 5 MB needs Pro.
Over the free 1,000-glyph limit
Rejected (glyph limit)Free tier enforces 1,000 glyphs. Subset to your critical charset or upgrade to Pro (65,536).
axes is 0
axes: 0No fvar table means a static file. The static-vs-variable budget split is meaningless; upload the variable master.
Break-even fixed at 3
By constructionbreakeven_static_count is always 3 (the 0.4 multiplier). Don't use it as a per-font signal; use the byte numbers at your weight count.
Options provided
IgnoredNo options are read; you always get the 1-9 projection. Choose the row for your critical-path weight count.
Non-font upload
Error: Unsupported font formatMagic-byte detection rejects anything that isn't TTF/OTF/WOFF/WOFF2 before computing the budget.
Frequently asked questions
How do I turn this into a font byte budget?
Take variable_file_bytes as the variable-strategy line and estimated_static_total_bytes at your weight-count row as the static line. Budget against the smaller of the two — that's the one to put on your LCP path.
Which file should I upload for an accurate budget?
The WOFF2 you actually serve. variable_file_bytes is the uploaded file's size as-is, so an uncompressed TTF will overstate the budget.
Are the static totals real measurements?
No — each static weight is estimated at 40% of the variable size. Treat the static line as a planning ceiling and verify real bytes for the strategy you pick.
Why does font size hurt Core Web Vitals?
Render-blocking or late-discovered fonts delay text paint, hurting LCP, and a swap can cause layout shift. Keeping font bytes minimal and preloading the right file is the lever this tool helps you size.
What if glyph_count is huge?
The font is heavy because of coverage. Subset to your real charset with the Font Subsetter before it counts against your budget.
Does it preload or set font-display for me?
No. Use the Preload Tag Builder for the preload tag and Font Display Strategy for swap behaviour.
Can I inline a small font to skip a request?
For very small faces, yes — generate the data URI with Font to Base64. Mind that base64 inflates bytes ~1.33x, so it only helps for tiny files.
What weight count should I budget for?
The number of distinct font-weight values your above-the-fold/critical text actually renders — not every weight in the design system.
Why is break-even always 3?
It's a fixed artefact of the 40% per-weight multiplier, not a per-font computation. Budget by the byte numbers, not the break-even count.
Is there a file limit?
Free tier: 5 MB and 1,000 glyphs. Pro: 50 MB and 65,536 glyphs. A font that big on the LCP path is itself a warning sign.
Does it send my font anywhere?
The computation runs in the browser and returns JSON; the font isn't uploaded to a server for this calculation.
What does the output file contain?
<stem>.size-compare.json with variable_file_bytes, glyph_count, axes, estimated_static_per_weight_bytes, breakeven_static_count, and the projections array.
Privacy first
Every JAD Font tool runs entirely in your browser using opentype.js and the wawoff2 WASM Brotli encoder. Your fonts never leave your device — verified by zero outbound network requests during processing.