Measured across 11 popular models · reproducible

Up to 56% of every browser LLM you download
is a table it barely reads.

Small language models now run entirely in your browser. But a big slice of every download is the token-embedding table — and a real conversation touches under 0.3% of its rows. We measured it across the most popular models. Then we built the fix.

38.5%average of the download that is the embedding table (11 models)
<0.3%of its rows a typical conversation actually reads (Heaps' law, β≈0.75)
0quality lost — the fix is byte-identical, verified against the model's own weights

The waste leaderboard

Share of the total model download (q4f16 ONNX, browser build) that is the token-embedding table. Bigger bar = more of your download is a lookup table you barely use.

Measured from each model's config.json (vocab × hidden × dtype) and ONNX file sizes; validated byte-exact against three models loaded in full. Reproduce with research/survey/leaderboard.py.

Audit any model yourself

Paste a Hugging Face ONNX repo (or pick one). Your browser fetches its config.json + file sizes directly from HF and computes the embedding share live — nothing hits our servers.

Why this happens — and why it's avoidable

Language is heavy-tailed (Zipf) and its vocabulary grows sublinearly with length (Heaps' law — we measure β≈0.75, R²=0.996 on 714k real tokens). So a whole conversation only ever needs a few hundred of a model's 100k–260k embedding rows. Yet every serving stack — Transformers.js, onnxruntime-web, llama.cpp, Google's own LiteRT-LM — downloads or memory-maps the entire table before the first token.

SparseGemma fetches only the byte ranges for the tokens actually used (HTTP Range requests), caches them across sessions, and — using the draft model already run for speculative decoding as a free prefetch oracle — hides the latency. On the hardest case (Gemma-4-E2B's 1.59 GB table) the live demo fetches hundreds of KB instead of gigabytes, with byte-identical output.

Honest caveat (we checked so you don't have to be surprised): in most of these models the embedding is weight-tied to the output head, so exhaustive full-vocabulary argmax still needs the whole table for the output projection. The clean win is on the input side universally, and on the output side too for separate-head exports or draft-guided / top-k generation. Full trace + math in the research folder — nothing swept under the rug.

See it, read it, build on it

▶ Live demo (WebGPU) GitHub — code + proofs Upstream RFC

Open source (MIT). The technique should be free and everywhere — that's the point.