Package Exports
- browservec
Readme
BrowserVec
In-browser WebGPU vector store with custom WGSL kernels, for fast offline / in-session retrieval — embeddings, similarity search, and persistence, all client-side, no server round-trip.
Contents: Status · Install · Quick start · Core API · Features · Benchmarks · Docs · Contributing · License
Status
M1–M5 complete, M6 mostly complete (encryption, CPU/WASM fallback done; cross-device tuning in progress). In short: flat brute-force + IVF approximate search, fp32/int8/int4/1-bit quantization (and every combination of the two), OPFS/IndexedDB persistence with optional AES-256-GCM encryption, an on-device text embedder, Worker-offloaded ingest, and a WASM-SIMD CPU fallback for devices without WebGPU. See CHANGELOG.md for the release history and Not yet here below for open milestone work.
Install
npm install browservecimport { BrowserVec } from 'browservec';Requires a browser with WebGPU for the GPU-accelerated path; falls back to a WASM-SIMD/scalar CPU path (exact fp32 flat search) where WebGPU is unavailable — see CPU fallback.
Quick start
npm install
npm run dev # open the printed URL → demo/index.html (needs a WebGPU browser)The demo builds a random corpus, runs a GPU top-k query, and checks recall against a CPU brute-force reference — exercising the M1 exit criterion (exact top-k correct vs. reference).
Core API
import { BrowserVec } from 'browservec';
BrowserVec.isSupported(); // { webgpu, opfs, wasm }
const db = await BrowserVec.create({ dimension: 768, metric: 'cosine' });
await db.addBatch([
{ id: 'a', vector: vecA, metadata: { lang: 'en' } },
{ id: 'b', vector: vecB },
]);
const hits = await db.query(queryVec, { k: 5 });
// → [{ id, score, metadata? }, ...] (higher score = closer)
db.get('a'); // → { id, vector, metadata? } | null
db.delete('a'); // tombstone by id → true/false (compacted on save)
await db.update({ id: 'a', vector: v2 }); // replace/upsert a vector
await db.compact(); // physically drop tombstones (no reload)
db.stats(); // { count, deleted?, dimension, metric, device, lastQueryMs, persist? }
db.destroy(); // free GPU resourcesFull method/type reference: docs/api-reference.md.
Features
Each links to a short guide with runnable code:
| Feature | What it does |
|---|---|
| Deleting vectors | Tombstone-based delete/update/compact — cheap deletes, GPU memory reclaimed on compact or reload. |
| Persistence | Versioned binary snapshots to OPFS (or IndexedDB), auto-load on create(), export/import as a Blob. |
| Encryption at rest | AES-256-GCM + PBKDF2 passphrase envelope for persisted/exported snapshots. |
| Quantization (TurboQuant) | int8/int4/1-bit codes via randomized Hadamard rotation + exact fp32 re-rank — ~4×/8×/32× less memory. |
| Approximate search (IVF) | GPU-assisted k-means clustering; queries scan only the nearest nprobe clusters. Combines with quantization for the ~1M-row path. |
| Text retrieval / embedder | addText/queryText via a zero-dep hashing embedder or an optional real semantic model (transformers.js). |
| Worker ingest offload | Rotate+quantize and IVF k-means mean-updates run off the main thread so ingest doesn't freeze the UI. |
| Corpus chunking | Corpus spreads across multiple GPU buffers once it would exceed the device's per-buffer limit — transparent, same results. |
| GPU top-k | Top-k reduction runs on the GPU past 4k rows, so only a short candidate list is read back per query. |
| CPU fallback | Exact WASM-SIMD flat scan when WebGPU is unavailable — same results, bit-identical to the GPU path. |
Benchmarks
Numbers below are from the demo's M6 device report tool (fixed-seed 20k×384
corpus, recall@10 against an exact fp32 reference). This is a small, growing
device matrix, not an exhaustive one — generate your own with the same tool
(npm run dev → demo → M6 device report) or the interactive
perf-benchmark example, which sweeps corpus
size/dimension/index type directly in the browser.
Chrome 149 / macOS, Apple M-series (Metal-3) — WebGPU, OPFS, WASM-SIMD, and
Worker offload all available; maxStorageBufferBindingSize = 4 GiB.
| Config | recall@10 | Query latency |
|---|---|---|
| flat fp32 | 1.000 | 1.71 ms/q |
| flat int8 | 1.000 | 1.52 ms/q |
| flat int4 | 1.000 | 1.69 ms/q |
| flat 1-bit | 1.000 | 2.45 ms/q |
| IVF fp32 | 1.000 | 0.51 ms/q |
| IVF int8 | 1.000 | 0.63 ms/q |
| IVF int4 | 1.000 | 0.93 ms/q |
| IVF 1-bit | 1.000 | 1.11 ms/q |
| CPU fallback (WASM-SIMD), 8k rows | — | 1.76 ms/q |
Chrome (CriOS 149) / iPhone 15, iOS 26.5 — no WebGPU (iOS third-party browsers are WebKit under the hood, so WebGPU isn't exposed), no OPFS (IndexedDB is used instead), WASM-SIMD and Worker both available.
| Config | Query latency |
|---|---|
| CPU fallback (WASM-SIMD), 8k rows | 0.40 ms/q |
Takeaways so far (see docs/internals.md for the kernel detail behind these)
- Sub-byte quantization is a memory lever, not a speed lever at this scale.
The quantized kernels are ALU-bound (manual nibble/sign unpack costs more than
a plain fp32
vec4load), so query time actually rises as bit-width shrinks at 20k rows (fp32 ≈ int8 < int4 < 1-bit). The payoff is memory (int8 ~4×, int4 ~8×, 1-bit ~32× smaller) and, at much larger corpora, bandwidth — tighter codes only start winning on throughput once the scan is bandwidth-bound rather than ALU-bound. maxStorageBufferBindingSizevaries a lot by GPU — 4 GiB on Apple Metal vs. a much more conservative default on many other adapters. Corpus chunking (§NFR-10) triggers off the device's actual reported limit, so this needs no configuration — but where the chunking crossover happens is device-dependent.- iOS is CPU-fallback-only today: no WebGPU means quantization/IVF are
unavailable and only exact fp32-flat search runs, over IndexedDB persistence
(no OPFS on iOS). Pass
fallback: 'wasm'explicitly when targeting iOS Chrome or Safari, orBrowserVec.create()will throw.
Still missing from the matrix: Android Chrome (has WebGPU — a real gap), Windows + NVIDIA/AMD (likely a much smaller buffer-size cap, which would actually exercise chunking), and desktop Firefox/Safari. Contributions of a device-report JSON block from any of these are welcome — see Contributing.
Not yet here
- M6 (in progress) — exact fp32-flat CPU fallback + WASM-SIMD kernel + encryption are done. Cross-browser/mobile tuning is underway via the demo's M6 device report button: a fixed-seed capability probe + full config matrix (fp32/int8/int4/1-bit × flat/IVF, recall + latency + memory, plus WebGPU adapter limits and OPFS/WASM-SIMD/Worker support) that emits one paste-back JSON block per device, feeding the Benchmarks table above.
Everything else (M1–M5, plus M6's other pieces) is done — see CHANGELOG.md for what shipped in each release.
Docs & further reading
- docs/ — architecture overview, full API reference, and per-subsystem internals (quantization codec, IVF/k-means, persistence format, Worker offload, CPU fallback) for contributors.
- docs/architecture.md — includes the file↔spec
mapping table (which source file implements which
REQUIREMENTS.mdsection). - REQUIREMENTS.md — the original design spec.
- CHANGELOG.md — release history.
Contributing
Issues and PRs are welcome. Before opening a PR, run:
npm run typecheck
npm run buildThere's no automated test suite yet — the demo (npm run dev) exercises the
GPU vs. CPU-reference recall check described above; changes touching kernels
or indexes should be verified there before submitting.
License
MIT © Sharma SK