JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 86
  • Score
    100M100P100Q83582F
  • License MIT

In-browser WebGPU vector store with custom kernels for fast offline retrieval

Package Exports

  • browservec

Readme

BrowserVec

npm license

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 browservec
import { 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 resources

Full 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 vec4 load), 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.
  • maxStorageBufferBindingSize varies 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, or BrowserVec.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.md section).
  • 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 build

There'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