Skip to content
runonweb

Text-to-Speech

Voice

Synthesize speech from text on-device. Three sizes: Kokoro 82M for English, Spanish and French, Supertonic 2 for English, Korean, Spanish, Portuguese and French at 44.1 kHz, or KittenTTS nano at ~28 MB. Stream PCM as it is generated, or download a WAV.

Try Voice

Runs in this tab · nothing uploaded

Pick a size and a voice, then a sample or type your own

Model size

Try with

What it does

Natural speech, generated locally.

  • Default small is Kokoro 82M (StyleTTS 2): American, British, Spanish and French. Runs on Transformers.js directly, no kokoro-js. fp32 on WebGPU (fp16 gives NaN on the v4 runtime), q8 on WASM.
  • Pass size: "multi" for Supertonic 2 (Supertone, 2026): one ~262 MB model for English, Korean, Spanish, Portuguese and French at 44.1 kHz, 10 preset voices (st_f1st_m5). Pass language per call.
  • Supertonic 2 is OpenRAIL-M: open weights with use restrictions (no impersonation, no deception, no illegal use). The rest of the TTS sizes are Apache-2.0.
  • Pass size: "tiny" for KittenTTS nano (~28 MB, 8 English voices, WASM).
  • Audio streams sentence by sentence via speakStream. WebGPU is several times faster on small and multi; WASM works everywhere.
  • Pass voice to pick a speaker (af_heart / bella / st_f1). Kokoro Spanish and French download a local eSpeak-NG WASM (~18 MB) on first use. Nothing is uploaded.

Specifications

Package
runonweb/tts
License
Apache-2.0
Download
~326 MB / ~92 MB (small)multi ~262 MBtiny ~28 MB
Quantization
fp32 on WebGPUq8 on WASM (multi is fp32, tiny is WASM)
Backend
WebGPUWASM fallback
Input
Text

Get started with runonweb/tts

pnpm add runonweb
runonweb/ttsts
import { TextToSpeech } from 'runonweb/tts'

const tts = new TextToSpeech({ size: 'small', voice: 'af_heart' })
await tts.load()

for await (const chunk of tts.speakStream('Hello from the browser')) {
  // chunk.audio is 24 kHz PCM. Play as it arrives
}

const wav = await tts.speakToBlob('Hola mundo', { voice: 'ef_dora' })
new Audio(URL.createObjectURL(wav)).play()

// Supertonic 2: one model, five languages, 44.1 kHz
const multi = new TextToSpeech({ size: 'multi', voice: 'st_f1' })
const pt = await multi.speakToBlob('Olá do navegador', { language: 'pt' })

tts.dispose()

Same shape everywhere: construct, load(), run, dispose(). Pass onProgressto show download progress on first use.

Questions about Voice

Does Voice run on device?

Yes. Weights download once and stay in the browser. Nothing is sent to a server.

What does Voice cost?

Nothing. runonweb is free and MIT-licensed, with no device limits, tokens or sign-in.

Which browsers support Voice?

Chromium browsers use WebGPU. Firefox and Safari fall back to WebAssembly automatically, slower but identical output.

Can I use a different model with runonweb/tts?

Yes. See each module’s docs for the options it accepts (model, size, language…). Check the license and test both backends before shipping.