WordInk GitHub

Local engine

@wordink/local runs Moonshine tiny (English) in the browser through transformers.js, in a Web Worker so the page stays responsive. It uses WebGPU when the browser has it and WebAssembly otherwise. Once the model has loaded, dictation makes no network calls and audio never leaves the device. The live demo uses it.

import { createDictation } from "@wordink/core";
import { createLocalProvider } from "@wordink/local";

const local = createLocalProvider();
local.on("progress", (percent) => showProgress(percent)); // 0..100, first visit only
local.on("ready", ({ device }) => console.log("model ready on", device)); // "webgpu" | "wasm"
local.on("error", (e) => console.error(e));
void local.load(); // optional: start the download now instead of on the first press

const dictation = createDictation({ provider: local });

With the element, set it as a property: document.querySelector("wordink-mic").provider = local. Plain HTML pages need the two files served from their own origin; see the HTML quickstart.

It is a batch provider: the core hands it the whole utterance on release and it reports one final text. A model or inference failure fails that utterance with a ProviderDown error.

Size, speed and caching

WASM WebGPU
First download about 32 MB (q8 encoder and decoder, tokenizer) about 80 MB (fp32 encoder, q4 decoder)
Speed Noticeably slower on long utterances Fast

Files go into the browser's Cache API (transformers-cache), so later visits load from disk and emit no progress events. WebGPU is missing on some Linux and Android browsers, which then use WASM.

Pinning and checksums

The model is fetched from a pinned Hugging Face commit (DEFAULT_REVISION), never main. The ONNX weights are checked against pinned SHA-256 checksums (DEFAULT_SHA256) as they download: a mismatch fails the load before the file reaches the cache. The small JSON files (config, tokenizer) are covered by the revision pin only. The check needs WebCrypto, so the page must be a secure context (HTTPS or localhost).

Self-hosting

By default the model comes from huggingface.co and the onnxruntime-web runtime from cdn.jsdelivr.net. To serve everything from your own origin:

Option Default
modelBaseUrl https://huggingface.co/onnx-community/moonshine-tiny-ONNX/resolve/<revision>/ Directory holding config.json, tokenizer.json, onnx/…. Relative URLs resolve against the page
wasmPaths cdn.jsdelivr.net Directory with the onnxruntime-web runtime (ort-wasm-simd-threaded*.mjs and .wasm from onnxruntime-web/dist)
revision DEFAULT_REVISION Hub commit for the default URL. A different revision needs sha256 too
sha256 DEFAULT_SHA256 Expected SHA-256 per file, relative to modelBaseUrl

Copy the pinned revision's files (keeping the onnx/ folder) to your server and point modelBaseUrl at them; the default checksums still apply. Set wasmPaths as well, or the runtime code still comes from jsDelivr.

const local = createLocalProvider({
  modelBaseUrl: "/models/moonshine-tiny/",
  wasmPaths: "/ort/",
});

Bundlers

The provider starts its worker with new Worker(new URL("./worker.js", import.meta.url), { type: "module" }), which Vite and webpack 5 recognize. If a dev server pre-bundles dependencies and the worker fails to load, exclude the package (Vite: optimizeDeps: { exclude: ["@wordink/local"] }).