Audio Feedback — Accessible Audio Cues for Claude Code
The audio-feedback plugin provides non-speech audio cues for Claude Code hook events, supporting awareness of Claude's activity without needing to watch the terminal. Short synth sounds fire on response complete, notifications, context compaction, user input, tool calls, subagent completion, session start, and pre-compact. Each event maps to a configurable sound (or off) and is organised by theme. Independent of the tts plugin (purely non-speech).
First Step
When invoked with no arguments, the FIRST action must be a single AskUserQuestion tool call (no preamble):
"What would you like to do with audio-feedback?"
Set header: "Action" and offer: config, sounds, test, help.
If the user's message already includes a subcommand, skip the AUQ and jump into the matching workflow.
Helper Commands
audio-feedback-config [KEY=VALUE ...]— view or update config
Invoke as a bare command — bin/ is on PATH automatically.
Configuration Reference
Global config: ~/.claude/.audio-feedback-config
Core
| Key | Default | Purpose |
|---|---|---|
ENABLED |
true |
Master switch — all sounds off when false |
THEME |
default |
Sound pack (subdirectory under sounds/) |
STOP_SOUND |
stop |
Response complete |
NOTIFICATION_SOUND |
notification |
Cron/alert fired |
PRE_COMPACT_SOUND |
pre-compact |
Context about to compact |
USER_PROMPT_SOUND |
user-prompt-submit |
Input acknowledged |
SESSION_START_SOUND |
off |
Session began |
SUBAGENT_STOP_SOUND |
off |
Subagent finished |
PRE_TOOL_USE_SOUND |
off |
Before tool call |
POST_TOOL_USE_SOUND |
off |
After tool call |
DAEMON_ENABLED |
true |
Use the resident playback daemon when available |
DAEMON_IDLE_TIMEOUT |
30 |
Seconds of inactivity before the daemon self-exits |
DAEMON_MAX_VOICES |
8 |
Max concurrent mixed voices in the daemon |
VOLUME |
1.0 |
Playback level, 0.0–1.0 (linear); scales daemon + paplay output |
SUBAGENT_ACCENT |
true |
For a tool run inside a subagent, play the sound's -subagent background variant (extra reverb + low-pass) instead of the plain sound |
Set any event to off to silence it. Set ENABLED=false to silence everything.
Subtype-specific sounds (e.g. notification-permission.wav, post-tool-use-observe.wav) are auto-resolved from the theme directory when present, falling back to the generic sound above.
Bundled Sounds — Default Theme
Lo-fi minimal aesthetic, 0.5s reverb tail, normalised to 0dB peak, mono 44.1 kHz.
Generic event sounds
| File | Event | Character | Duration |
|---|---|---|---|
stop.wav |
Stop | Descending G4–C4 fifth, settling | 1.0s |
notification.wav |
Notification | Ascending A4–C5, attention | 0.9s |
pre-compact.wav |
PreCompact | Low G3 tone, warning | 1.1s |
user-prompt-submit.wav |
UserPromptSubmit | Tiny 600Hz click | 0.55s |
session-start.wav |
SessionStart | Ascending C4–E4–G4 arpeggio | 0.94s |
subagent-stop.wav |
SubagentStop | Double ping E5 | 0.66s |
pre-tool-use.wav |
PreToolUse | Barely-there 500Hz tick | 0.53s |
post-tool-use.wav |
PostToolUse | Short 700Hz tick | 0.54s |
Subtype-aware variants
The hook inspects the JSON payload and, when a subtype-specific WAV exists, prefers it over the generic sound. Lets the user distinguish kinds of events by ear — core to the accessibility purpose of the plugin.
| Event | Subtype source | Variant files |
|---|---|---|
| Notification | notification_type |
notification-permission.wav, notification-idle.wav, notification-auth.wav, notification-elicitation.wav |
| SessionStart | source |
session-start-resume.wav, session-start-compact.wav, session-start-clear.wav |
| PreToolUse / PostToolUse | tool_name → tool group |
pre-tool-use-{execute,observe,modify,network,dispatch,interact}.wav (and post-tool-use-* mirror) |
Tool-group mapping (so one file serves a family of tools):
| Group | Tools |
|---|---|
execute |
Bash |
observe |
Read, Glob, Grep |
modify |
Write, Edit, NotebookEdit |
network |
WebFetch, WebSearch |
dispatch |
Agent |
interact |
AskUserQuestion, ExitPlanMode |
Any WAV file not listed is a silent no-op if missing from the active theme — themes don't need to ship a complete set.
Workflow: CONFIG
Two modes:
- No args — run
audio-feedback-configto show current config. Pass output through. - With KEY=VALUE — run
audio-feedback-config KEY=VALUE [...]to update. Validates sound names against bundled files.
Workflow: SOUNDS
Run audio-feedback-config (no args) — its footer lists sounds in the active theme and all available themes.
Workflow: TEST
Play each configured sound that isn't off in sequence so the user can hear them. Resolves the active theme directory so the test works regardless of THEME.
cfg=$(audio-feedback-config 2>/dev/null)
plugin_root=$(dirname "$(dirname "$(readlink -f "$(which audio-feedback-config)")")")
theme=$(printf '%s' "$cfg" | awk -F= '/^ THEME=/{print $2}')
sounds_dir="$plugin_root/sound-theme/${theme:-default}/sounds"
for key in STOP_SOUND NOTIFICATION_SOUND PRE_COMPACT_SOUND USER_PROMPT_SOUND \
SESSION_START_SOUND SUBAGENT_STOP_SOUND PRE_TOOL_USE_SOUND POST_TOOL_USE_SOUND; do
sound=$(printf '%s' "$cfg" | awk -F= -v k=" $key" '$1==k{print $2}')
[ -z "$sound" ] || [ "$sound" = "off" ] && { echo "$key: $sound"; continue; }
echo "$key: $sound"
paplay "$sounds_dir/${sound}.wav" 2>/dev/null || echo " [missing: $sounds_dir/${sound}.wav]"
done
Show which event→sound mappings played and which were off or missing.
Workflow: HELP
Print the subcommand grammar, config reference table, and bundled sounds table inline. No helper script needed.
Subcommand Grammar
/audio-feedback → interactive (AUQ first)
/audio-feedback config [KEY=VALUE] → view or update config
/audio-feedback sounds → list available sounds
/audio-feedback test → play all active sounds in sequence
/audio-feedback help → grammar + config + sounds reference
Important Notes
- Coexistence with tts plugin: If tts is also installed with CHIME_ENABLED=true, both plugins fire on Stop — double chime. Disable one: either
/tts config CHIME_ENABLED=falseor/audio-feedback config STOP_SOUND=off. - PreToolUse and PostToolUse default to
offbecause they fire on EVERY tool call (high frequency, can be annoying during heavy tool use). - When a hook's JSON payload carries
agent_id(i.e. the tool call was made on behalf of a subagent), the sound's<name>-subagent.wavbackground variant (extra reverb + low-pass, "heard from another room") plays instead of the plain sound, ifSUBAGENT_ACCENT=true(default). - Hooks are non-blocking: the event sound runs in a detached background process, so the hook script returns in ~50ms regardless of paplay latency.
- Requires PipeWire (
paplay). The optional playback daemon (af-soundd) needsuv(which supplies sounddevice/soundfile/numpy via PEP 723) and collapses concurrent playback into one process / one PipeWire client; without uv, each event uses paplay. Subtype resolution needsjq.