tripolskypetr/piper-tts-web
Piper TTS — fully offline in the browser
Text-to-speech that runs entirely on the client: phonemization with espeak-ng (WebAssembly), synthesis with onnxruntime-web (WASM). No server calls at runtime — verified in headless Chromium with all non-local requests blocked. Ships with Russian (Ruslan) and English (Joe) medium voices; any voice from rhasspy/piper-voices can be added.
Running locally
python3 serve.py 8080 # then open http://localhost:8080/
An HTTP server is required (file:// won't work: WASM and fetch of the model are blocked). serve.py sends COOP/COEP headers so onnxruntime-web can use multithreaded WASM; without them it silently falls back to a single thread. On nginx or any other server add:
Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: require-corp
Contents
API
const eng = await Piper.createEngine("./ru_RU-ruslan-medium.onnx", "./ru_RU-ruslan-medium.onnx.json");
const pcm = await eng.speak("Привет, мир!", { lengthScale: 1.0 }); // Float32Array, 22050 Hz mono
await Piper.playPcm(pcm, eng.sampleRate);
const wav = Piper.toWav(pcm, eng.sampleRate); // Blob audio/wavLower level: Piper.getPhonemizer() → .phonemize([text], "en-us") returns [{ text, phonemes, phoneme_ids }]; eng.infer(ids, speakerId, { noiseScale, lengthScale, noiseW }).
Adding voices
Drop a *.onnx + *.onnx.json pair from rhasspy/piper-voices next to the others and add an entry to voices.json — the selector picks it up. Models load lazily on first selection and are cached for the session. The espeak language comes from cfg.espeak.voice; pass speakerId for multi-speaker models.
To shrink the bundle, keep only ort-wasm-simd-threaded.wasm in vendor/ort/ (or ort-wasm-simd.wasm if you can't set COOP/COEP) — ORT picks one file by browser capabilities and never downloads the rest. EOF cd /home/claude && rm -rf piperofflinemulti && cp -r offline piperofflinemulti && rm -f piperofflinemulti/nohup.out /mnt/user-data/outputs/piperofflinemulti.zip && zip -qr /mnt/user-data/outputs/piperofflinemulti.zip piperofflinemulti && cp offline/README.md /mnt/user-data/outputs/README.md && echo ok
