English | 日本語
Vokra Web ランタイム(web/pkg、npm パッケージ @vokra/web)の使い方:
ブラウザ上の Whisper base ASR を
- WASM CPU パス(SIMD128 / scalar)と
- WebGPU バックエンド(
navigator.gpuを生 wasm import shim + 手書き JS glue で駆動 —wgpucrate /wasm-bindgen不使用、ランタイム npm 依存ゼロ)
の 2 経路で動かします。
scripts/build-wasm.sh pkg
npm install ./web/pkgsource package は現在 0.0.0-dev で、npm registry には未公開です。別途承認
された npm 公開が行われるまでは local package を使ってください。
モデルは同梱されません。Whisper base checkpoint をオフラインで変換します:
cargo build --release -p vokra-cli
./target/release/vokra-cli convert \
--model whisper \
--input /path/to/whisper-base/model.safetensors \
--output whisper-base.ggufwhisper-base.gguf を静的アセットとして配信してください。Web ランタイムは
これを全量メモリロードします — WASM に mmap はありません
(vokra-mmap は native 専用。ブラウザ経路は fetch → ArrayBuffer →
in-memory GGUF parser です)。
import { createSession } from "@vokra/web";
const model = await (await fetch("/models/whisper-base.gguf")).arrayBuffer();
// backend は明示選択 — Vokra は silent fallback しません:
const session = await createSession(model, { backend: "cpu" }); // または "webgpu"
const wav = await (await fetch("/audio/jfk-30s.wav")).arrayBuffer(); // 16 kHz mono PCM16
const { text, rtf } = await session.transcribe(wav);
console.log(text, `RTF ${rtf.toFixed(3)}`);
await session.close();transcribe は 16 kHz mono PCM の Float32Array も受け付けます。
| 指定 | 挙動 |
|---|---|
{ backend: "cpu" }(既定) |
WASM CPU パス。特別なヘッダ不要でどこでも動作。 |
{ backend: "webgpu" } |
cross-origin isolation と WebGPU adapter の両方が必要。欠けている場合は理由を明記したエラーで reject — 裏で CPU に落ちることはありません。CPU で動かすのは呼び出し側の明示的な "cpu" 選択です。 |
エラーメッセージには対処法(下記 §5 の COOP/COEP 配備、または WebGPU 対応ブラウザ)が明記されます。
WebGPU の readback(mapAsync)は async のみ、Vokra の推論ループは同期です。
ランタイムは推論を専用 Web Worker で回し、SharedArrayBuffer 上の
Atomics.wait で main-thread GPU proxy と同期橋渡しします。ブラウザが
SharedArrayBuffer を有効にするのは cross-origin isolated なページのみ
なので、サーバは次の 2 ヘッダを返す必要があります:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
ローカル確認用に依存ゼロのサーバを同梱しています:
scripts/build-wasm.sh pkg
node web/demo/serve.mjs # http://localhost:8788/web/demo/CPU パスはどちらのヘッダも不要です。
WASM には runtime CPU feature detection がありません — SIMD の可否は
module validation 時に確定します。そのためパッケージは 2 つの artifact
(vokra_wasm_simd128.wasm / vokra_wasm_base.wasm)を同梱し、loader が
WebAssembly.validate probe で選択します(session.meta.artifact で確認
可能)。Relaxed SIMD は不採用(Safari 部分対応のみ = 四半期監視、
relaxed fma の非決定性は parity 規律と非整合)— kernel は決定的な
mul + add のみを使います。
Whisper base(~74M params)は wasm32 線形メモリに収まるため wasm32 を
対象とします。本 WP 調査時点: Rust wasm64-unknown-unknown は Tier 3、
ブラウザ Memory64 は Chromium/Firefox 系で shipped・Safari 未対応
(メンテナの実機 spot check で追認 — 対応可否は記録であり発明しません)。
大型モデルの Web 対応は follow-up です。
WebGPU バックエンドは現状 per-op(kernel ごとに upload → dispatch →
readback)で動きます。whisper-base 規模ではこれは WASM CPU パスより
遅い見込みです — Metal backend が device 常駐化前に通ったのと同じ段階
です。デモの RTF 表示 / tools/wasm/parity.html で実測し、記録は
docs/bench-baselines/web-2026-07-15/README.md(Kill switch G の対照
数値でもあります)へ。device 常駐化は follow-up です。
| 症状 | 原因 / 対処 |
|---|---|
backend webgpu needs SharedArrayBuffer… |
COOP/COEP ヘッダ未配備 — §5。 |
no WebGPU adapter… |
WebGPU の無いブラウザ/コンテキスト — 明示的に "cpu" を選ぶか WebGPU 対応ブラウザで。 |
WAV must be 16 kHz mono PCM16 |
オフラインで変換(ffmpeg -i in.wav -ar 16000 -ac 1 -c:a pcm_s16le out.wav)するか Float32Array を渡す。 |
Node で backend: "webgpu" が失敗 |
期待どおり — Node に navigator.gpu は無く、明示的な unavailability contract です。 |