A query encoder as a 120 MiB file.
In the browser. Open source.

We take an embedding model, keep its document index exactly as it is, and compile the query side into a file a tenth of the size that answers in the browser in about 100 ms. Every number below was measured on the model's own index and can be reproduced from the public repository.

What we achieved

Five results that hold, each with its number, its limits and a link to the evidence. Click a number for the context.

98.2 %

SciFact test split, nDCG@10 against the model's own fp32 index (0.7559): 120 MiB at 2.1 bits per weight 98.2 %, 105 MiB at 1.8 bits 96.5 %, 91 MiB at 1.6 bits 93.6 % (torch simulation of the container, synthetic-query calibration, 4-d codebooks per 256-column block, input-side structured rotation). The browser reproduces the simulation: 0.7375 vs 0.7380 on the 300 test queries, per-query cosine to the reference 1.00000. The released SciDocs containers of the same recipe (127 / 113 MiB) hold 94.7 / 91.3 % and were verified in the browser to ±0.001; the SciFact files are on Hugging Face as well. Below ~180 MiB llama.cpp has no format at all; BitNet-270m gets to 140 MiB only by training the model ternary.

of the original retrieval quality from a 120 MiB, 2.1-bit vector-quantised harrier-0.6b client with no retraining: above the trained BitNet-270m (140 MiB, 97.0 %) and below any file llama.cpp can produce (178 MiB, 93.9 %). Verified in the browser.
4.3×

Idle laptop, Intel integrated GPU, headless Chrome, 300 SciFact queries: our runtime p50 103 ms / p95 113 ms, load 2.4 s, peak process memory 1 592 MiB; llama.cpp in wllama (WebGPU) p50 444 ms / p95 754 ms, load 4.2 s, 2 080 MiB, at +0.011 nDCG@10 for the larger file. The runtime decodes the codebook weights once per query, runs the 28 blocks of the encoder in 17 dispatches per block and needs no WebAssembly. One device, one model, one corpus; discrete GPUs and phones are not measured yet. Runtime: client/vqweb in the repository, Apache-2.0.

faster than llama.cpp's WebGPU path on the same integrated GPU: 103 ms per query for the 119 MiB vector-quantised client against 444 ms for the 235 MiB Q3_K file, in our own open-source WebGPU runtime.
99.2 %

bge-m3 Q4_K_M with a 4-bit token table: cosine to the fp32 query vector 0.990, top-10 overlap 0.864; the 321 MiB Q3_K file keeps 98.3 %. Qwen3-Embedding-0.6B: 98.7 % at 340 MiB, 99.5 % at 385 MiB. The same Czech-calibrated files keep 99.6–100 % on SciFact. What made it work: at low bit widths the language of the calibration text matters more than its domain (+0.09 nDCG@10 for Czech over English text at 2.6 bits, domain another +0.01–0.02), and tolerance to compression is a property of the checkpoint (one 0.6B model holds 99 % at 3.4 bits, another of the same architecture fails below 4.5). Next Czech step, measured but not yet a file: a 3.6-bit vector-quantised Qwen3-Embedding-0.6B keeps 98.0 % of fp32 (cosine 0.945) on the public WebFAQ-cs benchmark of 7 231 human-judged questions in simulation, with two-dimensional codebooks beating four-dimensional ones per bit; the token table, the kernel and the browser check are the remaining steps.

on a Czech supreme-court index of 55 071 segments with a 355 MiB bge-m3 client calibrated on Czech text; Qwen3-Embedding-0.6B 98.7 % at 340 MiB. Both released, both English quality intact.
+0.010

The prompt in front of every query is the same 19 tokens, so the full-precision model computes its keys and values once and the compressed model only reads the user's words. Calibrating every block with the exact prompt in place gives +0.013 / +0.009 / +0.008 nDCG@10 over the best query-calibrated client of the same seed at 1.8 bits on SciFact (cosine to fp32 0.90 → 0.92) and +0.007 / +0.016 / +0.010 on the Czech legal index; nothing at 2.1 or 1.6 bits and nothing on SciDocs or NFCorpus, so the released files stay as they are and the recipe is an option in the runtime. What the two winning corpora share: calibration queries shaped like the real ones. Pre-registered 2026-09-10/11, scored against the prediction, published with the negative arms.

nDCG@10 at 1.8 bits from a method of ours: ship the query prompt's keys and values in full precision (2 MB) and calibrate the compressed model with them in place. Three seeds on SciFact and on the Czech legal index, every paired interval above zero.
Apache-2.0

Repository: https://github.com/rosecky/embedding-quantization-public. It carries the data layer and metrics, the recipe to verify any client against your own index (import, synthetic queries, quantise, paired bootstrap), the GPTQ exporter onto the llama.cpp K-quant grids and the llama-quantize recipes of the released files, the vector-quantisation quantiser and compiler, the torch reference, the WebGPU runtime with its verification ladder (bit-exact dequantisation, per-op activations, end-to-end browser check), and the demo. Method: every run is pre-registered with a prediction and a kill rule, comparisons change one thing at a time, differences are paired bootstrap intervals over queries (10 000 draws) read against calibration-draw variance (~0.01) and between-machine variation (±0.006). The report states which claims held, which failed and which are ties.

for the whole pipeline since 2026-09-16: the quantiser, the .vqw container compiler, the structured rotation, the WebGPU runtime, the verification recipe and 11 released client files on Hugging Face. Nothing behind the numbers is closed.

Where the smallest files stand

client (SciFact, harrier-0.6b)MiB% of fp32 nDCG@10what it is
harrier-0.6b fp16, the server model1 143100 %reference
llama.cpp Q3_K with imatrix23598.9 %the free baseline; our GPTQ export ties it, we say so
llama.cpp IQ2_XS, its smallest format17893.9 %below this size llama.cpp has nothing
BitNet-270m, ternary, trained (Microsoft)14097.0 %the strongest small competitor; needs training
thinletter VQ 2.1 bits per weight12098.2 %ours: post-training, no retraining, open WebGPU runtime
thinletter VQ 1.8 bits per weight10596.5 %ours
thinletter VQ 1.6 bits per weight9193.6 %ours, the smallest working client

nDCG@10 against the model's own fp32 index, SciFact test split (300 queries, fp32 0.7559). Our rows are torch simulations of the container with synthetic-query calibration; the 2.1- and 1.8-bit SciFact files are on Hugging Face and the browser reproduces the simulation to ±0.001; the released SciDocs files of the same recipe (127 / 113 MiB) hold 94.7 / 91.3 %. BitNet-270m and the llama.cpp files are measured as files.

What we learned along the way

Measured the same way and kept on the page: the baseline everyone gets for free, an idea that did not work, and a cliff.

96–101 %

SciFact 98.9 %, NFCorpus 99.3 %, ArguAna 101 %, SciDocs 99.2 % (microsoft/harrier-oss-v1-0.6b, GPTQ on the llama.cpp Q3_K grid, generic English calibration). At this bit width llama.cpp's own quantiser reaches the same quality with the same text; at ~200 MiB our export is not a better file, and the report says so. The value is the verified pipeline and the calibration map, not the file.

of full-precision nDCG@10 kept by the 3.4-bit harrier-0.6b client on four English corpora, 235 MiB instead of 1 143 MiB. Not our contribution: llama.cpp's own quantiser gets there too.
−0.008

The idea: give the neurons the corpus uses most a larger codebook and the rest a smaller one at the same average rate. Three rotation seeds, matched random control: importance minus random −0.0085 cosine to fp32, nDCG@10 within noise. Side finding: two quantisations that differ only in which rows share a codebook differ by up to 0.03 cosine, so on this grid an effect under 0.01 needs three seeds or a paired construction. Pre-registered and closed 2026-09-10.

cosine lost when a 1.75-bit vector-quantised client spends its codebook budget by neuron importance instead of uniformly; a random split does the same. Closed.
65 → 81 %

Qwen3-Embedding-0.6B is an order of magnitude more fragile under compression than harrier or jina of the same architecture: every file at or below 3 bits fails on Czech, the released client is a 4.9-bit K-quant (340 MiB). The 2.1-bit vector-quantised point gave 65.6 % of fp32 on the laptop (torch 2.6, cu124) and 80.6 % on a rented box (torch 2.11, cu128) from the identical configuration; the 3.1-bit point 96.3 % vs 96.5 %. Read numbers below ~85 % for this model as a regime, not a value. Measured 2026-09-15/16 on the public WebFAQ-cs benchmark.

for the same 2.1-bit quantisation of Qwen3-Embedding-0.6B on Czech, same seed, two machines: at that rate this model sits on a cliff and the number is not a measurement. At 3.1 bits the two machines agree to 0.002.
Released query clients
clientbase model · licenceMiBquality vs the model's own fp32 index
harrier-0.6b Q3_K, generic English calibrationmicrosoft/harrier-oss-v1-0.6b · MIT235SciFact 98.9 % · NFCorpus 99.3 % · ArguAna 101 % · SciDocs 99.2 %
harrier-0.6b Q2_K, SciDocs synthetic queriesMIT192SciDocs 94.5 %
harrier-0.6b vector-quantised 2.10 / 1.83 bpw, SciDocs and SciFact filesMIT127 / 113 · 120 / 105SciDocs 94.7 % / 91.3 % · SciFact 98.2 % / 96.5 % (open thinletter WebGPU runtime)
Qwen3-Embedding-0.6B Q4_K_M, English calibrationQwen/Qwen3-Embedding-0.6B · Apache-2.0340SciFact 100.0 % · NFCorpus 99.4 % · ArguAna 100.1 % · SciDocs 99.3 %
Qwen3-Embedding-0.6B Q4_K_M / Q5_K_M, Czech calibrationApache-2.0340 / 385Czech legal index 98.7 % / 99.5 %
bge-m3 Q4_K_M / Q3_K, Czech calibrationBAAI/bge-m3 · MIT355 / 321Czech legal index 99.2 % / 98.3 % · SciFact 99.6 % / 100.0 %

Every client is a query encoder for exactly one document encoder and its settings. Each row links to its Hugging Face repository; the model card carries compatibility, recipe and sha256.

Work with us

Pilots start with a measurement: we take your embedding index, quantise the query side, and hand you the paired numbers before anything is decided. What follows is integration — the client, the runtime and the verification in your own application.

info@thinletter.io

Jan Rosecký, founder · or open an issue on GitHub