Kestrel
distilled TTS for Apple Silicon · open weightsI took Kokoro-82M apart and rebuilt it for MLX as a single-voice audiobook engine, down to about 10M active parameters from 82M. The vocoder is a per-bin complex mask over an exact-phase harmonic template. It never leaves frame rate. On an M2 MacBook with 16GB, a 163-second chapter renders in 0.239s on the student-fast preset: a real-time factor of 706, and 57x faster than stock Kokoro. That factor is measured against the roughly 168 seconds of audio the preset actually emits, because its durations drift.
Content survived the compression. ASR intelligibility sits at parity with the teacher, 5.42% WER against 5.65%, and pitch tracks to about 9Hz RMSE. The student preset is slower at 1.117s but keeps durations bit-exact against the teacher.
- chapter render
- 0.239s
- real-time factor
- ×706
- WER, teacher 5.65%
- 5.42%
- active params
- ~10M
Instrumenting my stack against the PyTorch reference turned up five places where the MLX port of Kokoro diverges from it, so I wrote them up and sent a patch upstream to mlx-audio . The loud one is a constant 2.5 dB attenuation: the inverse STFT divided by the sum of the window instead of its square, which for this configuration is exactly 0.75x. The subtle one is a one-frame misalignment in the upsample path that sits directly in the pitch contour, where predicted F0 went from 0.134 relative RMSE against the reference to 0.0000. Across a 55-utterance battery, MCD 7.29 to 4.09 dB and F0 error 11.5 to 5.1 Hz. It is open, not merged.
did not clear the reason this is the lead project
Texture is audibly hazier than Kokoro under direct A/B. Mel-cepstral distortion comes in around 11.8 against a 3.9 pass bar, and speaker similarity at 0.9374 against a 0.998 gate. Both are failing gates. The frozen zero-loss battery isn't passed, and the README says so on its first screen.
The figures above are the repo's. The independent no-cheating audit re-timed the same preset at 0.340s and reproduced the speedup as 40x. It ran on 29 July 2026 against the build before the compile round, and docs/REPORT.md logs what closed the gap after it: mx.compile with fixed-shape padding across the student stages, 0.333s down to 0.239s. Same engine either side of one optimization, not a dispute. What the audit says about the goal does stand, and it's the line worth quoting: 1000x wasn't reached, and the configuration that does pass the gates, ship-q8, is roughly 1x. That preset is still the default provider, and Kestrel stays opt-in until the gap closes.
weights on Hugging Face · every figure above is in docs/REPORT.md