How to make Claude Code play a sound or speak when it finishes.
Two hooks in ~/.claude/settings.json do it: Stop for “done” and Notification for “needs you”. Copy the config for your OS, then choose between a chime, a spoken sentence, or a voice layer.
Claude Code runs shell commands on lifecycle events through hooks. The Stop event fires when Claude finishes responding, so it is the reliable “task finished” signal. If the turn ends on an API error, StopFailure fires instead. The Notification event fires when Claude is waiting on you, for example a permission prompt you have not answered for about six seconds. Point each one at a sound command in ~/.claude/settings.json: afplay on macOS, paplay on Linux, or PowerShell on Windows.
To make Claude Code speak instead of chime, pipe the Stop hook’s last_assistant_message field into a text-to-speech command such as say. The Claude Code CLI has voice dictation for input but no built-in voice output, and requests for a native completion chime were closed as not planned (#25267, #45014), so hooks are the supported path. For spoken summaries across several sessions, use a voice layer such as Heard.
Two hooks cover it: Notification for “needs you”, Stop for “done”.
Every option
Five ways to hear Claude Code, from a bell to a voice layer.
Start with the lightest option that solves your problem. The first three are built from Claude Code settings and your operating system, with nothing to install.
Option
What you hear
Setup
Platforms
Cost
Terminal bellpreferredNotifChannel
A bell, or a desktop banner in Ghostty, Kitty and iTerm2
One setting
Any terminal
Free
Sound hookafplay · paplay · PowerShell
Distinct “done” and “needs you” tones
Two hooks, five minutes
macOS, Linux, Windows
Free
Speech hooksay · spd-say
Claude’s final message, read word for word
One hook plus jq
macOS, Linux, Windows
Free
Plugins and extensionsClaude Notifier · Agent Ping
Sound packs, banners, or read-aloud
One install command
Varies by tool
Free, open source
HeardVoice layer
Short spoken summaries instead of raw text; answer back on Power
Heard is our product. The other four options are free and work today.
(1)Step 1 · macOS sound
Play a sound when Claude finishes or needs you.
Add both hooks to ~/.claude/settings.json, or to .claude/settings.json inside a repo to scope them to that project. The permission_prompt matcher limits the Notification hook to permission requests, so the “needs you” tone always means you have a decision to make. Leave the matcher out and the hook runs for every notification type, including idle_prompt.
In a terminal session, Notification waits until you seem to be away: permission_prompt plays once you have not typed for about six seconds, and each keystroke pushes it back. So use Stop as your completion signal: it fires at the end of every turn. Any file in /System/Library/Sounds works. Glass and Funk are different enough to tell apart without looking.
Every Stop hook receives a JSON payload on stdin, and its last_assistant_message field holds the text of Claude’s final reply. Pipe it through jq into a text-to-speech command and Claude Code reads its own result aloud. The field and command match the Stop input in the hooks reference (checked October 2026).
Cap the length. A final message can be a long report, and say reads every word, including file paths and code. 300 characters is usually one or two sentences. "async": true lets Claude Code carry on while the voice is still speaking.
The hook shape is identical on every platform; only the commands change. On Linux, paplay plays the freedesktop sounds through PulseAudio or PipeWire, and spd-say speaks text. On Windows, PowerShell plays a WAV with System.Media.SoundPlayer and speaks with System.Speech.
Run each command once in your own shell before relying on it, on every platform. Sound files and speech packages vary by macOS version, Linux distribution and Windows edition.
Check each link in the chain once, in this order, so you know which part to fix if something stays quiet.
Run the sound command on its own, for example afplay /System/Library/Sounds/Glass.aiff. No sound here means the problem is your audio setup, not Claude Code.
Start Claude Code and type /hooks. Your Stop and Notification hooks should be listed under their events.
Ask Claude to do something that needs approval, such as running a shell command you have not allowed. Switch to another window and stop typing. After about six seconds the “needs you” tone plays.
Approve it and let the turn finish. The “done” tone plays when Claude stops responding.
(5)When the turn fails
Play a different sound when the turn fails.
Stop does not fire when you interrupt Claude, and a turn that ends on an API error, such as a rate limit, an overloaded server or a login problem, fires StopFailure instead. Without a hook for it, a failed turn is silent and you come back expecting finished work.
Give StopFailure its own sound, distinct from “done”. Claude Code ignores this hook’s output and exit code, so it can only make a noise or log something; it cannot retry the turn. Its payload has an error field (rate_limit, server_error, authentication_failed and others) that you can use as a matcher if you only care about some errors.
Each event fires at a different moment, so pick the one that matches what you want to hear. Timings are from the hooks reference (checked October 2026).
If six seconds is too slow for approvals, a PermissionRequest hook fires the moment Claude asks. Keep it to a sound with "async": true, as in the example below. Claude Code does not run it for a sandboxed command’s network request; permission_prompt still covers that.
Claude finishes responding (not when you interrupt it)Stop
The turn ends on an API error, instead of StopStopFailure
The moment Claude asks to use a toolPermissionRequest
A permission prompt waits about 6 seconds; in a terminal, typing resets the waitpermission_prompt
About 60 seconds after Claude finishes, if you have not typedidle_prompt
A background session finishes or fails, while agent view is openagent_completed
Two distinct sounds are the right default. If you run long or parallel sessions, tighten the rules so every sound still means something.
Leave SubagentStop unhooked. It fires every time a subagent finishes, which is constant noise during a large task.
Speak the folder name so you know which session finished: every payload includes cwd, so jq -r .cwd | xargs basename | say works.
Want a different signal for approvals, questions and idle prompts? Split the Notification hook by type, as in the needs-input guide.
Want no hooks at all? Set "preferredNotifChannel": "terminal_bell" for a plain bell in any terminal (docs).
(8)Troubleshooting
If the hook stays silent.
Most silent hooks come down to one of these causes.
Run the command by itself first. If afplay /System/Library/Sounds/Glass.aiff is silent in your terminal, fix the output device or volume before touching hooks.
Run /hooks inside Claude Code to see what loaded. Claude Code watches settings files and normally picks up hook edits on its own, so you do not need to restart; a JSON syntax error is the usual cause.
Silent after an error? Stop does not fire on API errors or when you interrupt the turn. Add a StopFailure hook as shown above.
In the VS Code extension and Claude Desktop, hooks come from the same ~/.claude/settings.json as the CLI. Since Claude Code v2.1.233, permission_prompt fires there about six seconds after Claude asks, unless you answer first; before that it did not fire at all. Set CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKS=1 to turn it off there (docs).
Over SSH, hooks run on the remote machine, so afplay plays on the server. Claude Code’s built-in desktop notification does reach your laptop in Ghostty, Kitty and iTerm2.
Inside tmux, add set -g allow-passthrough on to ~/.tmux.conf so notifications reach the outer terminal.
On Windows, use PlaySync() as in the example above: it waits for the sound to finish. Play() returns at once and PowerShell exits before you hear anything.
On Linux, paplay plays WAV and OGG. For MP3, use mpg123 or ffplay -nodisp -autoexit instead.
In Apple Terminal, /terminal-setup turns the audible bell off. Turn it back on under Settings → Profiles → Advanced if you rely on terminal_bell.
(9)Beyond a chime
When a chime is not enough: a voice layer.
A chime tells you that something happened. say tells you what Claude wrote, word for word. Neither tells you which of four sessions just stopped, or whether it stopped because tests failed or because the work is done.
Heard is a voice layer for coding agents on macOS. It connects to Claude Code and Codex through native adapters, decides what deserves airtime (questions, blockers, failures, results), and says it in a sentence or two. With several sessions running, it batches routine activity into short summaries that start with the project name. On Power you can answer back by voice or from your iPhone. The engine is open source under Apache 2.0 with a local Kokoro voice, and every new account starts with a 14-day Power trial, no card required.
Useful
“Payments session: three snapshot tests failed. Want me to update the snapshots?”
Noise
“src/payments/__tests__/checkout.test.ts, line 42, expected 1999, received 2099, at Object.toEqual…”
Ready-made
Tools that set this up for you.
Prefer not to edit JSON? These open-source projects install the hooks for you. Star counts and activity checked October 2026.
A VS Code notifier with distinct sounds for done and needs-you, a minimum task length so short turns stay quiet, auto-mute while the window is focused, and remote audio so sounds play on your laptop when Claude runs over SSH or WSL. Also covers Codex.
A VS Code extension that can speak a fixed line, “Claude finished the task” or “Claude needs your input”, plus a sound and a toast, toggled from the status bar.
A Claude Code plugin that plays sounds and shows OS notifications when Claude stops, needs permission, or hits an error. Install it with /plugin marketplace add DavidWilsby/agent-ping, then /plugin install agent-ping.
Summarized spoken updates for every Claude Code and Codex session, with voice replies and an iPhone companion on Power.
Best for
Several sessions at once
Surface
macOS app
Agent support
Claude Code, Codex CLI, cloud agents via MCP
Tradeoff
macOS only; voice replies need Power.
Pick one
Which approach should you choose?
Start with the lightest option that fits how you work. You can always add speech or a voice layer later.
(1)
One session, you just want a ping
Two sound hooks
A Stop sound for done and a Notification sound for needs you. Local, free, nothing to maintain.
(2)
You want to hear what happened
A speech hook
Pipe last_assistant_message into say or spd-say, capped at a sentence or two.
(3)
You live in VS Code or work over SSH
An extension
Claude Notifier manages the hooks for you and plays sounds on your laptop even when Claude runs remotely.
(4)
Several sessions, or you want to answer back
A voice layer
Heard summarizes each session, batches routine work, and on Power lets you reply by voice or from your iPhone.
Questions
Frequently asked questions
Does Claude Code have a built-in completion sound?
Not a custom one. Claude Code sends a desktop notification in Ghostty, Kitty and iTerm2, and can ring the terminal bell elsewhere with "preferredNotifChannel": "terminal_bell". Requests for a native chime were closed as not planned (#25267, #45014), and the suggested route is preferredNotifChannel or hooks (#1288).
Can Claude Code read its responses aloud?
Not in the CLI. /voice is dictation: you speak and Claude Code types. For voice output, pipe the Stop hook’s last_assistant_message into say or spd-say, install a read-aloud tool, or use Heard for summarized narration.
Which hook fires when Claude Code finishes a task?
Stop. It fires when Claude finishes responding. Notification fires when Claude is waiting on you, and in a terminal only after you stop typing for a while, so it is not a reliable completion signal by itself.
Why did my Stop hook stay silent?
Stop does not fire when you interrupt Claude, and when a turn ends on an API error such as a rate limit, Claude Code fires StopFailure instead. Hook StopFailure with a different sound so a failed turn still reaches you.
How do I get a sound when Claude Code needs my approval?
The Notification hook with the permission_prompt matcher in Step 1 plays after about six seconds without a reply. For an alert the moment Claude asks, use the optional PermissionRequest hook under “When each event fires”. The needs-input guide covers every kind of “needs you” alert.
Do I need to restart Claude Code after editing hooks?
No. Claude Code watches its settings files and reloads hooks automatically. Run /hooks to confirm they loaded.
Why is my Notification hook silent in VS Code?
Check your Claude Code version. Before v2.1.233, permission_prompt did not fire in the VS Code extension or Claude Desktop (#26925). Since then it fires about six seconds after Claude asks for permission, unless you answer first, and CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKS=1 turns it off. The extension reads hooks from the same ~/.claude/settings.json as the CLI (docs).
How do I tell which session finished when I run several?
Every hook payload includes cwd, so a hook can speak the project folder name. If you run background sessions from agent view, a Notification hook with the agent_completed matcher fires when one finishes or fails, while agent view is open. Past two or three sessions, a voice layer like Heard summarizes routine work per project instead of reading raw text.