I needed a tool that reads my markdown notes out loud on the subway. No fancy AI, no cloud subscription, just speech synthesis that works offline without sounding like a robot from 2010.
So I went down the local TTS rabbit hole. I tried Coqui TTS first — model was 2GB+, took forever to set up, and pinned my CPU to 100% on my old laptop. Then Piper, which claimed to be lightweight at 160M params, but the Chinese voice sounded like an American doing a really bad Mandarin impression. Painful.
I was about to give up and start paying OpenAI for TTS, when an HN post hit the front page with 661 points: Local, CPU-Friendly, High-Quality TTS with Kokoro. 82 million parameters. Apache 2.0 license. Runs on CPU.
My first reaction: yeah right, another overhyped GitHub project.
I tried it anyway. And honestly? It slaps.
What Kokoro Actually Is
Kokoro-82M is an open-weight TTS model. Apache 2.0, weights downloadable from HuggingFace, 82M parameters (~330MB on disk). Sounds tiny, right? But the output quality made everything else I tried feel like a toy.
Quick rundown of the important bits:
- Parameter count: 82M (not 82B), about 330MB on disk
- License: Apache 2.0 — commercial use, modify, redistribute, all good
- Languages: American English, British English, Spanish, French, Hindi, Italian, Japanese, Portuguese, Mandarin Chinese
- Voices: about 50, mostly English-optimized but a few usable Chinese voices
- Architecture: based on StyleTTS 2, uses espeak-ng for phoneme conversion (this will bite you, more on that later)
The CPU performance is the part that actually shocked me. From ariya's benchmark, using the am_eric English voice on a short paragraph:
- Intel Core i7-4770K (released in 2012, a 13-year-old CPU): 4.7 seconds
- Apple M2 Pro: 4.5 seconds
- AMD Ryzen 7 8745HS: 1.5 seconds
A 13-year-old chip can synthesize a paragraph in under 5 seconds. Let that sink in.
My First Run Crashed (Of Course It Did)
I followed the README's "simplest setup":
| 1 | |
| 2 | |
Wrote a quick test:
| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 | |
Ran it. ModuleNotFoundError: No module named 'kokoro'.
Fine, reinstalled. Got the Successfully installed kokoro-0.9.4 message. Ran it again:
| 1 | |
Apparently pip install kokoro doesn't pull in espeak-ng's Python bindings. I had to add pip install phonemizer-fork separately. This is the kind of thing that wastes 30 minutes of your life on a Tuesday afternoon.
After fixing that, I finally got a wav file. Put on headphones.
It sounded good. Natural pauses, proper intonation, a hint of emotion. Way better than the Web Speech API. I immediately sent a message to a dev group chat: "This 82M model sounds more human than some 1.6B ones."
Want an OpenAI-Compatible API? Use Kokoro-FastAPI
pip install kokoro is great for one-off synthesis, but I wanted to run it as a service for my note-taking system. So I set up Kokoro-FastAPI, a community container that wraps Kokoro behind an OpenAI-compatible HTTP API.
One command:
| 1 | |
The image is around 5GB. That's because it bundles all voice models, espeak-ng, and the Python environment. After it pulls, port 8880 is live, and you can hit http://localhost:8880/web for a basic web UI to type text and hear the output.
The actual magic, though, is the OpenAI compatibility. The container exposes /v1/audio/speech matching OpenAI's TTS endpoint. Every script I had that called OpenAI's TTS, I changed by literally two lines:
| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 | |
| 11 | |
| 12 | |
| 13 | |
| 14 | |
base_url and a placeholder api_key. Nothing else. This kind of API compatibility is rare and honestly kind of beautiful.
How's the Chinese Quality?
This is the question I get most. I tested it with lang_code='z':
| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
My honest take:
- ✅ Words are pronounced correctly, no obvious errors
- ✅ Rhythm is natural, pauses land in the right spots
- ❌ Tone is a bit flat, lacks the natural rise-and-fall of native speech
- ❌ Occasionally has that "native English speaker butchering Chinese" feel (because StyleTTS 2 was primarily trained on English)
For an 82M model, this is genuinely impressive. It works for: reading notes, announcements, accessibility. It does NOT work for: audiobooks, podcasts, professional voiceover.
If you need pro-grade Chinese TTS, you still need a commercial service (or wait for future Kokoro versions). But for "just read this thing to me" — Kokoro is the best open option I've found.
Five Things That Tripped Me Up
Saving you the 30 minutes I lost on each:
1. espeak-ng is a system dependency
pip install kokoro only handles the Python parts. You also need espeak-ng as a system binary, plus the Python bindings.
| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 | |
| 11 | |
Windows users: download the latest .msi from espeak-ng releases.
2. Mac M1/M2 users should enable MPS acceleration
By default Kokoro uses CPU on Mac. It's still fast, but you can do better:
| 1 | |
3. The container image is 5GB
Kokoro-FastAPI bundles everything. If disk is tight, check out Speaches. Smaller image, but you download voice weights manually. Bonus: it also includes Whisper for speech-to-text, so it's a TTS+STT one-stop shop.
4. lang_code must match the voice
This tripped me up. The mapping is:
'a'= American English → use withaf_*/am_*voices'b'= British English → use withbf_*/bm_*voices'z'= Mandarin Chinese → use withzf_*/zm_*voices
Mismatch = either "voice not found" or worse, garbled audio. Just pay attention to this.
5. Only four Chinese voices right now
Last time I checked VOICES.md on HuggingFace, Chinese had exactly zf_001, zf_002, zm_001, zm_002 — two female, two male. Enough to work with, but you don't have the "serious news anchor" vs "warm storyteller" range. Future versions should add more.
Hooking It Up to a Local LLM
The real use case for me: local LLM answers questions → Kokoro reads the answer out loud. Full offline AI assistant.
My setup:
- Local LLM via Ollama (Qwen2.5-7B)
- Kokoro-FastAPI on port 8880
- A Python script that chains them
| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 | |
| 11 | |
| 12 | |
| 13 | |
| 14 | |
| 15 | |
| 16 | |
| 17 | |
| 18 | |
| 19 | |
| 20 | |
| 21 | |
| 22 | |
| 23 | |
| 24 | |
Works beautifully. The speaker plays the answer as soon as the LLM finishes. Fully offline. No data leaves my machine.
Use cases opened up fast: reading paper abstracts on the subway, language learning with sentence-by-sentence playback, story time for kids (though the Chinese voice isn't quite there yet for that).
One Thing I Didn't Expect
Before sending LLM output to TTS, you need to clean it up. LLMs love to format with markdown, and TTS will dutifully read out loud:
| 1 | |
| 2 | |
It will literally say "hash hash hash Introduction" and "bracket link bracket". Yes, I made this mistake and almost spit out my coffee.
A simple preprocessor fixes it:
| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 | |
| 11 | |
| 12 | |
| 13 | |
| 14 | |
| 15 | |
| 16 | |
| 17 | |
Run LLM output through this first, then send to TTS. The robot stops yelling "hash hash" and starts saying actual words.
Performance: My Own Numbers
ariya's article only benchmarked English. I added Chinese to the comparison (using zf_001, same Jupiter paragraph translated):
- i5-8250U (my laptop): 5.8 seconds
- M2 MacBook Air: 4.3 seconds
- AMD Ryzen 7 5800X (friend's desktop): 1.4 seconds
Pure CPU, no GPU. 5-6 seconds for a paragraph is totally fine for my use case — about 3x faster than Coqui TTS on the same hardware.
Memory footprint is around 600MB resident. Less than Slack. Trivial.
Long Text: A Real Problem
I tried feeding a 5000-word markdown note directly. Results:
- 4+ minutes to fully synthesize
- No way to pause mid-generation
- Memory peaked at 1.2GB, fans spun up
My current workaround is chunking:
| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 | |
| 11 | |
| 12 | |
| 13 | |
| 14 | |
| 15 | |
| 16 | |
| 17 | |
| 18 | |
| 19 | |
| 20 | |
Then synthesize chunk by chunk. Memory stays stable, and you can overlap: play chunk N while synthesizing chunk N+1. Way better UX.
True streaming output (where you hear chunk 1 immediately while chunk 5 is still generating) is theoretically possible — Kokoro is already a generator, you just need to push the audio chunks to a sound backend (pyaudio, sounddevice). But that's a project for another weekend.
What I Haven't Figured Out Yet
Some open questions I'm sitting with:
- Voice cloning: Not supported. You pick from 50 preset voices. v2 roadmap might add it, we'll see.
- Streaming output: Currently you wait for the whole thing to generate before hearing anything. True streaming is doable but I haven't written the code.
- Code-mixing: A paragraph that mixes English and Mandarin — I currently split it into separate calls. Awkward. Would love one unified pipeline.
- NVIDIA CUDA acceleration: I enabled MPS on Mac but haven't tested CUDA on my RTX 3060. Theoretically 82M on a GPU should be sub-100ms per chunk. But my GPU is busy running Stable Diffusion 24/7.
Drop a comment if you've solved any of these.
Kokoro vs ElevenLabs / OpenAI TTS
People will ask. Here's my honest take after using both for a few months:
Where Kokoro wins:
- Fully offline: private notes, medical records, legal documents, all fine
- Zero marginal cost: a million words is still $0
- OpenAI API compatible: nearly free migration
- Tiny: 82M params, runs on a regular laptop
Where commercial wins:
- Voice variety: ElevenLabs has thousands, including accents, emotions, ages
- Voice cloning: ElevenLabs' flagship feature, not in Kokoro
- Chinese quality: Dedicated Chinese TTS services (Volcano, iFlytek) still lead
- Long-form: Commercial services are optimized for hour-long content
- Support: commercial SLAs vs GitHub issues
Pick Kokoro when:
- Data must stay local
- Cost matters, you don't want subscriptions
- Hardware is modest, no big GPU
- You're already using OpenAI TTS-style code
Pick commercial when:
- Producing audiobooks or podcasts
- Need voice cloning with your own voice
- 50+ languages including low-resource ones
- Don't care about data leaving your machine
My current setup: both, side by side. Commercial TTS for user-facing products (where quality matters most), Kokoro for personal offline tools (where privacy matters most). The combo has been working great.
Deployment Notes From The Trenches
A few things I learned the hard way running this in production:
1. Run it on a NAS, not a laptop
My laptop is a 2018 i5-8250U. Fine for dev work, but fans spin up under TTS load. I moved Kokoro-FastAPI to my home NAS (a small Celeron box, J4125), which runs 24/7 and costs pennies in electricity. I just hit the API remotely when I need it.
If your NAS has 4GB+ RAM, you can host this permanently. 82M params is nothing.
2. Watch out for espeak-ng locale issues
Some Linux distros throw locale errors when espeak-ng runs inside Docker (espeak: error: can't set locale). Fix:
| 1 | |
| 2 | |
| 3 | |
Or in your Docker run command: -e LC_ALL=C.UTF-8.
3. Don't expose the web UI in production
Kokoro-FastAPI exposes both a web UI at /web and an API at /v1. The web UI is great for testing and for non-technical teammates to play with. But it has zero auth. Anyone who hits your port can use your TTS resources.
In production, only expose the API endpoint, put nginx in front with basic auth or an API key.
4. WAV vs MP3
By default Kokoro outputs 24kHz WAV files. WAV is uncompressed — 10 seconds of audio is ~1.5MB. A 5000-word article can balloon to 50MB+.
Compress with ffmpeg:
| 1 | |
| 2 | |
64kbps is plenty for speech. 10 seconds becomes 80KB — 95% smaller.
Or skip the post-processing: Kokoro-FastAPI's /v1/audio/speech endpoint supports MP3 output directly.
Final Thoughts
If any of these match your situation:
- You need fully offline TTS
- You're tired of paying for ElevenLabs / OpenAI every month
- You want an OpenAI-compatible open-source drop-in
- Your hardware is modest (no 4090, no H100)
Kokoro-82M is the local TTS model you should know about in 2026. 82 million parameters making this kind of quality — the open-source community is genuinely cooking.
Quick start path:
pip install kokoro(after installing espeak-ng system-wide)- Run the official notebook to hear it
- For an API server,
podman runKokoro-FastAPI - Already using OpenAI TTS? Just swap the base URL
I plan to come back once I get streaming working and test CUDA acceleration. Questions or war stories, drop them in the comments.
References: