Skip to content

Voices & speed

The voice set

The Kokoro INT8 kit ships 103 voices. Every voice has an id, an index into voices.bin, a language, and a label:

PrefixLanguageExample ids
zf_Chinese, femalezf_001, zf_002, …
zm_Chinese, malezm_009, …
af_English (American), femaleaf_maple, af_sol
bf_English (British), femalebf_vale

Voice entries follow the KokoroVoiceManifest shape:

ts
interface KokoroVoiceManifest {
  id: string; // "zf_001"
  index: number; // position in voices.bin
  language: "en" | "zh";
  label: string; // human-readable label
}

Only Recommended voices are certified

Voices are marked recommended or experimental in the model manifest. Only recommended voices have passed the Beta corpus. The certified set is zf_001, zm_009, af_maple, af_sol, and bf_vale — everything else is Experimental. Chinese prosody and punctuation pauses vary noticeably between voices, so evaluate any other voice on your own content before shipping it.

Switching voices

Pass voiceId per request:

ts
const tts = createTts({
  model: kokoroZhInt8,
  voiceId: "zf_001",
  numThreads: 4,
});
await tts.load();

const chinese = await tts.synthesizeToFile({ text: "你好。" });
const english = await tts.synthesizeToFile({
  text: "Hello.",
  voiceId: "af_maple",
});

Changing the voice does not reload the model. The ONNX session is created once; a different voiceId just selects a different style embedding from voices.bin for that synthesis. Switching is cheap.

To change the default, set it on the client and keep using one voice:

ts
const tts = createTts({ model: kokoroZhInt8, voiceId: "af_maple" });

Speed

speed is a multiplier on the generated audio, valid from 0.5 to 2.0:

ts
await tts.synthesizeToFile({ text: "慢一点。", speed: 0.8 });
await tts.synthesizeToFile({ text: "Faster now.", speed: 1.4 });

Values outside the range are rejected before inference runs.

Speed is a hard range, not a hint

0.5 and 2.0 are the extremes. Beyond them the SDK throws rather than clamping silently, so validate any user-facing speed control yourself.

Tuning threads

numThreads maps to the ONNX Runtime intra-op thread count:

ts
const tts = createTts({
  model: kokoroZhInt8,
  voiceId: "zf_001",
  numThreads: 4,
});

More threads is not automatically better — on mobile, oversubscribing cores can cost more than it gains. Benchmark on your target hardware with result.elapsedMs (which excludes model load) and pick the best value.

Text length limits

LimitValue
Whole input2,000 Unicode code points
Single candidate segment300 code points
Model token budget510 tokens (508 payload)

The SDK splits long text into segments and concatenates the resulting audio. Exceeding the whole-input limit throws TEXT_TOO_LONG; a segment that cannot be split below the cap throws TEXT_SEGMENT_TOO_LONG.

Chinese numbers and dates are normalized automatically before encoding — 2024年5月1日 is read as Chinese numerals, not digits.