Kokoreader
Kokoreader reads text and Markdown aloud with local Kokoro inference. It provides a Node.js CLI and a VS Code/VSCodium extension for an editor tab or selection. Synthesis is local: no account, API key, or cloud service is used after one-time dependency and model downloads.
Main Features
Kokoreader is built for reading real working documents aloud, not just short plain-text prompts.
Local by design
Everything runs on your machine after the one-time model and dependency download. There is no account, API key, text upload, or cloud inference path.
Read where you work
Use the CLI for files and pipelines, or use the VS Code/VSCodium extension to read an editor selection, a whole tab, or from the current cursor through the rest of the document. Save the same output as WAV, MP3, FLAC, or Opus when live playback is not practical.
Long-document playback that stays moving
Kokoreader phonemizes text before synthesis, splits it safely below Kokoro’s model boundary, and synthesizes one upcoming unit while the current one plays, including split units within the same paragraph. It uses one resident inference worker; if the upcoming audio is not ready when playback ends, it waits for that synthesis to finish. Pause allows an already-running prefetch request to finish but withholds further prefetch requests until Resume. That avoids the long-paragraph bounds failure and reduces dead air caused by waiting for the next inference.
Live playback retains paragraph preparation only for the active paragraph and its successor, releases consumed cached PCM, and discards completed entries during forward reading. Source text remains available for navigation; Previous and Replay synthesize the selected paragraph again rather than keeping a document-wide audio cache.
It preserves a natural 800 ms gap between source paragraphs in both live playback and saved audio, without adding pauses inside a paragraph that was safely split into multiple units. The first unit targets an early sentence boundary within 200 phonemes to reduce time to first audio; later units retain the 500-phoneme safety boundary.
Markdown-aware, without silently hiding structure
Formatting, links, images, and HTML are removed before speech. Block quotes are announced as “Quote.”, including quoted task and list items; task-list items are “Checked item.” or “Unchecked item.”, unordered list items receive a short pause instead of an item marker, and ordered lists retain their number. The default technical profile says “The code is - ” and reads closed triple-backtick code blocks. Use -p narrative or set PROFILE=narrative to announce them instead:
"A code block follows. You can see the code in the document."
Reading profiles
Technical is the default: it reads closed code blocks after saying “The code is - ”, and normalizes inline code, decimal numbers, and currency amounts for speech. Narrative is available with -p narrative, PROFILE=narrative, or kokoreader.profile; it removes Markdown formatting and announces closed code blocks without reading their source. Both profiles read well-formed Markdown tables, including one-column tables, as labeled rows.
Language-aware Markdown labels
The speech-inserted Markdown labels follow --lang / kokoreader.lang: code-block introductions, table/column/row/blank-cell markers, quotes, task state, and ordered-list numbers are mapped for en-us, en-gb, es, fr-fr, hi, it, ja, pt-br, and cmn. Any other language code uses English labels. Technical-token normalization, including “dot”, “dash”, and currency names, remains English in this release.
Playback controls that respect listening
Pause immediately ends the active player so audio stops rather than continuing in the background. Resume replays a short amount of context instead of skipping a word. Stop cancels live playback and active exports.
Language-aware guardrails
Kokoreader validates selected voices, style data, and eSpeak language codes before phonemization or inference. Known voice-prefix/language disagreements warn without blocking experimental pairings; unknown custom prefixes remain usable. Script and phoneme-coverage warnings identify likely pronunciation loss in mixed-language Markdown and non-Latin text.
Documentation-grade output
Saved audio is streamed through one ffmpeg encoder, so long exports do not require buffering an entire document in memory. Each export encodes inside its own private directory beside the destination and publishes by atomic rename; it does not use or remove a preexisting sibling .part. Failure or cancellation before publication preserves prior output and cleans only owned staging. Stop requests CLI cleanup; the extension retains ownership and private input until streams close, with a hard-kill fallback after 2.5 seconds. An already-completed publication is not undone.
Concurrent CLI exports use separate staging directories: the last successful atomic publication wins, without ordering or locking guarantees. Use a stable, user-controlled destination directory; this does not defend against malicious ancestor replacement or processes running as the same user.
Repository layout
| Path |
Contents |
bin/ |
CLI entry point. |
extension/ |
VS Code/VSCodium extension entry point. |
python/ |
Kokoro worker and Python requirements. |
config/ |
Sample CLI configuration. |
test/ |
Node-based checks. |
Compatibility
** Windows OS still needs to be further tested - unfortunately I do not have any Windows device as of the current, so I will be depending on everyone's help on this. Feel free to raise an issue, specially if you're on Windows OS.
| Surface |
Supported target |
Notes |
| CLI |
macOS, Linux, Windows** |
Needs Node.js, Python 3.11+, and ffmpeg. Live playback also needs ffplay. |
| Extension |
VS Code and VSCodium 1.75+ |
Runs the bundled CLI through the editor's Node runtime. |
| CPU |
macOS 14+ Apple silicon, Windows** x64, Linux x64 |
Pins CPU ONNX Runtime 1.28.0; no official baseline wheels for Intel/older macOS. |
| Linux ARM64 |
Best effort |
Matching Python and ONNX Runtime wheels must be available. |
| Save to File |
Any supported CLI platform with ffmpeg |
No audio device or ffplay required. |
| Pause / Resume |
macOS verified, Linux and Windows** expected |
Ends the paragraph's player to mute and replays it on Resume. No platform suspend or mute API is involved. See the table below for what each platform sounds like. |
| Remote/headless editor |
Save to File, by default |
Live playback requires an audio device where the extension host runs. |
ffplay cannot be frozen in place, so Pause ends its ffmpeg/ffplay pair and Resume replays the same paragraph from a remembered offset. What that means per platform:
| Platform |
Mute |
Click at Pause |
Resume lands at |
| macOS |
player exits 8-9 ms after the signal, measured |
subtle, confirmed by ear |
measured: the rest of the paragraph plays, nothing skipped |
| Linux |
same code path: SIGTERM, which SDL turns into a quit event that closes the audio device. Not measured. |
not known |
same arithmetic. Unmeasured: if PulseAudio, PipeWire, or ALSA owns more audio than the 0.8 s back-off, Resume skips that much instead of repeating it. Report it. |
| Windows** |
same code path, but Node terminates the process abruptly instead of signalling it, so muting is at least as fast. Not measured. |
expected to be the hardest of the three |
same arithmetic. Pause did nothing at all before 0.1.3. |
The character of the click belongs to the audio stack, not to Kokoreader. Only the macOS row has been checked on real hardware.
Packaged reader/export lifecycle checks pass on macOS, Fedora, and Ubuntu with VS Code 1.75/1.144 and VSCodium 1.75/1.143. Automated playback uses dummy SDL and Save tests substitute the dialog result; manual UI and listening acceptance remain separate. Editor launches preserve the 1.75 minimum without passing Microsoft's obsolete private bootstrap flag to stock VSCodium or current Electron.
Kokoreader is not a mobile, browser, or web-extension tool.
Requirements
- Node.js 20 or newer
- Python 3.11 or newer; current official runtime wheels cover CPython 3.11–3.14
pip3 for that same Python interpreter
- ffmpeg, including
ffplay for live playback
- About 340 MB free for default model files, plus generated audio
Kokoreader pins kokoro-onnx==0.4.7 and onnxruntime==1.28.0 in python/requirements.txt, and rejects older ONNX Runtime before loading a model. Official macOS installation requires macOS 14+ Apple silicon; Intel/older macOS needs a separately verified patched-source route, not an affected-version fallback. The configured Python and ffmpeg must be accessible to the CLI and the GUI application's environment.
For the standalone CLI, prefer a maintained Node.js LTS release, such as the tested Node.js 22. Node.js 20 remains the compatibility minimum but is upstream EOL; the extension uses its editor runtime, so prefer a maintained editor release too.
Run the CLI suite with npm test. CI uses Node.js 22 and runs node test/package.js after making vsce 4.0.0 available. That check verifies a distribution-file allowlist against disposable local-state fixtures; new runtime files must be added explicitly to .vscodeignore.
CLI setup
1. Get the source
Clone or download this repository and open a terminal in its directory.
2. Check Node and Python
node --version
python3 --version
python3 -m pip --version
The Python version must be 3.11 or newer. python3 and pip3 must resolve to the same installation. On Windows, use an absolute python.exe path when python3 is not available.
3. Install inference dependencies
pip3 install -r python/requirements.txt
With multiple Python installations, target the configured one explicitly:
/path/to/python3 -m pip install -r python/requirements.txt
4. Install and verify ffmpeg
Install an ffmpeg distribution for your platform, then confirm both executables are on PATH:
ffmpeg -version
ffplay -version
Saving WAV needs only ffmpeg; live reading needs ffplay and a working audio device.
5. Download model assets
Choose a directory and run:
node bin/kokoreader.js --model-dir kkr-models --download
This downloads kokoro-v1.0.onnx and voices-v1.0.bin. Downloads happen only when explicitly requested, never while reading. A failed or cancelled download cleans its owned staging; rerun the command.
Each asset is checked against the SHA-256 of the file in the model-files-v1.0 release it came from, both after download and for files that already exist. A file that fails its digest is reported and left untouched; --force replaces exactly those:
node bin/kokoreader.js --model-dir kkr-models --download --force
Use --force to restore mismatching files from the pinned release, including truncated files, not to refresh regenerated upstream content. Matching assets are never downloaded or rewritten, even with --force. If upstream bytes no longer match the pinned digest, the download fails and preserves the existing file.
Downloads use private operation-owned staging directories inside the model directory; existing sibling .download files and symlinks are ignored. Only digest-verified assets publish by atomic rename, and --force does not bypass verification. Failed or cancelled transfers close owned streams before staging cleanup. Concurrent downloads use separate staging; use a stable, user-controlled model directory.
FP32 is the default. To explicitly download FP16 or INT8 instead:
node bin/kokoreader.js -md kkr-models -mp fp16 -d
node bin/kokoreader.js -md kkr-models -mp int8 -d
Set the same MODEL_PRECISION in configuration or kokoreader.modelPrecision in the extension before using a non-FP32 model from modelDir.
The current worker requires kokoro-onnx==0.4.7 and ONNX Runtime 1.28.0 or newer. Its guard covers supported tokens/FLOAT-speed and input_ids/INT32-speed input signatures, not generic ONNX or vocabulary-identity certification. INT32-speed graphs reject fractional speeds. Voice styles need at least 501 rows of (1, 256) real, finite values: FLOAT32 for tokens, or safely convertible to FLOAT32 for input_ids. Upgrade runtime and assets together, not model URLs alone.
Copy config/kokoreader.sampleconf to config/kokoreader.conf, then set absolute paths:
MODEL_DIR=/path/to/kokoro-models
MODEL_PRECISION=fp32
PYTHON_PATH=/path/to/python3
VOICE=af_heart
LANG=en-us
PROFILE=technical
SPEED=1.0
TEMPO=1.0
GAIN=-1
VOLUME=1.0
FORMAT=wav
SAMPLE_RATE=0
NORMALIZE=false
LIMITER=true
PYTHON_PATH must be an executable such as /usr/local/bin/python3, not a directory ending in bin.
Relative MODEL_DIR, MODEL_PATH, and VOICES_PATH values in a config file resolve against the Kokoreader install directory, so MODEL_DIR=kkr-models works from any working directory and in the extension. Absolute paths are still recommended. OUTPUT_FILE, START_PARA, and DEBUG are accepted only as flags, never from a config file.
Configuration precedence:
KOKORO_READER_CONFIG, a config-file path
config/kokoreader.conf
~/.config/kokoreader/config
- Built-in defaults
CLI flags override config-file values.
7. Test it
node bin/kokoreader.js --list
node bin/kokoreader.js README.md
node bin/kokoreader.js --output README.wav README.md
Expect paragraph progress. Kokoreader phonemizes and splits each paragraph into safe units before synthesis. It adds a natural 800 ms silent gap between source paragraphs in both live playback and saved audio, but not between safe units from the same paragraph. With a file input, a controlling process can send pause, resume, previous, replay, next, and stop through stdin. Navigation is paragraph-based; it cancels stale work and keeps the paused state. pause ends the paragraph's player at once; resume replays that paragraph from where it was heard, backed off by about 0.8 s. With no file, stdin is text input:
printf 'Hello from Kokoreader.\n' | node bin/kokoreader.js
CLI reference
| Flag |
Purpose |
-md, --model-dir DIR |
Directory containing model assets. |
-m, --model PATH; -vs, --voices PATH |
Explicit model and voice-data paths. |
-mp, --model-precision P |
Download/use fp32 (default), fp16, or int8 model assets. |
-py, --python-path PATH |
Python interpreter executable. |
-v, --voice NAME; -l, --lang CODE; -s, --speed N |
Voice, language, and Kokoro synthesis speed (0.5-2.0). |
-p, --profile NAME |
technical (default) says “The code is - ” then reads closed fenced code blocks; narrative announces them. |
-t, --tempo N; -g, --gain DB; -vol, --volume N |
Final playback controls. Tempo accepts 0.5 through 100. |
-f, --format TYPE; -sr, --sample-rate HZ |
Saved format and sample rate. The format controls encoding; use a matching filename extension. |
-n, --normalize; -nn, --no-normalize |
Enable or disable EBU R128 normalization. |
-lim, --limiter; -nlim, --no-limiter |
Enable or disable final peak limiting. |
-o, --output FILE |
Save audio instead of playing. Use an extension that matches --format. |
-sp, --start-para N |
Start at one-based paragraph N; 0 reads from the beginning. |
--threads N |
Cap ONNX Runtime inference threads. 0 is the default and lets ONNX Runtime choose. |
--pronunciations FILE |
Read a local JSON pronunciation replacement dictionary. |
-d, --download |
Download selected assets to --model-dir. |
--force |
With --download, replace assets whose SHA-256 does not match the release. |
-ls, --list; -ll, --list-languages; -dbg, --debug |
List voices, list accepted language codes, or show worker errors. The extension always appends failed reads and exports to err.jsonl. Add --lang CODE to --list to show only installed voices matching that Kokoro voice language. |
Input is plain text or Markdown. Formatting, links, images, and HTML tags are removed before synthesis. The default technical profile says “The code is - ” before reading a closed triple-backtick fenced code block. With -p narrative, the block is replaced with the spoken paragraph:
“A code block follows. You can see the code in the document.”
Inline backtick code is normalized as a technical token in both profiles: dots become “dot”, underscores become “underscore”, path separators become “slash” or “backslash”, hyphens become “dash”, and camelCase gets a word boundary. Thus nmnm.jsonc is read as “nmnm dot jsonc.” In either profile, a dot directly between digits is read as “dot”, so 3.11 becomes “3 dot 11.” Currency decimals instead use “point” and a trailing currency name: $3.50, €45.35, £1.00, and ¥0.75 become “3 point 50 dollars,” “45 point 35 euros,” “1 point 00 pounds,” and “0 point 75 yen.” Block quotes are announced, task-list state is spoken, unordered list bullets produce a short pause, and ordered lists retain numbered markers. These inserted labels use the configured language map for en-us, en-gb, es, fr-fr, hi, it, ja, pt-br, and cmn; unsupported codes use English labels. Well-formed GitHub-flavored pipe tables, including one-column tables, are read in both profiles as a table and column announcement followed by one labeled row per paragraph; blank cells use the configured language’s blank marker. Malformed tables remain source text. The technical profile uses the configured language’s code introduction before reading each closed backtick or tilde fence with those rules. Unclosed fences remain literal source text.
Pronunciations and CPU budget
Set --pronunciations FILE, PRONUNCIATIONS_PATH, or kokoreader.pronunciationsPath to a readable local JSON object such as { "nmnm.jsonc": "n m n m dot json c" }. Replacements are case-sensitive, match whole literal phrases, prefer longer keys, run only against original source text, and run before Markdown and technical-token processing. Empty keys or values and malformed JSON stop the read before the worker starts. Set --threads N, THREADS, or kokoreader.threads to cap ONNX Runtime CPU use. Keep 0: the current listening comparison found longer synthesis pauses with explicit caps of 2 and 4. The worker reports ready after model startup; startup that exceeds 20 seconds and requests that exceed 120 seconds stop the worker with a named error.
Debug error log
Every failed extension read or Save to File export appends one JSON Lines record. Records contain only timestamp, operation, process event, exit code or signal, and a generated summary; raw stderr, source text, and audio are never recorded. Export failures show a bounded CLI error after stderr drains, including setup and encoder details without enabling debug. Cancellation produces no success/failure notification or error-log entry. kokoreader.debug exposes additional worker diagnostics without replacing playback progress. Kokoreader first writes <extension>/logs/err.jsonl, for example /Users/lines/.vscode-oss/extensions/openlines.kokoreader-0.4.0/logs/err.jsonl; if that installed directory is not writable, it uses the editor's extension log storage instead. The file rotates at 1 MiB, retaining one previous file as err.jsonl.1.
VS Code and VSCodium setup
The repository currently contains extension source. Install a packaged .vsix once one is built, or use an Extension Development Host when developing locally.
Kokoreader requires Workspace Trust and is disabled in Restricted Mode. Workspace settings can select the Python executable and model files, so review those settings before trusting a workspace. Model-contract checks are not a sandbox for untrusted model files.
Configure these editor settings:
kokoreader.pythonPath: an absolute Python 3.11+ executable with the requirements installed.
kokoreader.modelDir: the downloaded model directory, or both kokoreader.modelPath and kokoreader.voicesPath.
kokoreader.modelPrecision: fp32 by default, or fp16/int8 when the matching model asset was downloaded.
kokoreader.profile defaults to technical; set it to narrative to announce, rather than read, fenced code. The setting applies to Read, Read From Cursor, and Save to File.
kokoreader.lang is a dropdown of Kokoro-82M’s nine voice languages. Set kokoreader.voice manually to a matching installed voice; use kokoreader --list --lang CODE to inspect matches. The interactive language and filtered voice commands are deferred.
- Optionally set playback controls below, and
kokoreader.debug to reveal Python worker errors.
Right-click an editor tab for Read, Read From Cursor, Pause, Resume, Stop, or Save to File. On the active editor, Read and Save to File use its current in-memory selection when one exists, otherwise its current in-memory document, so unsaved edits are included. A command invoked for another tab reads that tab’s saved file rather than silently using the active editor. Read From Cursor ignores any selection and speaks from the exact active cursor position through the end of the current in-memory document, so it can start mid-word. Save to File uses kokoreader.format to select WAV, MP3, FLAC, or Opus; it is unavailable while reading, and only one export or pending save dialog can run at a time. Stop cancels either safely before an export process or private temporary input is created. The same guards apply to Command Palette commands, and a dialog failure restores the commands immediately. The status bar shows activity and stops active playback when clicked. Pause mutes at once and Resume replays the current paragraph from where it stopped, so a word or two can repeat.
Implemented controls
| Setting |
Effect |
voice / VOICE / --voice |
Selects Kokoro timbre and style. |
lang / LANG / --lang |
Selects language and phonemization behavior. VS Code offers Kokoro-82M’s supported languages; the CLI accepts any installed espeak-ng code. |
profile / PROFILE / -p, --profile |
Selects technical (read closed code fences) or narrative (announce them). |
speed / SPEED / --speed |
Changes Kokoro synthesis speed within 0.5-2.0. |
threads / THREADS / --threads |
Caps ONNX Runtime inference threads; keep 0 for the current default because listening tests found longer synthesis pauses with 2 and 4. |
tempo / TEMPO / --tempo |
Changes final playback speed while preserving pitch. |
gain / GAIN / --gain |
Sets output headroom in dB. |
volume / VOLUME / --volume |
Sets final loudness multiplier. |
format / FORMAT / --format |
Selects WAV, MP3, FLAC, or Opus for saved audio. |
sampleRate / SAMPLE_RATE / --sample-rate |
Converts saved audio; 0 preserves Kokoro’s source rate. |
normalize / NORMALIZE / --normalize |
Applies EBU R128 loudness normalization. |
limiter / LIMITER / --limiter |
Caps peaks after gain, tempo, and optional normalization. |
pronunciationsPath / PRONUNCIATIONS_PATH / --pronunciations |
Applies literal source-phrase replacements before Markdown and technical-token processing. |
modelDir, modelPath, voicesPath, modelPrecision |
Chooses installed model assets and FP32/FP16/INT8 precision. |
pythonPath |
Chooses the Python inference environment. |
Languages, voices, and mismatch warnings
--lang is an espeak-ng code, not Kokoro's documented language letter. Match it to the language of the text as well as the voice: Japanese text should use --lang ja and a j-initial Japanese voice. Safe chunking prevents the 510-phoneme bounds failure but cannot make Japanese pronounced through en-us sound correct. Mixed-language text is not automatically routed to multiple G2P pipelines. A punctuation-free run is hard-split without dropping text, which can make that boundary sound less natural. VOICE_LANGS in python/kokoro_worker.py maps each Kokoro voice initial to the code its voices were trained for:
| Voice |
Language |
--lang |
af_*, am_* |
American English |
en-us |
bf_*, bm_* |
British English |
en-gb |
ef_*, em_* |
Spanish |
es |
ff_* |
French |
fr-fr |
hf_*, hm_* |
Hindi |
hi |
if_*, im_* |
Italian |
it |
jf_*, jm_* |
Japanese |
ja |
pf_*, pm_* |
Brazilian Portuguese |
pt-br |
zf_*, zm_* |
Mandarin Chinese |
cmn |
The following Kokoro-82M v1.0 inventory and overall grades are derived from upstream VOICES.md. Grades estimate training-data quality and quantity, not a universal listening-quality ranking. “Not published” means upstream does not provide an overall grade for that voice. Custom or partial local voices-v1.0.bin assets can differ; use --list to inspect what is installed.
| Name |
Grade |
Language |
af_heart |
A |
American English |
af_alloy |
C |
American English |
af_aoede |
C+ |
American English |
af_bella |
A- |
American English |
af_jessica |
D |
American English |
af_kore |
C+ |
American English |
af_nicole |
B- |
American English |
af_nova |
C |
American English |
af_river |
D |
American English |
af_sarah |
C+ |
American English |
af_sky |
C- |
American English |
am_adam |
F+ |
American English |
am_echo |
D |
American English |
am_eric |
D |
American English |
am_fenrir |
C+ |
American English |
am_liam |
D |
American English |
am_michael |
C+ |
American English |
am_onyx |
D |
American English |
am_puck |
C+ |
American English |
am_santa |
D- |
American English |
bf_alice |
D |
British English |
bf_emma |
B- |
British English |
bf_isabella |
C |
British English |
bf_lily |
D |
British English |
bm_daniel |
D |
British English |
bm_fable |
C |
British English |
bm_george |
C |
British English |
bm_lewis |
D+ |
British English |
ef_dora |
Not published |
Spanish |
em_alex |
Not published |
Spanish |
em_santa |
Not published |
Spanish |
ff_siwis |
B- |
French |
hf_alpha |
C |
Hindi |
hf_beta |
C |
Hindi |
hm_omega |
C |
Hindi |
hm_psi |
C |
Hindi |
if_sara |
C |
Italian |
im_nicola |
C |
Italian |
jf_alpha |
C+ |
Japanese |
jf_gongitsune |
C |
Japanese |
jf_nezumi |
C- |
Japanese |
jf_tebukuro |
C |
Japanese |
jm_kumo |
C- |
Japanese |
pf_dora |
Not published |
Brazilian Portuguese |
pm_alex |
Not published |
Brazilian Portuguese |
pm_santa |
Not published |
Brazilian Portuguese |
zf_xiaobei |
D |
Mandarin Chinese |
zf_xiaoni |
D |
Mandarin Chinese |
zf_xiaoxiao |
D |
Mandarin Chinese |
zf_xiaoyi |
D |
Mandarin Chinese |
zm_yunjian |
D |
Mandarin Chinese |
zm_yunxi |
D |
Mandarin Chinese |
zm_yunxia |
D |
Mandarin Chinese |
zm_yunyang |
D |
Mandarin Chinese |
List every code the installed espeak-ng build accepts, plus the voice counts for the assets in --model-dir:
node bin/kokoreader.js --list-languages
espeak-ng does the phonemization, so three failures are otherwise visible only in the audio. Each is reported once per run on stderr as Kokoreader: ... and playback continues:
- Foreign script under a Latin-only code (Han, kana, Hangul, Cyrillic, Greek, Arabic, Hebrew, Devanagari, Thai): espeak-ng spells the characters out in English, so
世界 becomes "chinese letter". Set --lang to the language of the text.
- A language switch inside one paragraph: espeak-ng marks it
(en)-style and Kokoro's vocabulary filter keeps the letters inside the brackets as phonemes.
- Phonemes outside Kokoro's vocabulary: reported as a percentage. Mandarin loses its tone digits this way (about 22%);
yue loses less. cmn-latn-pinyin resolves to the same espeak-ng voice as cmn, so Latin pinyin is still read as English.
Japanese and Mandarin are the weakest pairings: upstream trains them with misaki and publishes no espeak-ng fallback, and kokoro-onnx does not use misaki.
Troubleshooting
| Symptom |
Cause and action |
spawn .../bin ENOENT |
PYTHON_PATH is a directory. Use a Python executable. |
No module named 'kokoro_onnx' |
Install requirements with the exact configured interpreter. |
requires onnxruntime 1.28.0 or newer |
Reinstall python/requirements.txt with the configured interpreter. Intel/older macOS has no official baseline wheel; do not downgrade around the guard. |
model not found |
Run --download with --model-dir, or correct model paths. |
| No audio |
Verify ffplay -version and audio output. Try --output test.wav first. |
| Resume repeats a word or two |
Expected. ffplay cannot be paused in place, so Pause ends its player and Resume replays the paragraph from the remembered position, about 0.8 s back. |
| A short click at Pause |
Expected, and the point of ending the player: it replaces the word ffplay used to stutter out while frozen. Measured on macOS as subtle; expected to be most audible on Windows, where the process is terminated abruptly. |
speed must be between 0.5 and 2.0 |
Kokoro synthesis limit. Keep speed in range and use tempo for faster or slower playback. |
lang "zh" is not supported by espeak-ng |
--lang takes espeak-ng codes, not Kokoro's: Mandarin is cmn, Cantonese is yue. Run --list-languages for the full set. |
Kokoreader: espeak-ng cannot read ... (Latin dictionaries only) |
The text is not Latin script but --lang is a Latin-only code. Set --lang to the language of the text. |
Kokoreader: espeak-ng read part of this text as ... |
The paragraph mixes languages and espeak-ng switched. Split the text, or set --lang to the dominant language. |
Kokoreader: NN% of the phonemes ... were dropped |
Expected for cmn/yue: tone digits and vowels are outside Kokoro's vocabulary. Non-Latin output quality is limited by espeak-ng, not by Kokoreader. |
does not match the kokoro-onnx model-files-v1.0 release |
The asset is stale or truncated. Rerun with --download --force to replace it. |
| Worker error |
Check the reported error; retry with --debug for additional Python diagnostics. |
Known bugs
Hardening remains in progress. Windows verification is deferred until a device is available, not passed or waived. Packaged lifecycle checks, native pinned Linux downloads, local TLS failure probes, and Node 20/22 download-close regressions pass. Manual UI/listening acceptance remains pending; the tested ONNX Runtime 1.28.0 baseline is now enforced. Voice/model guards do not certify arbitrary custom graph/vocabulary pairings. Japanese and Mandarin quality remains a documented engine limitation.
Credits and licensing
Kokoreader is an independent wrapper inspired by and built around the same Kokoro ONNX inference approach as Kokoro TTS, created by Nazmul Hossain (@nazdridoy). Thank you for the MIT-licensed design reference; no source code was copied or translated from it.
Kokoreader's original source is MIT-licensed. It uses kokoro-onnx and Kokoro-82M, but its current packages do not include Python dependencies, native libraries, model/voice assets or FFmpeg. Users install dependencies and download assets directly from upstream; shipping this wrapper does not redistribute those copies.
The Python worker uses GPL-licensed phonemizer-fork/eSpeak NG. MIT is GPL-compatible, so original wrapper source can remain MIT; a redistributed combined runtime must still follow the GPL. If you later bundle or mirror dependencies, preserve their applicable licences/notices and provide corresponding source where the GPL requires it. Redistributing Apache-2.0 model/voice assets requires their applicable licence and notices, not training attestations. These are conditional packaging duties, not missing notices for the current unbundled wrapper.
The pinned ONNX matches its converter origin, and repacking all 54 original voice tensors reproduces the pinned voice file exactly. Such provenance checks provide supply-chain assurance; the inspected licences do not require upstream build/training attestations or an independent training-data audit to use or distribute this wrapper.