Workers / WebAssembly

Browser WebAssembly retrieval guide

Run retrieval and local MiniLM embedding in dedicated browser Workers with packaged WebAssembly tiers.

Published Worker packages; persistence deferredView the v0.1.0 source guide →
On this page

Install

Browser retrieval and browser embedding are independent Worker packages. They do not import or fall back to the native Node.js packages.

npm install @gungorbasa/retrievalkit-browser@0.1.0
npm install @gungorbasa/retrievalkit-browser-embedding@0.1.0

Retrieval combines flat, graph-only, and graph-scoped databases in one package. Embedding uses a separate Worker and a pinned FP32 MiniLM model.

Create the retrieval Worker

The Worker selects the SIMD128 build when supported and otherwise loads the portable WebAssembly tier.

// retrievalkit.worker.ts
import { installRetrievalKitWorker }
  from "@gungorbasa/retrievalkit-browser/worker";
import { createAdaptiveGeneratedWasmAdapter }
  from "@gungorbasa/retrievalkit-browser/adapter";

installRetrievalKitWorker(createAdaptiveGeneratedWasmAdapter({
  portable: async () => {
    const wasm = await import(
      "@gungorbasa/retrievalkit-browser/wasm/portable"
    );
    await wasm.default();
    return wasm;
  },
  simd128: async () => {
    const wasm = await import(
      "@gungorbasa/retrievalkit-browser/wasm/simd128"
    );
    await wasm.default();
    return wasm;
  },
}));

Create the embedding Worker

// embedding.worker.ts
import { installBrowserEmbeddingWorker }
  from "@gungorbasa/retrievalkit-browser-embedding/worker";

installBrowserEmbeddingWorker();

The embedding Worker owns model acquisition, verification, tokenization, ONNX session creation, warmup, and inference. execution: "auto" prefers WebGPU and falls back to deterministic WASM.

Create the clients

import { RetrievalKitBrowser }
  from "@gungorbasa/retrievalkit-browser";
import { BrowserEmbedder }
  from "@gungorbasa/retrievalkit-browser-embedding";

const kit = await RetrievalKitBrowser.create({
  worker: () => new Worker(
    new URL("./retrievalkit.worker.js", import.meta.url),
    { type: "module" },
  ),
});

const embedder = await BrowserEmbedder.load({
  worker: () => new Worker(
    new URL("./embedding.worker.js", import.meta.url),
    { type: "module" },
  ),
  execution: "auto",
});

Model acquisition happens during load() or prefetch(). Calls to embed() and embedBatch() never download model artifacts.

const builder = kit.retrievalDatabase({
  corpusId: "notes",
  metric: "cosine",
  encoding: "f32",
});

const documentEmbedding = await embedder.embed("A local-first search note");
await builder.add([{
  id: "note-1",
  text: "A local-first search note",
  embedding: documentEmbedding,
}]);

const database = await builder.build();
const queryEmbedding = await embedder.embed("local search");
const hits = await database.search({
  mode: "hybrid",
  text: "local search",
  embedding: queryEmbedding,
  alpha: 0.6,
});

Use kit.graphDatabase() for graph-only work or kit.graphRetrievalDatabase() when graph selection should scope the same ranker.

Close and deploy

await database.close();
await embedder.close();
kit.close();

Browser databases are Worker-owned and in memory. Persistence and threaded WebAssembly are not claimed in v0.1.0. Bundle both Worker entries as static module assets and permit Worker creation and WebAssembly compilation in the application content security policy.

The complete Apollo 11 demo has run successfully in Chromium, Firefox, and Safari on desktop. WebAssembly support alone is not enough: local answer generation also requires WebGPU and sufficient GPU memory. These successful runs are not a promise for every browser, operating system, or device. Physical mobile browsers remain unqualified.

Choose the database boundary

Three query paths

Compare the same job in TypeScript. These are separate APIs, not modes hidden behind one query.

01

Hybrid vector + BM25

RetrievalDatabase Worker client

Rank the whole in-memory corpus with exact vector similarity and BM25.

TypeScript
const hits = await retrievalDatabase.search({
  mode: "hybrid",
  text: "What did we decide about offline sync?",
  embedding: queryEmbedding,
  alpha: 0.6,
  limit: 5
});
02

Graph query alone

GraphDatabase Worker client

Select records by relationship. No embedding or retrieval ranker is involved.

TypeScript
const selection = await graphDatabase.graph.query({
  seed: {
    kind: "nodes",
    nodes: [{
      kind: "record",
      nodeType: "Notebook",
      recordId: "product-research"
    }]
  },
  traverse: [{ relationship: "references" }]
});
03

Graph + vector + BM25

GraphRetrievalDatabase Worker client

Select candidates with the graph, then rank only that scope with vector + BM25.

TypeScript
const selection = await graphRetrievalDatabase.graph.query({
  seed: {
    kind: "nodes",
    nodes: [{
      kind: "record",
      nodeType: "Project",
      recordId: "mobile-app"
    }]
  },
  traverse: [{ relationship: "contains" }]
});

const hits = await graphRetrievalDatabase.retrieval.search({
  mode: "hybrid",
  text: "Why is cold start slow on Android?",
  embedding: queryEmbedding,
  alpha: 0.6,
  within: selection,
  limit: 5
});