audio-system
Manage atmospheric audio for breathe-together-v2. Add new sounds, configure breath synchronization, adjust mixing, and debug audio issues. Includes registry pattern, Tone.js integration, and breath-phase volume envelopes. Covers ambient drones, breath tones, nature soundscapes, and transition chimes.
When & Why to Use This Skill
The Audio System Skill is a specialized developer tool designed to manage and optimize atmospheric audio within web applications, specifically tailored for the breathe-together-v2 project. It leverages Tone.js and a registry-based architecture to create immersive, breath-synchronized soundscapes. This skill automates the process of registering new audio assets, configuring complex volume envelopes, and ensuring seamless transitions between breathing phases, significantly reducing the manual overhead of web audio engineering.
Use Cases
- Automated Sound Asset Integration: Quickly add and register new ambient drones, nature soundscapes, or transition chimes into the project's audio registry with proper pathing and metadata.
- Dynamic Breath Synchronization: Configure audio parameters to respond in real-time to user breathing cycles, such as opening filters during inhalation or decreasing volume during exhalation.
- Professional Audio Mixing & Balancing: Adjust base volumes and frequency allocations across multiple layers (ambient, breath, nature) to prevent clashing and ensure a harmonious user experience.
- Technical Audio Debugging: Identify and resolve common web audio issues including autoplay policy restrictions, pathing errors, and loop-point clicking through structured troubleshooting workflows.
- Complex Volume Envelope Design: Implement progress-following volume changes or phase-specific targets to create responsive and emotionally resonant audio feedback.
| name | audio-system |
|---|---|
| description | Manage atmospheric audio for breathe-together-v2. Add new sounds, configure breath synchronization, adjust mixing, and debug audio issues. Includes registry pattern, Tone.js integration, and breath-phase volume envelopes. Covers ambient drones, breath tones, nature soundscapes, and transition chimes. |
| allowed-tools | [Read, Write, Edit, Glob, Grep, Bash(npm:*)] |
Audio System Skill
Manage atmospheric audio that enhances the breathing meditation experience.
Design Intent & Goals
Core Philosophy
Audio in breathe-together-v2 is atmospheric, not instructional. The sounds should:
- Support, not lead - Audio enhances the visual breathing cues, never replaces them
- Breathe with the user - Volume, filters, and textures respond to breath phases
- Create presence - Ambient layers make the space feel alive and inhabited
- Remain optional - The app works perfectly without audio; it's an enhancement layer
Emotional Goals
| Phase | Feeling | Audio Response |
|---|---|---|
| Inhale | Expansion, hope, gathering | Volume rises, filters open, brightness increases |
| Hold-in | Stillness, presence, fullness | Stable, sustained, subtle shimmer |
| Exhale | Release, letting go, softening | Volume falls, filters close, warmth increases |
| Hold-out | Quiet, anticipation, emptiness | Near-silence, gentle presence, space |
Technical Goals
- Seamless loops - No audible click or gap when sounds repeat
- Smooth transitions - All parameter changes use ramping (no harsh cuts)
- Layered mixing - Multiple sounds blend without clashing
- Performance - Audio processing doesn't impact 60fps rendering
Mode Selection
Choose your workflow:
- Mode 1: Add a New Sound - Drop a file, register it, done
- Mode 2: Configure Breath Sync - Make sounds respond to breathing
- Mode 3: Adjust Mixing - Balance volume levels and frequencies
- Mode 4: Debug Audio Issues - Fix playback, sync, or mixing problems
Mode 1: Add a New Sound {#mode-1-add}
Adding a sound is a 2-step process.
Step 1: Drop the File
Place your audio file in the correct folder:
public/audio/
├── ambient/ # Continuous drones (loop forever)
├── breath/ # Phase-triggered tones (play once per phase)
├── nature/ # Soundscape layers (one active at a time)
├── chimes/ # Transition markers (optional, short one-shots)
└── ui/ # Interface feedback (button clicks, toggles)
File Requirements:
- Format: MP3 (compatibility) or OGG (quality)
- Sample rate: 44.1kHz
- Bit rate: 128-192kbps
- Channels: Stereo
- Peak level: -3dB (headroom for mixing)
- Loops: Must be seamless (no click at boundary)
Step 2: Register in Registry
Open src/audio/registry.ts and add an entry:
// ─────────────────────────────────────────────────────
// NATURE SOUNDSCAPES
// ─────────────────────────────────────────────────────
'nature/waterfall': {
path: '/audio/nature/waterfall.mp3',
category: 'nature',
loop: true,
baseVolume: -15, // dB (quieter = more negative)
fadeIn: 3, // seconds
fadeOut: 3, // seconds
breathSync: {
volumeMin: 0.7, // Volume during hold-out (quietest)
volumeMax: 1.0, // Volume during hold-in (loudest)
phaseVolumes: [0.85, 1.0, 0.85, 0.7], // Per-phase targets
},
},
Registry Field Reference
| Field | Type | Required | Description |
|---|---|---|---|
path |
string | Yes | URL path from public folder |
category |
string | Yes | ambient, breath, nature, chimes, ui |
loop |
boolean | No | True for continuous sounds |
baseVolume |
number | Yes | Base level in dB (-20 to 0) |
fadeIn |
number | Yes | Attack time in seconds |
fadeOut |
number | Yes | Release time in seconds |
triggerPhase |
number | No | 0-3, for phase-triggered sounds |
breathSync |
object | No | Breath synchronization config |
Done!
The audio system automatically loads and manages the new sound.
Mode 2: Configure Breath Sync {#mode-2-breath-sync}
Make sounds respond to the breathing cycle.
Option A: Phase-Triggered Sounds
Play a sound when entering a specific phase:
'breath/inhale': {
path: '/audio/breath/inhale.mp3',
category: 'breath',
triggerPhase: 0, // 0=inhale, 1=hold-in, 2=exhale, 3=hold-out
baseVolume: -6,
fadeIn: 0.3,
fadeOut: 0.5,
},
Option B: Progress-Following Volume
Volume follows easedProgress (0→1) within each phase:
'breath/inhale': {
// ...
breathSync: {
volumeMin: 0.3, // Start quiet
volumeMax: 1.0, // End loud
followProgress: true, // Volume = min + (max-min) * progress
},
},
Use for: Rising tones, falling tones, swells
Option C: Phase-Specific Volumes
Different target volume for each phase (with smooth transitions):
'nature/ocean': {
// ...
breathSync: {
volumeMin: 0.7,
volumeMax: 1.0,
followProgress: false,
phaseVolumes: [0.85, 1.0, 0.85, 0.7],
// ↑ ↑ ↑ ↑
// inhale hold-in exhale hold-out
},
},
Use for: Nature soundscapes, ambient drones
Breath Phase Reference
| Phase | Index | Duration | breathPhase | Feeling |
|---|---|---|---|---|
| Inhale | 0 | 3s | 0→1 | Rising, expanding |
| Hold-in | 1 | 5s | ~1 | Full, still |
| Exhale | 2 | 5s | 1→0 | Falling, releasing |
| Hold-out | 3 | 3s | ~0 | Empty, quiet |
Mode 3: Adjust Mixing {#mode-3-mixing}
Balance sounds so they work together pleasantly.
Volume Hierarchy
Follow this relative loudness:
Layer Base Volume During Breath Peak
─────────────────────────────────────────────────────────
Master -3dB -3dB
├─ Ambient Drones -12dB -12dB (constant)
├─ Breath Tones -18dB -6dB (swells)
├─ Nature Soundscape -18dB -12dB (breathes)
└─ Chimes -15dB -9dB (punctuates)
Frequency Allocation
Avoid sounds competing in the same frequency range:
Low Mid-Low Mid Mid-High High
60-200Hz 200-400Hz 400-1.5kHz 1.5-4kHz 4-8kHz
─────────────────────────────────────────────────────────────
Drones ████████████ ████████ (low)
Breath ████████████ ████████ (mid)
Nature ░░░░░░░░░░░░ ░░░░░░░░░░░░ ░░░░░░░░░░░░ ░░░░░░░░░░░░ (filtered)
Chimes ████████████ (high)
Mixing Rules
- Maximum 4 simultaneous layers - Master + 3 sound sources
- Only 1 nature soundscape at a time - Crossfade when switching
- Drones are constant - They don't respond to breathing (foundation)
- Breath tones peak during active phases - Inhale/exhale are loudest
- Nature sounds "breathe" - Volume modulates 70%→100%→70%
Adjusting Volume
Change baseVolume in registry (in dB):
| dB | Perception |
|---|---|
| 0 | Maximum (too loud) |
| -6 | Loud |
| -12 | Medium |
| -18 | Quiet |
| -24 | Very quiet |
Mode 4: Debug Audio Issues {#mode-4-debug}
Issue: Sound Not Playing
Check 1: Is the file in the correct folder?
ls public/audio/{category}/
Check 2: Is the path correct in registry?
// Path must match exactly (case-sensitive)
path: '/audio/nature/ocean.mp3', // Starts with /
Check 3: Is the category enabled?
// In AudioProvider state
ambientEnabled: true, // For 'ambient' category
breathEnabled: true, // For 'breath' category
natureSound: 'nature/ocean', // For 'nature' category (not null)
chimesEnabled: true, // For 'chimes' category
Check 4: Has user interacted?
Browser autoplay policy requires user interaction before audio.
The "Enable Audio" button must be clicked first.
Issue: Sound Too Quiet/Loud
Adjust baseVolume in registry:
baseVolume: -12, // Try -9 for louder, -15 for quieter
Issue: Sound Clicks at Loop Point
The audio file needs editing:
- Open in audio editor (Audacity, Logic, etc.)
- Find the loop point
- Apply crossfade at start/end
- Re-export with seamless loop
Issue: Breath Sync Not Working
Check 1: Does the sound have breathSync config?
breathSync: {
volumeMin: 0.7,
volumeMax: 1.0,
// ...
},
Check 2: Is the sound playing? (check console for errors)
Check 3: Is the volume range large enough?
// Too subtle (10% difference)
volumeMin: 0.9, volumeMax: 1.0
// Noticeable (30% difference)
volumeMin: 0.7, volumeMax: 1.0
Issue: Sounds Clashing
Two sounds are fighting in the same frequency range:
- Identify the clash - Listen for "muddy" or "harsh" mix
- Check frequency allocation - See chart above
- Reduce one sound's volume - Lower the less important layer
- Apply filtering - Add low-pass to nature sounds (cut highs)
File Structure
src/audio/
├── index.ts # Public API exports
├── registry.ts # ★ Sound definitions (ADD SOUNDS HERE)
├── types.ts # TypeScript interfaces
├── audioSystem.ts # ECS system (reads breath, controls playback)
├── AudioEngine.ts # Tone.js wrapper
└── AudioProvider.tsx # React context + useAudio hook
public/audio/
├── ambient/ # Continuous drones
├── breath/ # Phase-triggered tones
├── nature/ # Soundscape options
├── chimes/ # Transition markers
└── ui/ # Interface feedback
Quick Reference: Adding Common Sound Types
Ambient Drone (loops forever, constant volume)
'ambient/my-drone': {
path: '/audio/ambient/my-drone.mp3',
category: 'ambient',
loop: true,
baseVolume: -12,
fadeIn: 2,
fadeOut: 2,
},
Breath Tone (triggered per phase, volume follows progress)
'breath/my-inhale': {
path: '/audio/breath/my-inhale.mp3',
category: 'breath',
triggerPhase: 0,
baseVolume: -6,
fadeIn: 0.3,
fadeOut: 0.5,
breathSync: {
volumeMin: 0.3,
volumeMax: 1.0,
followProgress: true,
},
},
Nature Soundscape (loops, volume breathes with cycle)
'nature/my-nature': {
path: '/audio/nature/my-nature.mp3',
category: 'nature',
loop: true,
baseVolume: -15,
fadeIn: 3,
fadeOut: 3,
breathSync: {
volumeMin: 0.7,
volumeMax: 1.0,
phaseVolumes: [0.85, 1.0, 0.85, 0.7],
},
},
Transition Chime (one-shot, plays at phase boundary)
'chimes/my-bell': {
path: '/audio/chimes/my-bell.mp3',
category: 'chimes',
triggerPhase: 0,
baseVolume: -9,
fadeIn: 0,
fadeOut: 0,
},
Integration with Other Skills
Relationship to breathing-sync
Audio synchronization uses the same breath traits (breathPhase, phaseType, easedProgress) as visual synchronization. The patterns are analogous:
- Visual: Scale/opacity modulated by breathPhase
- Audio: Volume/filter modulated by breathPhase
Relationship to ecs-entity
The audio system integrates with ECS via audioSystem running after breathSystem in the pipeline. It queries breath traits the same way visual entities do.
Reference Materials
- 01-audio-architecture-plan.md - Architecture decisions
- 02-sound-asset-checklist.md - Sound file checklist
- 03-implementation-plan.md - Implementation phases
Troubleshooting Checklist
When audio isn't working:
- File exists in
public/audio/{category}/ - Path in registry matches exactly (with leading
/) - Category is enabled in AudioProvider state
- User has clicked "Enable Audio" button
- No console errors related to audio loading
-
baseVolumeis not too quiet (try -6 to test) - If breath-synced,
breathSyncconfig is present - Volume range is noticeable (min/max differ by >20%)
Let's make it sound beautiful! 🎵