kokoro-speakd
Persistent Kokoro TTS daemon for Claude Code and other clients. Loads the model once and serves synthesis requests over a Unix domain socket, so a machine running 10+ concurrent Claude Code sessions doesn't pay the cold-start cost on every response.
- Single process, single model in RAM, regardless of how many clients.
- Preemption over queueing — new speech always cancels older in-flight playback, which is what you actually want when juggling many sessions.
- Lazy per-language pipelines — default English loads at startup, other languages spin up on first use.
- Nix-first packaging —
flake.nixexports both the daemon and a stdlib-only client, withdlinfo/en_core_web_smworkarounds baked in sopython3Packages.kokorobuilds cleanly on darwin. - Line-delimited JSON protocol over Unix sockets, trivial to drive from any shell or language.
Capability
Pattern. Persistent Kokoro TTS daemon over a Unix socket — one kokoro-speakd process holds the Kokoro-82M weights and en_core_web_sm phonemizer in RAM; a stdlib-only kokoro-speak client speaks line-delimited JSON to it.
Trade-off. A 5–8 s cold model load on first boot, in exchange for sub-500 ms warm-call latency on every subsequent kokoro-speak invocation. Per-call model reload is avoided entirely, regardless of how many concurrent clients are talking to the daemon.
Use when. A Claude Code hook, shell pipeline, or agent loop needs synchronous TTS without paying PyTorch warm-up cost per response — markdown stripping is built in, preemption replaces queueing so the latest thought always wins, and the socket path is stable enough to drop into Stop / SessionEnd / UserPromptSubmit hooks.
pip install kokoro-speakd # PyPI (when first release cut)
kokoro-speakd & # daemon, single instance per user
echo "hello world" | kokoro-speak # stdin → speech, ~412 ms warm-call
kokoro-speak < release-notes.md # markdown → strip → speech
kokoro-speak interrupt # cancel in-flight playback
Demo
A non-interactive 18-second asciinema cast covering kokoro-speakd --help, daemon start with warm-load timing, kokoro-speak ping, an inline echo … | kokoro-speak, a markdown-stripping kokoro-speak < file.md, and kokoro-speak interrupt is checked into the repo at docs/assets/kokoro-demo.cast. Replay locally:
asciinema play docs/assets/kokoro-demo.cast
A hosted player embed will land in a follow-up PR after the cast is uploaded to asciinema.org.
How kokoro-speakd compares
Closest peers in the open-source TTS-client ecosystem:
| Capability | kokoro-speakd (this repo) |
say (macOS native) |
espeak-ng |
piper-tts |
|---|---|---|---|---|
| Persistent daemon (warm-call <500 ms) | yes | n/a (system service) | no (per-call process) | manual (no built-in daemon) |
| Quality voice (Kokoro 82M ONNX) | yes | yes (macOS voices only) | no (formant-synth, robotic) | yes |
| Cross-platform (Linux + macOS) | yes | no (macOS only) | yes | yes |
| Markdown → speech preprocessing | yes (markdown.py strip) |
no | no | manual |
| Preemption over queueing (latest thought wins) | yes | no (queues) | no (no queue) | no |
| Lazy per-language pipelines | yes | n/a | n/a (single back-end) | manual |
| PEP 740 PyPI attestations | pending Trusted Publishing wire | n/a | n/a | no |
| Claude Code hook integration documented | yes (Stop / SessionEnd) |
no | no | no |
For multi-tenant TTS gateways and self-hosted REST services see coqui-ai/TTS or openedai-speech — different shape of problem, listed in What this is NOT.
What this is NOT
- Not a multi-tenant TTS SaaS. Each
kokoro-speakdinstall is scoped to one user, one model in RAM, one socket. - Not a REST gateway. Use
coqui-ai/TTSoropenedai-speechif that's what you want. - Not a real-time streaming TTS. Synthesis runs per-request; preemption swaps the playback target, it does not splice mid-utterance.
- Not a voice-cloning tool. Voices are picked from the 54 ONNX models Kokoro ships; bring-your-own-voice is upstream's problem.
Quick start (with Nix)
# Start the daemon in the foreground:
nix run github:yolo-labz/kokoro-speakd
# From another terminal:
echo "hello from kokoro" | nix run github:yolo-labz/kokoro-speakd#kokoro-speak
nix run github:yolo-labz/kokoro-speakd#kokoro-speak -- ping
nix run github:yolo-labz/kokoro-speakd#kokoro-speak -- interrupt
The first request after a cold daemon start takes ~5–8 seconds while the PyTorch weights warm up. Every request after that is <500 ms regardless of how many clients are talking to the daemon.
Claude Code hook integration (nix-darwin / home-manager)
-
Add the flake as an input:
kokoro-speakd = { url = "github:yolo-labz/kokoro-speakd"; inputs.nixpkgs.follows = "nixpkgs"; };
-
Install the daemon and client on your user, and register a launchd agent so the daemon auto-starts at login:
{ inputs, pkgs, system, ... }: let kokoro = inputs.kokoro-speakd.packages.${system}; in { home.packages = [ kokoro.kokoro-speak kokoro.kokoro-speakd ]; launchd.agents.kokoro-speakd = { enable = true; config = { ProgramArguments = [ "${kokoro.kokoro-speakd}/bin/kokoro-speakd" ]; KeepAlive = true; RunAtLoad = true; StandardOutPath = "${config.home.homeDirectory}/.cache/claude-code-tts/launchd.out.log"; StandardErrorPath = "${config.home.homeDirectory}/.cache/claude-code-tts/launchd.err.log"; }; }; }
-
Point Claude Code's
Stop,UserPromptSubmit, andSessionEndhooks atkokoro-speak— the daemon handles the rest. Seephsb5321/NixOSfor a full reference integration, including the transcript walk that pulls Claude's final narrative text out of the JSONL log.
Protocol
One JSON request per connection, newline-terminated. Response is a single newline-terminated JSON object.
// speak (replaces any in-flight speech)
{"action": "speak", "text": "hello there", "voice": "af_sky", "lang": "a"}
// -> {"status": "queued", "id": 42}
// -> {"status": "loading"} // model still warming up
// -> {"status": "empty"} // text was whitespace after stripping
// cancel current playback
{"action": "interrupt"}
// -> {"status": "ok"}
// health check
{"action": "ping"}
// -> {"status": "pong", "ready": true}
Defaults: voice="af_sky", lang="a" (American English). Override per
request or globally via KOKORO_DEFAULT_VOICE / KOKORO_DEFAULT_LANG.
Environment
Daemon:
KOKORO_SPEAKD_SOCKET— socket path (default~/.cache/claude-code-tts/kokoro-speakd.sock)KOKORO_SPEAKD_LOG— log file pathKOKORO_DEFAULT_VOICE,KOKORO_DEFAULT_LANG— fallback values
Client (kokoro-speak):
KOKORO_VOICE,KOKORO_LANG— per-request overridesKOKORO_MAX— hard char cap on the text sent (default 5000)KOKORO_SPEAKD_SOCKET— matching override to reach a non-default daemon
Voices
Kokoro ships 54 voices. American English defaults to af_sky; see
hexgrad's VOICES.md
for the full list including British English, Japanese, Mandarin, French,
Italian, and more. Set KOKORO_VOICE=af_bella (etc.) and reload the daemon.
License
MIT. See LICENSE.
Services
Compliance-grade AI architecture for regulated workloads — async-first, USD-denominated, LATAM-based / EN-fluent. See blog.home301server.com.br/services.
Metadata
Release files for kokoro-speakd 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| kokoro_speakd-0.3.0.tar.gz | 11.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| kokoro_speakd-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 25.0 kB
Release files / kokoro_speakd-0.3.0.tar.gz
| Download URL | kokoro_speakd-0.3.0.tar.gz |
|---|---|
| Size | 11.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
50723298f982fb027d66ff12a3ff456f96fd468b7dcc3a2701e6ae277a799b2c
|
|
BLAKE2b-256 checksum How to use checksums |
63057cdf064cdf5c8ae934c7920b7064bc4a4a1909421f2c888ce93e6a1d3cd4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 29, 2026.
Transparency logRelease files / kokoro_speakd-0.3.0-py3-none-any.whl
| Download URL | kokoro_speakd-0.3.0-py3-none-any.whl |
|---|---|
| Size | 13.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
df8ee9dfb12ab50d107c613747f02d50907ec4ba9e816401af27f23130f3b5e2
|
|
BLAKE2b-256 checksum How to use checksums |
5bbeb5ec84ab1e30a599888c8f469119fdb445c35d33dc227746f68f8d84438c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 29, 2026.
Transparency log