browser-ML shippability study
The model is a third the size
the blog posts say it is.
Ten transformers.js configurations, measured end to end in a real browser: true transfer bytes, cold load, execution-provider readback, peak heap, cache behaviour.
Advertised is the fp32 model.onnx size from the Hugging Face tree API —
the figure casually quoted for a model. Measured is what Chrome actually pulled down,
because transformers.js ships a quantised file by default.
Four results that are not what you would guess.
Why n = 20 on one machine is still worth reading
It is not a population claim and nothing here should be generalised to your users'
hardware. What one machine measured carefully does establish is the
method: which numbers are knowable from a browser at all, which ones the
platform actively hides, and where the commonly cited figure and the real one
diverge by a factor of three. Every row is reproducible with
npm run measure.
All ten models, both execution providers.
| model | transfer | vs advertised | cold wasm | cold webgpu | warm | peak heap | revisit |
|---|
Raw study, every field: results.json. Cold is a fresh incognito context; warm is a second load in the same context.
Run the same instrumentation here.
idle.
Nothing downloads until you do — this page ships no model weights.
Why this page's byte totals are a floor, not a total
Byte counts here come from the public Resource Timing API, not from Chrome
DevTools Protocol. Confirmed live against huggingface.co on 2026-08-15: it sends no
Timing-Allow-Origin, so the browser zeroes out
both transferSize and
decodedBodySize for those cross-origin requests — not
just the transfer size, as you might expect.
When a request's real duration proves bytes moved but both size fields read zero, this page reports size unknown (opaque) and excludes it from the total rather than counting it as zero. The study above was measured via CDP, which has no such blind spot — treat it as authoritative over this panel for exact figures.
Execution-provider readback has no such limitation: it counts real
GPUQueue.submit() calls rather than trusting the device
string you asked for, and works identically here and in the harness.