349 milliseconds is a long time to stare at a robot.
Both models get the text at the same instant. ribt's real audio starts at 17.6 ms. Kokoro, the model it learned the voice from, makes its first sound at 349. Drawn to scale, in real time.
What just happened
Four small models turned that sentence into sound. Each figure below is what they produced for the line in the box, and they all follow the same playhead.
Letters become sounds
English spelling is a mess, so the first step is a dictionary and a tagger. lilt reads the sentence the way misaki does, picks the right "read" for the tense, spells out "$42.50", and guesses words it has never seen. It runs on the CPU in under a millisecond.
Every sound gets a length and a pitch
The encoder reads the phonemes and decides how long to hold each one, in frames of 10.7 ms, and where the voice should rise and fall. Stressed vowels stretch. Questions climb at the end.
One step of flow paints the sound
The decoder sketches a spectrogram: 100 frequency bands, one column per frame. A flow model then adds the fine detail in a single step, where its teacher needed sixteen. Low notes sit at the bottom; the bright bands are the harmonics of the voice.
The vocoder turns the picture into air
A spectrogram has no phase, so it can't be played. The vocoder fills that in and writes 24,000 samples a second. It is Vocos, imported weight for weight into tang.
The numbers
M4 Max, batch 1, warm. Kokoro's numbers use the same machine.
| ribt | Kokoro af_heart | |
|---|---|---|
| First audio | 17.6 ms | 349 ms |
| Real-time factor | 0.027 | 0.093 |
| UTMOS, WER-50 | 4.39 | 4.52 |
| Word error rate, WER-50 | 2.71% | 2.61% |
| Word error rate, held-out 50 | 1.73% | 1.73% |
| Speaker similarity | 0.987 | 0.990 |
Kokoro still sounds a little better, by about 0.13 UTMOS. ribt gets close at a twentieth of the first-audio latency. Kokoro ran with KPipeline on CPU, non-streaming; MPS was slower. Word error from Whisper small.en, UTMOS from utmos22_strong, similarity is WavLM-SV cosine to 121 held af_heart clips. In the browser, ribt matches the native build to within 6e-5 on the same weights.
Where the voice comes from
ribt's voice is distilled from Kokoro-82M's af_heart voice (Apache-2.0). We rendered 135 hours of af_heart and trained our own models on it. No Kokoro weights are in ribt. Kokoro's own training data included synthetic audio from closed commercial TTS providers, so the voice inherits that lineage.
The vocoder is Vocos by Hubert Siuzdak (MIT), trained on LibriTTS. lilt is a port of hexgrad's misaki (Apache-2.0). Text for the training renders came from LibriTTS-R transcripts and templated conversational lines.
Get it
Code at muni-works/ribt. Weights at municipal-robotics/ribt. Builds for Metal, CUDA, wgpu, OpenCL, plain C, and WebGPU.
# macOS (Metal)
$ cargo build --release
# Linux: cuda, wgpu, opencl or cpu
$ cargo build --release --no-default-features --features cuda
# the browser build on this page
$ web/build.sh