This is not the original Doc Reader. The original app is Cody Mitchell's: SproutSeeds/doc-reader. Cody designed and built Doc Reader, the local-first speech workspace, for macOS. This fork, kept by Aaron Tate, only adds the Windows part: the pieces needed to make the same app install and run on Windows, plus a few web-page conveniences that came with that work. Everything else, the reader, the speech engines, the Library, the macOS app, is Cody's. For the original project and its author, go to the upstream repository.
About this fork. Original: SproutSeeds/doc-reader by Cody Mitchell (research stewardship: Fractal Research Group, frg.earth). Cody's app runs on macOS. This fork makes it Windows-ready and keeps the macOS app working, so one codebase serves both. It keeps the upstream MIT license and Cody's copyright (see
LICENSE). Main branch:windows-support. Repository: github.com/AaronTateDev/doc-reader-fork.
Doc Reader reads documents and selected text aloud with a neural voice, turns
your speech into text anywhere you can type, and keeps everything it reads or
hears as cards in a local Library. Nothing leaves your machine: Kokoro
text-to-speech and Whisper speech-to-text run locally (on an NVIDIA GPU when
one is present), and the web page at http://127.0.0.1:8766 is served from
your own computer.
- Read anything: paste text, import
.pdf,.docx,.txt, or.md, or highlight text in any app and press the read-selection hotkey. - Dictate anywhere: hold the dictation key, speak, release; the transcript is pasted at the cursor and saved to the Library.
- 28 English Kokoro voices, American and British, with a play-sample button for each; speed control; pause, resume, stop.
- Hotkeys you choose: click a key in Details and press the one you want, or use the one-click presets. Side mouse buttons work for dictation.
- Library and Signal map: search and filter everything you have read or dictated, edit transcripts, and see word counts and topics.
The app itself is Cody's. These are the Windows additions and the small web-page changes made along the way:
| Area | Change |
|---|---|
| Windows port | run-doc-reader.cmd launcher, hidden background services, system-tray helper with hotkeys and hold-to-dictate, CUDA or CPU PyTorch install, doctor and status commands, login startup shortcut |
| Dictation latency | Pre-armed microphone stream and cached service health checks so dictation starts and ends without the earlier delay |
| Web page | Content-first workspace redesign (Library, editor, footer controls, Details inspector), dark and light themes, keyboard and screen-reader support |
| Voices | Kokoro voice picker with all English voices and samples; the chosen voice is used for playback and prepared audio |
| Hotkeys | Change the dictation key and read-selection shortcut from the page or the tray menu, with rules and plain-language errors; the running helper rebinds without a restart |
| Reliability | Stopping one Doc Reader instance can no longer kill another instance's processes |
Status: Windows is verified end to end on Windows 11 with an RTX 3080. The
macOS hotkey changes in macos/DocReaderApp were written on Windows and have
not yet been built or run on a Mac; the checklist is in
docs/design-notes/2026-09-12-content-first-workspace.md.
Maintained upstream by SproutSeeds. Research stewardship: Fractal Research Group (frg.earth).
A local-first speech workspace for reading documents, capturing dictation, and keeping the resulting material organized in one local Library. It runs on macOS (native menu-bar app) and on Windows (tray helper, this fork), with Kokoro text-to-speech and Whisper speech-to-text running entirely on your own machine.
It streams .pdf, .docx, .txt, and .md files through local neural TTS,
reads highlighted text with Right Command or Command-L, captures Option-key
dictation and uploaded audio through local speech-to-text, and keeps playback
continuous by preparing later chunks in the background.
- Uses Mac-local Kokoro and speech-to-text as the primary speech path.
- Keeps OpenAI speech as an explicit optional backend that loads only from your environment, macOS Keychain, or ORP secrets when you select it.
- Supports optional private remote speech through Tailscale, with local
sayfallback on macOS. - Turns reading, dictation, and external app handoffs into durable Library cards.
- Tracks a local Signal Map for STT/TTS word counts, reading completion, and batch analysis.
- Reads with understanding in
smartmode instead of spelling every character. - Prefetches chunks so playback remains continuous.
- Supports pause/resume, active-field dictation insertion, and selectable speech backends.
- macOS: menu-bar app, login agent, Right Command / Command-L selection hotkey, Option hold-to-dictate, and the right-click Services item.
- Windows 10/11: system-tray helper, Ctrl+Alt+R selection hotkey, Right Ctrl hold-to-dictate, login startup shortcut, and the same local web app. Kokoro and Whisper run on an NVIDIA GPU through CUDA when one is present, otherwise on CPU.
- Linux: the document CLI works with the same Python engine; the tray helper and service orchestration are not packaged.
Everything runs locally on your PC: the Kokoro speech service, Whisper speech-to-text, the web app, and a tray helper for hotkeys and dictation.
Prerequisites (one time):
winget install astral-sh.uv # creates the Python 3.12 environment
winget install Gyan.FFmpeg # audio playback (ffplay) and dictation audio cleanup
winget install eSpeak-NG.eSpeak-NG # optional: Kokoro bundles its own espeak-ngClone this repository, then from the checkout folder:
.\run-doc-reader.cmdThe first run builds .venv (PyTorch with CUDA 12.4 when an NVIDIA GPU is
detected, otherwise the CPU build), installs Kokoro, faster-whisper, PySide6,
pynput, and sounddevice, starts the three background processes, and opens
http://127.0.0.1:8766. The first Kokoro and Whisper model downloads take a
minute or two; the web page shows local-kokoro online once speech is ready.
Useful commands:
.\run-doc-reader.cmd status # service health, GPU in use, helper state
.\run-doc-reader.cmd stop
.\run-doc-reader.cmd restart
.\run-doc-reader.cmd doctor # Python, CUDA, Kokoro, ffmpeg, espeak, microphone checks
.\run-doc-reader.cmd enable-startup # launch at login
.\run-doc-reader.cmd disable-startup
.\run-doc-reader.cmd install-shortcuts # clickable "Doc Reader" on the Desktop and in the Start menu
.\run-doc-reader.cmd remove-shortcuts
.\run-doc-reader.cmd cli .\paper.pdf --mode smart # command-line readernpm install -g users get the same commands through read-docs start,
read-docs stop, read-docs status, read-docs doctor, and read-docs enable-startup when run from a checkout on Windows.
What the tray helper gives you on Windows:
- Read highlighted text: select text in any app and press
Ctrl+Alt+R. The helper copies the selection (restoring your clipboard afterwards), sends it to the web app, and playback starts through Local Kokoro. - Dictation: put the cursor in a text field and hold
Right Ctrl. A small HUD shows while recording; release the key and the audio goes to local Whisper, the text is pasted at the cursor, and aDictationcard lands in the Library. - Tray menu: Open Doc Reader, Read Selection, Read Clipboard, Pause/Resume, Stop, toggle dictation, Quit.
Change the keys from the web page: open Details, and under Dictation click the key shown next to "Dictation key" or "Read selection", then press the key you want. Preset chips below each field give a one-click swap. The running helper switches within a couple of seconds, no restart needed, and the choice is saved on disk with the other web settings, so it survives restarts and reboots.
Rules, enforced by the page and the server with a plain-language reason:
- Dictation is one key you hold: Ctrl, Alt, or Shift on their own (either side), F1 to F24, Scroll Lock, Pause, Insert, Caps Lock, or a side mouse button (Mouse 4 / Mouse 5). Letters, numbers, Space, Enter, Tab, the Windows/Command key, and left, right, or middle click are refused.
- Read selection is a chord: at least one of Ctrl, Alt, Shift plus one letter, number, function key, or Space. The Windows/Command key is refused.
On macOS the same page shows Control and Option instead of Ctrl and Alt, and the Mac helper applies the saved keys on its next status check (built-in fallback: hold Option, Control+Option+Command+R).
Environment variables still work as the fallback default when nothing has been picked in the page:
$env:DOC_READER_SELECTION_SHORTCUT = "<ctrl>+<shift>+r"
$env:DOC_READER_DICTATION_KEY = "f8" # any pynput key name: ctrl_r, alt_r, scroll_lock, f9...
$env:DOC_READER_STT_MODEL = "medium" # Whisper size: tiny, base, small (default), medium, large-v3
.\run-doc-reader.cmd restartData, logs, and PID files live in %USERPROFILE%\.doc-reader-managed
(logs\tts.log, logs\web.log, logs\helper.log). The web app listens on
127.0.0.1:8766 and the speech service on 127.0.0.1:8772; both are loopback
only.
Install the npm bootstrapper, then install the managed macOS app agent:
npm install -g read-docs
read-docs install
read-docs statusread-docs install copies the runtime into ~/.doc-reader-managed, prepares its
Python environment, registers a LaunchAgent, starts the menu-bar app, and installs
the Read with Doc Reader Services item for highlighted text.
Current native-wrapper builds require Apple's Command Line Tools because the app bundle is compiled locally during install:
xcode-select --installUseful app commands:
read-docs start
read-docs open
read-docs dock
read-docs stop
read-docs restart
read-docs status
read-docs doctor
read-docs ensure
read-docs uninstallStartup runs a tailnet orchestration pass: the web app is started, private
Tailscale Serve is checked or configured, and the Mac-local speech service
starts when needed. Remote speech startup is opt-in with
DOC_READER_REMOTE_SPEECH_AUTOSTART=1. Use read-docs ensure to run that pass directly, or
read-docs doctor --json to inspect readiness without starting dependencies.
Opening Doc Reader.app runs the same readiness pass after the local web app is
reachable, so closing and reopening the app also repairs stopped speech helpers.
The installer also creates ~/Applications/Doc Reader.app with the native app
icon and registers it with Launch Services. You can launch it from Applications,
Spotlight, or the Dock. The Dock/menu-bar app opens the web page, which is the
canonical DocReader interface.
Doc Reader runs as a local web app and can be exposed to your tailnet:
read-docs tailscale
read-docs web-statusBy default, the local service listens on http://127.0.0.1:8766. Tailscale
Serve can expose the same page at https://<this-machine>:8766 inside the
tailnet. The web app supports document upload, audio transcription, text
reading, Library cards, play, pause, resume, stop, and voice settings.
Local-only controls:
read-docs web-start
read-docs web-stop
read-docs webDoc Reader runs private neural speech sidecars as the normal app speech path. The default app playback backend stays on your Mac:
Mac Kokoro
The selectable private backends are:
Mac Kokoro -> Remote Kokoro -> Remote Chatterbox -> macOS say
The web page's Voice menu lists the 28 English Kokoro voices (American and British, female and male) with a play button that speaks a short sample of each. The chosen voice is saved with the other web settings and used for playback and prepared Library audio. The original engine choices (local fallback, remote Kokoro, Chatterbox, OpenAI) sit under "Original engine options" at the bottom of the same menu.
Doc Reader cleans Markdown/code-heavy text and splits long passages before they reach the neural TTS sidecars. Chatterbox is still available as a selectable voice, but the default app path favors Kokoro for steadier document playback.
Set up the optional remote speech service on a Tailscale-connected machine:
read-docs tts-umbra-install
read-docs tts-umbra-statusSet up the Mac-local speech service:
read-docs tts-mac-start
read-docs tts-mac-statusRun a benchmark and generate sample files:
read-docs tts-benchBenchmark reports and sample audio are saved under
~/.doc-reader-managed/tts-benchmarks/.
Doc Reader uses Mac-local speech-to-text for dictation by default. It can also use an optional remote speech sidecar when you configure one. Neither path calls a hosted speech API.
Set up the Mac-local sidecar:
read-docs tts-mac-start
read-docs restartSet up the optional remote sidecar:
read-docs tts-umbra-install
read-docs tts-umbra-start
read-docs restartThe Mac sidecar installs Kokoro plus faster-whisper and starts Whisper with a
CPU-friendly default model. Override it before read-docs tts-mac-start when
you want a larger local model:
export DOC_READER_MAC_STT_MODEL=smallOpen the canonical web app and confirm speech-to-text is on. Add an
audio file with the Audio picker to transcribe it into a Dictation card. Check
Timestamps before choosing the file when you want phrase-level timestamp lines
saved with the transcript. Choose the microphone from the Dictation settings if
the system default is not the input you want. Put the cursor in a text field,
then hold the Option/Alt key to record from the selected Mac microphone. Doc
Reader shows a small recording HUD while the key is held, sends the audio to the
active speech-to-text sidecar when the key is released, inserts the transcription at
the cursor, and adds the transcription as a Dictation card in the web app.
When a Logitech headset or mic is attached, Doc Reader pins it as the preferred
dictation input instead of drifting back to macOS System Default during device
refreshes. Set DOC_READER_DEFAULT_MICROPHONE_MATCH to a comma-separated list
of name/id tokens to prefer a different dedicated microphone.
The web app keeps read-aloud cards, dictation cards, and external app readings in one Library. Filter buttons narrow the Library to Readings, Dictations, or Clawdad-origin items. Dictation cards have copy and edit icon buttons; edits save back to the Library card text, update search snippets and word metrics, and copy still briefly switches the button to a green checkmark. The Read Speed slider stores a per-app WPM setting and applies it to new read-aloud playback plus already-running readings as each next chunk is prepared. Playback is spoken in short live segments so speed changes are picked up after the current segment, including on the default Mac Kokoro path. Library retention is unbounded: read-aloud cards, prepared TTS audio metadata, STT dictation cards, and saved dictation recording files are retained until you remove them from the managed app data.
The Library also tracks a local Signal Map. It counts words separately for STT
dictations and TTS/read-aloud material, records completion state for readings,
and writes batch semantic analysis to
~/.doc-reader-managed/library-analysis.json plus per-batch JSON files under
~/.doc-reader-managed/library-analysis-batches/. The analyzer runs
periodically while the web app is open and can be triggered from the Signal Map
button. By default it tries a configured local model endpoint through Ollama,
then falls back to deterministic local
structure if no model is reachable. Configure it with:
export DOC_READER_ANALYSIS_BACKEND=ollama
export DOC_READER_ANALYSIS_URL=http://100.72.151.28:11434
export DOC_READER_ANALYSIS_MODEL=llama3.1:8bThe Mac-local speech sidecar preloads its speech-to-text model when possible so
the first real dictation does not pay the model-load cost. Short warm dictations
should return quickly. macOS
may ask for microphone permission the first time the native app records audio.
The web app shows the selected input device, microphone authorization,
Accessibility, and Input Monitoring state. It also shows whether the native app
helper is online. Use the in-app Start Helper, Stop Helper, and Reset
controls to bring the menu-bar hotkey listener up, shut it down, or restart a
stale helper without leaving the web app. If the HUD does not appear while Doc
Reader is in the background, allow Doc Reader.app in macOS Privacy & Security
for Input Monitoring.
Accessibility is also required for automatic insertion into the active text
field; without it, Doc Reader copies the transcription to the clipboard.
- Create and activate a virtual environment.
- Install dependencies.
- Run the reader.
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python -m doc_reader /path/to/file.pdf --mode smart --style balanced --verboseThis repository is configured for the public npm package read-docs.
The unscoped doc-reader package name is already owned by another maintainer, so the
official SproutSeeds package uses the available global npm name read-docs.
The npm package is a bootstrapper and control surface for the managed app. Running
read-docs with no arguments shows the available app and CLI commands; it does not
directly run the Python tray from the package install location.
Install globally:
npm install -g read-docsPass a document path to use the command-line reader instead of the menu-bar app:
read-docs /path/to/file.pdf --mode smart --style balanced --verboseFrom the repo root:
./run-doc-readerThis command will:
- Create
.venvif needed - Install/update dependencies when
requirements.txtchanges - Launch the menu-bar app directly from the source checkout
Optional fallback TTS engine:
./.venv/bin/python -m pip install pyttsx3The default app path uses Mac-local speech. OpenAI remains available as an
explicit remote speech backend for users who choose it. When
--speech-backend openai is selected, the app checks OPENAI_API_KEY,
Doc Reader's macOS Keychain item, and ORP's local openai-primary Keychain
secret.
CLI with explicit backend selection:
./run-doc-reader \
--speech-backend openai \
--openai-model gpt-4o-mini-tts \
--openai-voice marin \
--openai-response-format wavApp usage:
- Open the panel from the menu bar icon.
- Choose a document, read clipboard text, or paste text into the reader window.
- Use the Library cards to pause, resume, copy dictation text, and switch between saved readings.
- Choose Mac Kokoro, Remote Kokoro, Remote Chatterbox, OpenAI API, or system speech.
- Store an OpenAI key in the macOS Keychain or ORP secrets only when using the optional remote backend.
- Click
Stop Readingfrom the menu bar item to stop active playback.
The supported app path is:
read-docs installFor local development, you can also run the Python menu-bar module directly:
python -m doc_reader.trayWhat it gives you:
- Native menu-bar app shell (
Doc Reader.app) with a formal app icon that opens the canonical web page. - Applications/Dock launcher at
~/Applications/Doc Reader.app. - Tailnet web app through
read-docs tailscale. Open DocReader Pageopenshttp://127.0.0.1:8766.Read Clipboard in DocReaderposts clipboard text into the web Library.- Pause/resume and stop controls call the web app.
- Persistent Library cards for documents, pasted text, clipboard text, highlighted text, dictation, and external app handoffs.
- Option/Alt hold-to-record dictation with microphone selection, Mac speech-to-text, active-field insertion, and copyable Dictation cards.
- Highlighted-text readback through Right Command, Command-L, or the right-click Services item.
- Signal Map metrics and batch analysis for local reading and dictation material.
- Web settings for Mac-local, optional remote speech, optional OpenAI API, and system speech.
- OpenAI API keys are loaded only when OpenAI API is explicitly selected.
- Right-click Services integration sends highlighted text into the web app.
The older PySide tray module remains in the source tree as a development fallback, but the npm app path uses the native macOS wrapper.
The native macOS helper can read highlighted text from the keyboard. Highlight
text in any app and tap the right Command key; Doc Reader copies the selection,
restores your clipboard, creates a reading card in the web app, and starts
playback through the selected TTS backend. Control+Command+R is also accepted
as a fallback.
Install a native Services entry as a fallback so highlighted text can also be
read from right-click menus:
read-docs installIf the app agent is already installed and you only need to refresh the Services
entry, run read-docs install-service.
Then in any app:
- Highlight text.
- Right click.
- Choose
Services -> Read with Doc Reader.
The Services flow uses macOS text input directly. If an app invokes the Service
but passes empty or partial text, the helper makes a clipboard-preserving copy
attempt and logs the selected-text handoff count to ~/Library/Logs/doc-reader-service.log.
Remove it later with:
read-docs uninstall-serviceThe app agent is registered by:
read-docs installThis installs a managed app copy at ~/.doc-reader-managed and registers a
LaunchAgent. Run read-docs restart after package updates to refresh that managed
copy and restart the app.
Disable later:
read-docs disable-startup--mode smart: Speaks key ideas from each chunk (default).--mode full: Speaks cleaned source text.
--style concise: very short key points--style balanced: moderate detail (default)--style detailed: more context per chunk
Pipeline architecture:
- Extract text progressively from the input file.
- Chunk text into early-small then steady-sized segments.
- Prepare speech-ready narration for each chunk.
- Queue prepared chunks while the current chunk is being spoken.
The first chunk target is smaller (--first-chunk-words) so audio starts quickly; later chunks use --chunk-words for steadier flow.
python -m doc_reader file.docx \
--mode smart \
--style detailed \
--speech-backend auto \
--rate 190 \
--voice Samantha \
--first-chunk-words 95 \
--chunk-words 240 \
--queue-size 10Dry-run without speaking:
python -m doc_reader notes.md --dry-run --verbose.doc(legacy Word) is not supported yet; convert to.docxfirst.- PDF quality depends on extractable text in the file (scanned PDFs need OCR first).
Contributions are welcome. Please see CONTRIBUTING.md for setup, PR guidelines, and security reporting.