Lightning-fast semantic embeddings in a 5β7 MB WebAssembly bundle. Engine + model + tokenizer in one file. Embedding search on CPU β no API calls, no GPU. Try the live demo - search 2k docs entirely on-device.
Two tiers, same API β pick by size/quality trade (full comparison):
npm install @ternlight/base # quality tier β 7 MB wire, ~5 ms/embed
npm install @ternlight/mini # small tier β 5 MB wire, ~2.5 ms/embedimport { embed, cosineSim, similar } from '@ternlight/base';
// One primitive: string β 384-dim L2-normalized Float32Array
cosineSim(embed('reset my password'), embed('I forgot my password')); // 0.88
// Nearest-neighbor search over a corpus
similar('I want my money back', [
'Refunds: how to get your money back',
'Track the status of your delivery',
'Update your billing address',
], { topK: 2 });
// β [{ text: 'Refunds: how to get your money back', sim: 0.70 },
// { text: 'Update your billing address', sim: 0.24 }]Works in Node β₯ 18, browsers (via any bundler), Cloudflare Workers, Vercel Edge, Deno, and Bun β the package routes each environment to the right loader. Package docs: @ternlight/base Β· @ternlight/mini.
Distilled from all-MiniLM-L6 with BitNet b1.58-style quantization-aware training. Three design choices stack to fit an embedding model in a few MB:
- Ternary weights β every weight is
-1,0, or+1; inference is adds and subtracts. Quality holds because the model trains as a ternary model from the start. - One bundle β model + BERT tokenizer + engine in a single
.wasm. No postinstall step, no runtime fetch. - SIMD inference engine β hand-written Rust compiled to WASM SIMD; the add/subtract math rides CPU vector instructions.
All numbers measured on the shipped int4 builds (M-series Mac, Node/V8):
| @ternlight/mini | @ternlight/base | |
|---|---|---|
| Wire size (gzipped wasm) | 5.0 MB | 7.2 MB |
| Latency (p50 per embed) | 2.5 ms | 5.1 ms |
| Throughput (single-thread) | ~400 emb/s | ~195 emb/s |
| Spearman vs MiniLM-L6 teacher | 0.820 | 0.844 |
| Retrieval (SciFact NDCG@10) | 0.439 | 0.465 |
| Architecture | 2-layer Β· d_model=256 Β· 4 heads | 2-layer Β· d_model=384 Β· 6 heads |
| Parameters | ~9.5M | ~15.4M |
| Output | 384-dim L2-normalized | 384-dim L2-normalized |
| Max input | 128 tokens (~95 words) | 128 tokens (~95 words) |
| Quantization | ternary weights Β· int4 embeddings | ternary weights Β· int4 embeddings |
On-device embedding unlocks:
- Search-as-you-type. Results appear before the user finishes typing. Faster than any network round-trip can be.
- Privacy-sensitive apps. Queries and documents never leave the device β no data-handling agreements, no leak risk.
- Offline-first apps. Browser extensions, Obsidian plugins, desktop apps.
- Edge-runtime apps. Cloudflare Workers, Deno Deploy, Vercel Edge. Embeddings co-locate with your request handler β no separate inference service to call.
- Edge devices and IoT hardware. Raspberry Pi, single-board computers, industrial gateways, kiosks. Add/subtract math runs efficiently on ARM cores β no GPU or NPU required.
- Static sites. Jekyll, Hugo, Astro. Ship the model with the bundle; semantic search works without a backend.
ternlight/
βββ packages/ Published npm packages (pnpm workspace)
β βββ base/ @ternlight/base β quality tier (d384)
β βββ mini/ @ternlight/mini β small/fast tier (d256)
βββ engine/ Rust β Wasm inference engine
βββ training/ Python distillation + QAT pipeline, packer
βββ eval/ Engine quality + perf benchmarks
βββ docs/ Design docs
βββ models/ Model release registry pointers
βββ scripts/ Build + release orchestration
βββ .github/ CI workflows
Deeper reading: project overview Β· architecture Β· inference engine Β· model internals Β· eval methodology.
There's still tons of headroom for perf and quality improvements. Beyond stacked encoders, I'm curious about other modalities, specially generative use cases that fit the same tight constraints. JS, Rust, and ML contributors all welcome.
ternlight builds on three open-source efforts:
- BitNet b1.58 (Ma et al., Microsoft Research, 2024) β the architectural research underlying ternary weight training.
bitlinearby @schneiderkamplab β the reference PyTorch implementation of BitLinear. We use it directly during training (bitlinear==2.4.6) and the Rust inference engine mirrors its forward-pass math byte-for-byte.sentence-transformers/all-MiniLM-L6-v2β the teacher model the student is distilled from.
The Rust engine in engine/src/kernels.rs is an independent reimplementation of bitlinear's BitLinear.forward() for the WASM target; parity tests guard against drift.
MIT β see LICENSE.
Banner photo via Macaulay Library, Cornell Lab of Ornithology.
In memory of Alex Movsessian - who held software to the highest standard, and treated everyone around him with kindness.

