[Feature Request] Native text-to-speech output for Claude Code responses
Description
Claude Code has /voice for speech-to-text input, but there's no native way to
have responses read aloud. This was previously requested in #2189 (auto-closed
due to inactivity, not resolved).
Use case
Claude Code's output is detailed and thorough — which is great — but it's often
too long and generated too fast for me to keep up with by reading. I'm a slow
reader and don't have the patience to scroll through walls of text after every
response. The information is valuable, I just can't absorb it visually at that
pace.
I'm used to listening to audiobooks at 2x speed. If Claude Code could read its
responses aloud with configurable playback speed, I could effortlessly absorb
all that detail while continuing to work. I'd actually want the verbose output
instead of skimming past it.
Important: I don't need voice input. I love typing my instructions — I'm
usually surrounded by people when I work, so speaking to Claude Code isn't
practical. What I need is the opposite direction: text input, audio output.
Just let me type my request, put on my headphones, and listen to what Claude
did and why — without having to read a novel every time.
Session-level requirements (critical)
A native implementation must support per-session control, not just per-project.
This is the key gap that community tools cannot solve on their own today.
My actual workflow:
- I often run **multiple Claude Code terminal sessions in the same project
directory** (main work + a parallel verification session, for example)
- For some sessions I want narration (the main work I'm focused on)
- For others I explicitly don't want narration (verification sessions I
just want to watch pass/fail, quick lookup sessions, background tasks)
Community TTS tools (claude-speak, claudevoice-macos, claude-code-tts, etc.)
all hit the same architectural wall: Claude Code writes JSONL logs at the
project level (~/.claude/projects/<encoded-path>/), and there's no stable,
shell-visible way to tell which JSONL file belongs to which terminal session.
This forces these tools into per-project state (a single pause flag, one
monitor per project dir), which means toggling narration off for a verification
session also mutes the main work session I care about.
A native implementation has the information the community tools don't, and
should use it:
- Per-session TTS state (on/off, voice, speed) independent of other sessions
in the same project dir
- A
/speakslash command that affects only the current session - Ideally: a way to set narration preferences at
claudelaunch time, e.g.
claude --speak or claude --no-speak, so I can decide per-terminal without
typing a command after the session starts
Proposed implementation
Core narration
- A
/speaktoggle (and/or a--speak/--no-speaklaunch flag) that reads
responses aloud for this session only
- Configurable reading speed (1x, 1.5x, 2x, configurable down to 0.5x) —
critical for users who consume audio faster than text, like audiobook
listeners
- Use system TTS by default (
sayon macOS,espeakon Linux, SAPI on Windows),
with configurable voice
- Option for streaming TTS (read as tokens arrive, not wait for full response)
- Configurable via settings.json (default voice, default speed, default on/off)
- Session state overrides global defaults
- Sensible text cleaning for audio: strip code blocks, file paths, and raw
markdown syntax so the spoken version sounds natural (community tools already
do this well — the patterns are well-established)
Read-on-demand (separate from auto-narration)
- A keyboard shortcut to read the latest assistant message on demand,
following the same pattern as the existing /voice dictation shortcut.
/voice is already bound to voice:pushToTalk in the Chat context in
~/.claude/keybindings.json (default: Space, rebindable to modifier
combos like meta+k). A TTS read-last action should work the same way:
a named action like speak:readLast in the Chat context, with a sensible
default shortcut that doesn't conflict with voice:pushToTalk.
- This should work even when auto-narration is off — i.e. if I've left
narration disabled for a session but want to hear one specific response, I
press the shortcut and it speaks the most recent assistant message
- Rebindable via
~/.claude/keybindings.jsonusing the same schema as
existing Claude Code keybindings
- A companion slash command (e.g.
/speak last) for users who prefer commands
over shortcuts
Playback controls
All of these should be exposed as named actions in the Chat context, with
default keybindings that can be overridden in ~/.claude/keybindings.json —
the same mechanism already used for voice:pushToTalk.
- Pause / resume (
speak:togglePlayback) — pause current narration and
resume later
- Skip to next / previous response (
speak:nextMessage,
speak:previousMessage) — jump between assistant messages in the session
history
- Seek within the current response (
speak:seekForward,
speak:seekBackward) — move forward/backward inside the currently-playing
message. Think of it like a podcast player: you can scrub to a specific
point, jump back 10 seconds, or jump to a specific paragraph/section. This
matters for long responses — if I zone out or miss something, I need to go
back without replaying the whole thing
- A visual progress indicator while narration is playing (current position
within the response, total length) so I can see how much is left and where
I am
- All controls should be session-scoped and survive pause/resume
- All keybindings should default to values that don't conflict with
voice:pushToTalk or other existing Chat-context bindings
Why native matters
Community MCP plugins exist (VoiceMode, mcp-voice-hooks, claude-code-tts,
claude-speak, claudevoice-macos, talkito) but they all hit the same problems:
- Per-session control is not solvable from outside Claude Code. None of
them can reliably scope narration to a single terminal because Claude Code
doesn't expose a stable session identifier in a way external tools can hook
into. The best they can do is per-project state, which breaks for the
multi-session-in-same-repo case.
- On-demand reading of the latest message is not solvable cleanly either
for the same reason — external tools can't tell which JSONL file belongs to
the terminal you're actively looking at.
- Seek controls are impossible from outside. External tools stream text
to a TTS service and play audio. They have no hook for "rewind 10 seconds
in the currently-playing response" because they don't control the playback
pipeline end to end.
- Extra setup, extra dependencies — pip installs, shell scripts, PID files,
hook-file merges. A native integration would work out of the box.
- Version fragility — tools that watch internal JSONL formats or
settings.json
hook shapes break when Claude Code updates its internals.
A native integration would:
- Make verbose output accessible: Users who can't keep up reading would
finally benefit from Claude Code's detailed explanations
- Lower the barrier: works out of the box, like
/voicedoes for input - Complete the interaction loop: text input + audio output is a valid and
practical interaction mode, especially in shared/open office environments
- Enable multitasking: listen to explanations through headphones while
reviewing the actual code changes
- Actually support real workflows: per-session control and on-demand
reading make narration compatible with how people actually run Claude Code
(multiple terminals, same repo, varied attention levels per session)
The core problem
Claude Code gives excellent detail in its responses, but the only way to
consume it is by reading. For users who process audio faster than text — or who
simply can't read as fast as Claude generates — all that useful context goes to
waste. TTS output with speed control, on-demand triggering, and seek controls
would turn a frustration into a superpower.
The /voice feature assumes people want to talk to Claude. But some of us
want Claude to talk to us — while we keep typing. These are independent needs.
And crucially: the narration has to work per session, support on-demand
triggering, and provide seek controls — or it can't coexist with real
multi-terminal workflows and long-form responses.
Prior art
- #2189 (auto-closed, community interest existed)
/voicealready proves Anthropic values voice interaction- Multiple community plugins confirm demand — and confirm that the per-session
constraint, on-demand reading, and seek controls can only be solved natively:
- claude-speak — JSONL log
monitor, per-project state, free (Microsoft Neural voices)
- claudevoice-macos — hook-based
notifications, macOS-only
- VoiceMode — MCP, two-way voice
- mcp-voice-hooks —
MCP, browser TTS
- claude-code-tts — MCP,
OpenAI TTS
- talkito — multi-channel TTS wrapper
None of these solve the multi-session-same-project case, none provide true
on-demand reading of the current terminal's last message, and none expose
seek controls — the necessary primitives aren't available to them.
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗