How to Make Claude Code Ding on Approval — Sounds, Hooks & Traffic Lights
How-to: make Claude Code play a sound or show a traffic light when it needs approval and when tasks finish. terminal_bell, Notification hooks, and DIY physical status lights from r/ClaudeCode.
You started a long Claude Code run, tabbed away, and came back to find Claude blocked on "Allow this Bash command?" — or sitting idle after finishing, with no idea anything happened. You do not need to watch the terminal the whole time.
Claude Code can ding when it needs approval, chime when a task completes, or — if you have seen the viral r/ClaudeCode builds — show a physical traffic light on the edge of your monitor. Same hook system, different output: ears or eyes.
This is a step-by-step how-to for audio alerts first, then traffic-light status lights for developers who want glanceable feedback during long agent sessions.
Update — June 25, 2026 (v2.1.191): Long unattended sessions benefit from ~37% lower streaming CPU and scroll that no longer jumps to the bottom mid-stream — less jitter when you glance back at the terminal. Comma-separated hook matchers (used by some traffic-light scripts) also work again after the 2.1.191 fix. Changelog: Claude Code 2.1.191.
Claude wants you to approve a tool call (Bash, Write, etc.)
Task finished
idle_prompt
Claude stopped working and is waiting for your next message
You can use one sound for everything (quick setup) or different sounds so your ears know which is which (recommended).
Option A — Fastest: one ding for everything
Step 1. Run this once in any terminal:
bash
claude config set --global preferredNotifChannel terminal_bell
Step 2. Enable the audible bell in your terminal:
Terminal
Where to enable
macOS Terminal
Preferences → Profiles → Advanced → Audible bell ✓
VS Code
Settings → terminal.integrated.enableBell: true
iTerm2
Use claude config set --global preferredNotifChannel iterm2 instead — iTerm2 uses desktop alerts, not the ASCII bell
Step 3. Test it. Start Claude Code, ask it to run a command that triggers a permission prompt, or wait for a long task to finish. You should hear a bell.
Done. This covers approval and task complete with the same sound.
Option B — Recommended: different sounds for approval vs done
This is the setup most r/ClaudeCode users actually want: urgent sound when approval is needed, softer chime when the task is done.
Step 1. Open (or create) your user settings file:
bash
mkdir -p ~/.claude
nano ~/.claude/settings.json
Step 2. Paste this block. If the file already has content (e.g. existing hooks), merge the "hooks" key — do not delete your other hooks.
Popular Reddit config — pops a macOS alert when Claude needs approval:
json
{"hooks":{"Notification":[{"matcher":"permission_prompt","hooks":[{"type":"command","command":"osascript -e 'display notification \"Claude needs your approval\" with title \"Claude Code\" sound name \"Glass\"'","timeout":10}]},{"matcher":"idle_prompt","hooks":[{"type":"command","command":"osascript -e 'display notification \"Task complete — Claude is waiting\" with title \"Claude Code\" sound name \"Ping\"'","timeout":10}]}]}}
Step 3. Verify inside Claude Code:
snippet
/hooks
You should see Notification registered with your commands.
Step 4. Test both events:
Approval: Ask Claude to run rm or any command that triggers a permission prompt.
Task done: Ask Claude to summarize a file — when it finishes and shows the prompt, you should hear Ping.
Option C — Linux and Windows
Linux
Replace afplay with notify-send — add -u critical for approval, -u low for task done:
The top r/ClaudeCode answer: ask Claude, not Reddit.
Open Claude Code and paste:
snippet
Edit ~/.claude/settings.json. I want:
1. A Glass system sound when you need my approval (permission_prompt)
2. A Ping system sound when you finish a task and wait for me (idle_prompt)
Use Notification hooks on macOS with afplay. Merge with any existing hooks.
Claude Code will edit settings.json for you. Run /hooks to confirm.
Option E — Traffic light status (physical or on-screen)
In June 2026, r/ClaudeCode and X exploded over a simple idea: mount a mini traffic light on your monitor and wire it to Claude Code's hook events. When the light turns red, Claude needs your confirmation. Yellow means it is busy executing. Green means the task finished — you can glance from the couch instead of alt-tabbing back to the terminal.
A DIY Claude Code traffic light on a developer's monitor — green indicates the agent finished and is idle. Photo via the r/ClaudeCode community; same concept @gxjo_dev and others popularized on X.
Traffic light color map
Community builds mostly agree on this mapping for DIY hardware (as @gxjo_dev and @tuakdotsol described on X):
Some macOS apps use a different mapping (e.g. Lights uses red = executing, yellow = permission). Always match hooks to your project's README.
The physical gadget works because Claude Code hooks already expose every state transition. Sound notifications use Notification events only; traffic lights usually wire PreToolUse, PermissionRequest, Stop, and Notification together for three distinct colors.
Ready-made projects (copy their hook configs)
You do not need to 3D-print anything on day one. These open-source projects ship hook templates:
Lights is the closest to "integrated with the OS" — a macOS menu-bar app on localhost:9876. Its color map differs from DIY hardware (red = executing, yellow = permission, green = idle). Hooks call:
bash
curl localhost:9876/executing # → red (model/tools running)
curl localhost:9876/permission # → yellow (needs your input)
curl localhost:9876/idle # → green (response complete)
Install the app, run Setup Hooks from the menu, and Claude Code writes the curl commands into your settings.
DIY physical light — minimal hook sketch
If you built or bought a USB traffic light (the r/ClaudeCode photo style), point each hook at a script that sets GPIO or sends a serial command. Example pattern from claude-traffic-light-control:
set-light.sh maps WAITING_USER → red, THINKING → yellow blink, TASK_COMPLETE → green. Ask Claude Code to generate the script for your hardware.
Multiple Claude sessions?
@tobiastornros asked the obvious question on X: what if you have multiple sessions? Desktop widgets like claude-code-traffic-lightaggregate all sessions and show the highest-priority state (red beats yellow beats green). Physical single-lamp setups usually show whichever session last fired an event — or you run one light per machine, not per tab.
Traffic light + sound together
Best unattended setup: Option B sounds for audio when you are in another room, plus Option E widget for visual status when you are at the desk. Both read from the same ~/.claude/settings.json hook block — add afplay and curl in parallel hook commands, or use separate hook entries for the same event.
Prompt to paste into Claude Code:
snippet
Add Notification hooks: Glass sound on permission_prompt, Ping on idle_prompt.
Also add PreToolUse hook calling curl localhost:9876/executing and Stop hook
calling curl localhost:9876/idle for the Lights macOS app.
Merge into ~/.claude/settings.json.
All notification matchers (reference)
Use these in the "matcher" field if you want to go beyond approval + task done:
Matcher
When it fires
permission_prompt
Approve a tool call
idle_prompt
Task done, waiting for you
auth_success
Login succeeded
elicitation_dialog
Interactive dialog open
elicitation_complete
Dialog closed
"" (empty string)
Everything — same sound for all events
Bonus setups
Bell + hooks together
terminal_bell and Notification hooks both run — you may get double sounds. To use hooks only:
bash
claude config set --global preferredNotifChannel notifications_disabled
Then rely entirely on your permission_prompt / idle_prompt hooks.
Long agent runs — delayed reminder
If you walk away during a long loop, ask Claude to add a hook that plays a sound 10 seconds after a permission_prompt if you have not responded yet. Community pattern from r/ClaudeCode — Claude can generate the script.
Visual pane rings when any parallel session needs you
iTerm2 / Kitty / Ghostty native alerts
bash
claude config set --global preferredNotifChannel iterm2 # or kitty, ghostty
These terminals get desktop notifications by default with "auto" — no hooks required, but you cannot separate approval vs task-done sounds without hooks.
Troubleshooting
Problem
Fix
No sound at all
Enable audible bell; check VS Code enableBell
Hook not listed
Run /hooks; validate JSON at jsonlint.com
Same sound twice
Disable built-in channel: notifications_disabled
Only approval, no task-done sound
Add a second hook entry with idle_prompt matcher
Hook feels slow
Add "timeout": 10 to the hook object
Copy-paste checklist
☐ Choose Option A (one bell), Option B (two sounds), or Option E (traffic light)
Five minutes of setup means you can start a long agent run, make coffee, and hear a ding or see a green light when Claude needs you — instead of discovering a stalled session twenty minutes later. The traffic-light trend is the same insight as sound hooks: agentic coding works better when status crosses into the physical world, not just the terminal buffer.