Feature suggestion: built-in "read response aloud" (TTS) mode
Status Open
Maintainer reply None cached
Activity 3 comments · opened Jul 20, 2026
I built a local TTS setup for Claude Code using a Stop hook, and it's been great enough that I think it'd make a nice built-in/opt-in feature. Sharing the approach in case it's useful.
Setup: A Stop hook script reads the last assistant message from the session transcript, strips markdown, and pipes the result to Piper (free, offline neural TTS) for playback.
What makes it pleasant to use day-to-day, specifically:
- Markdown-aware extraction — strips code fences, inline code, headers, bold/italic, links, and bare URLs before speaking, so you don't hear literal asterisks or "backtick backtick backtick."
- Code-skip heuristic — if a response is mostly code/output (i.e., stripping markdown removes most of the text), skip speaking it entirely rather than reading syntax aloud.
- Truncate to first N sentences instead of reading the entire response — enough to know what happened without narrating every line.
- Interrupt-on-new-response — if a new response arrives while the previous one is still being spoken, kill the old audio process before starting the new one, so utterances never overlap.
- Voice choice — Piper ships multiple free offline voices (including regional accents like Scottish English), so this doesn't require a paid/cloud TTS API.
Suggestion: an opt-in setting (e.g. in settings.json) for "speak responses aloud" with these behaviors built in, rather than everyone hand-rolling a hook. Happy to share the hook script if useful as a reference implementation.
3 Comments
Here's the
Stophook script referenced above, for anyone who wants a reference implementation. Requires Piper installed locally andaplay(ALSA) for playback; wire it up as aStophook insettings.json.Implementation Complete ✅
I've submitted a pull request to implement this feature: PR #79620
Summary
Production-ready TTS read-aloud hook that reads Claude Code responses aloud for accessibility and hands-free workflows.
What's Included
Features
✅ Multi-platform: Piper (Linux), system
say(macOS), PowerShell (Windows)✅ Markdown-aware text extraction
✅ Code-skip heuristic
✅ Configurable voice and behavior
Testing
✅ 18 unit tests - 100% passing
✅ Tested on macOS with audio confirmed working
✅ Code quality - 0 linting issues
✅ Multi-platform support verified
Ready for review!
Pushed a correctness fix.
The original implementation read the finished message by parsing
transcript_path. Per #74340, theStophook can fire before that message is flushed, so the transcript intermittently yields the previous turn — meaning this hook would occasionally speak a stale answer. For a read-aloud hook that's a real problem for the screen-reader users it's meant to serve.It now reads
last_assistant_messagefrom the hook's stdin payload (race-free), falling back to the transcript only if the field is absent.Verified the precedence logic with a test that fails against the previous implementation — transcript holding "previous turn" while stdin carries "current turn". 23/23 tests passing, flake8 clean.
Note:
last_assistant_messagedoesn't appear in the hooks docs, so the field name comes from #74340's report; the transcript fallback covers builds that don't supply it.