defnotmid

Projects / 04

ottis

Text to speech with word timings, in Odin. Kyutai's Pocket TTS on the CPU, and a time for every word it says.

  • Odin
  • MIT
  • Pocket TTS · 100M
  • CPU only
  • Linux · macOS · Windows
Source on GitHub ↗
28 seconds of ottis: every line is spoken by ottis in four of its voices, and Ottis's mouth, the captions and the word table all follow the word times it returns. Rendered with onimite, whose say() runs ottis.

By the numbers

faster than real time
~4×
4.96 s of speech in 1.19 s on a Ryzen 9 9900X3D, 4 threads
from word cue to the voice
~30 ms
median +10 ms against the onset after a pause
binary, nothing to install
678 KB
the runtime, model and aligner download on first use
of real time to align
2%
0.47 s for 21.4 s of audio

Speed from ottis say (2026-10-12, voice george, seed 12); alignment numbers from PERFORMANCE_TRADEOFFS.md, measured against pause onsets and Whisper medium.en word timestamps.

What's in it

  1. 01Pocket TTS, no Python

    Kyutai's 100M-parameter Pocket TTS runs through ONNX Runtime's C API, loaded at run time, with a SentencePiece tokenizer and the chunking and generation loop written in Odin.

  2. 02A time for every word

    Each chunk's text is force-aligned to its audio by a wav2vec2 CTC recognizer at 20 ms frames, so cues land where the word is actually spoken, not where the model's attention puts it.

  3. 03Honest about numbers

    Words the aligner can't spell, like "$3.50" said as "three fifty", share the span between their neighbours and are marked estimated.

  4. 04Same seed, same samples

    The only randomness is the flow head's noise from ottis's own seeded generator, and ONNX Runtime's thread count is fixed, so a seed reproduces its audio bit for bit; say prints the SHA-256.

  5. 05Downloads, pinned

    ONNX Runtime 1.30.0, the english_2026-04 bundle, the voice and the aligner are fetched on first use and checked against SHA-256 pins, over libcurl on Linux and macOS and WinHTTP on Windows.

  6. 06Whisper-style JSON

    The -words output is the shape Whisper writes, so caption tools read it as is; onimite's captions{} does.

  7. 07A library too

    After engine_init the engine is read-only: synthesize runs on several threads at once and gives the same samples as alone. pocket.VERSION changes whenever the output would.

  8. 08Ottis, who lip-syncs

    The mascot is an SVG drawn by tools/mascot with eyes, four mouths and sound waves as layers a script switches by id, so Ottis can mouth ottis's own word cues.

Credit and licences

ottis is MIT-licensed. The model is Pocket TTS by Kyutai (CC-BY-4.0), so audio made with ottis should credit Pocket TTS by Kyutai (CC-BY-4.0). Two voices, cosette and jean, come from recordings licensed for non-commercial use only.

Next on the list (PLAN.md): a native Odin port of the model, which would drop ONNX Runtime.

Use it

odin build . -o:speed -out:build/ottis
build/ottis say "The lamp cost $3.50." -o lamp.wav -words lamp.json
build/ottis say -f script.txt -voice marius -seed 3 -o scene.wav -words scene.json
build/ottis voices        # the 27 voices
build/ottis fetch -verify # download up front, or re-hash what is there

The family