{"id":46082592,"url":"https://github.com/ctoth/cacophony","last_synced_at":"2026-05-10T00:02:12.246Z","repository":{"id":189078170,"uuid":"679996860","full_name":"ctoth/cacophony","owner":"ctoth","description":"Powerful Typescript Webaudio library with built-in cache support","archived":false,"fork":false,"pushed_at":"2026-02-19T20:07:04.000Z","size":2520,"stargazers_count":7,"open_issues_count":1,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2026-02-19T22:24:38.522Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ctoth.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-08-18T05:27:02.000Z","updated_at":"2026-02-19T20:06:44.000Z","dependencies_parsed_at":"2024-02-02T01:28:01.734Z","dependency_job_id":"7a2e8b0d-2d8d-47cc-b85e-557bb8d56185","html_url":"https://github.com/ctoth/cacophony","commit_stats":null,"previous_names":["ctoth/cacophony"],"tags_count":141,"template":false,"template_full_name":null,"purl":"pkg:github/ctoth/cacophony","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ctoth%2Fcacophony","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ctoth%2Fcacophony/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ctoth%2Fcacophony/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ctoth%2Fcacophony/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ctoth","download_url":"https://codeload.github.com/ctoth/cacophony/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ctoth%2Fcacophony/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29819265,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-25T05:36:42.804Z","status":"ssl_error","status_checked_at":"2026-02-25T05:36:31.934Z","response_time":61,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-03-01T16:04:50.177Z","updated_at":"2026-05-10T00:02:12.203Z","avatar_url":"https://github.com/ctoth.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\r\n# Cacophony: Advanced Browser Audio Library\r\n\r\nCacophony is a powerful and intuitive audio library designed for modern web applications. It provides a high-level interface to the Web Audio API, simplifying complex audio operations while offering fine-grained control. Cacophony is perfect for projects ranging from simple sound playback to sophisticated audio processing and 3D audio positioning.\r\n\r\n## Key Features\r\n\r\n- **Versatile Audio Source Handling**: Manage audio from various sources including `AudioBuffer`, URL strings, synthesizers, and live microphone input\r\n- **Comprehensive Playback Control**: Play, stop, pause, resume, loop, and seek within audio with ease\r\n- **3D Audio Positioning**: Create immersive soundscapes with precise spatial audio control\r\n- **Advanced Audio Processing**: Apply and manage a variety of audio filters for enhanced sound manipulation\r\n- **Dynamic Volume Control**: Adjust global and individual volume levels with support for smooth fading effects\r\n- **Synthesizer Integration**: Create and manipulate synthesized sounds with customizable oscillator options\r\n- **Efficient Group Management**: Organize and control multiple sounds or synthesizers as groups for streamlined audio management\r\n- **Live Microphone Input**: Capture and process real-time audio input from the user's microphone\r\n- **Network-Backed Playback**: Play audio directly from URLs using media-element-backed sounds\n- **Flexible Caching**: Implement efficient audio caching strategies for improved performance\r\n\r\n## Installation\r\n\r\n```bash\r\nnpm install cacophony\r\n```\r\n\r\n## Quick Start\r\n\r\n```typescript\r\nimport { Cacophony } from 'cacophony';\r\n\r\nasync function audioDemo() {\r\n  const cacophony = new Cacophony();\r\n\r\n  // Create and play a sound with 3D positioning\r\n  const sound = await cacophony.createSound('path/to/audio.mp3');\r\n  sound.play();\r\n  sound.position = [1, 0, -1]; // Set sound position in 3D space\r\n\r\n  // Create and play a synthesizer\r\n  const synth = cacophony.createOscillator({ frequency: 440, type: 'sine' });\r\n  synth.play();\r\n\r\n  // Apply a filter to the synth\r\n  const filter = cacophony.createBiquadFilter({ type: 'lowpass', frequency: 1000 });\r\n  synth.addFilter(filter);\r\n\r\n  // Create a group of sounds\r\n  const group = await cacophony.createGroupFromUrls(['sound1.mp3', 'sound2.mp3']);\r\n  group.play(); // Play all sounds in the group\r\n\r\n  // Capture microphone input\r\n  const micStream = await cacophony.getMicrophoneStream();\r\n  micStream.play();\r\n}\r\n\r\naudioDemo();\r\n```\r\n\r\n## Core Concepts\r\n\r\n### Sound vs Playback Architecture\r\n\r\nCacophony uses a two-tier architecture that separates audio assets from their playback instances:\r\n\r\n- **Sound**: Represents an audio asset (file or buffer). Acts as a container managing multiple playback instances.\r\n- **Playback**: Represents a single playback instance of a Sound. Each call to `play()` creates a new Playback.\r\n\r\nThis design allows the same sound to be played multiple times simultaneously with different settings (volume, position, playback rate, etc.).\r\n\r\n```typescript\r\nconst sound = await cacophony.createSound('laser.mp3');\r\n\r\n// Play the same sound three times with different settings\r\nconst [playback1] = sound.play();\r\nplayback1.volume = 0.3;\r\nplayback1.playbackRate = 1.0;\r\n\r\nconst [playback2] = sound.play();\r\nplayback2.volume = 0.8;\r\nplayback2.playbackRate = 1.5;  // Higher pitched\r\n\r\nconst [playback3] = sound.play();\r\nplayback3.volume = 0.5;\r\nplayback3.position = [5, 0, 0];  // Positioned to the right\r\n\r\n// Control individual playbacks\r\nsetTimeout(() =\u003e playback1.pause(), 1000);\r\nsetTimeout(() =\u003e playback2.stop(), 2000);\r\n\r\n// Or control all playbacks through the Sound\r\nsound.stop();  // Stops all three playbacks\r\n```\r\n\r\n## Sound Types\r\n\r\nCacophony supports three sound types:\n\r\n| Type | Memory | Latency | Seeking | Multiple Instances | Best For |\n|------|--------|---------|---------|-------------------|----------|\n| **Buffer** (default) | High | None | Full | Yes | Sound effects, UI sounds, short music clips |\n| **HTML** | Medium | Low | Full | Yes | Background music, large audio files, podcasts |\n| **Streaming** | Medium | Low | Full | Yes | Network-backed playback created via `createStream()` |\n\r\n```typescript\r\n// Buffer - entire file loaded into memory\r\nconst sfx = await cacophony.createSound('explosion.mp3', SoundType.Buffer);\r\n\r\n// HTML - streams from network, good for large files\r\nconst music = await cacophony.createSound('bgm.mp3', SoundType.HTML);\r\n\r\n// Streaming - convenience helper for network-backed playback\nconst radio = await cacophony.createStream('https://example.com/stream.m3u8');\n```\r\n\r\n## Playback Control\r\n\r\n### Seeking\r\n\r\n```typescript\r\nconst sound = await cacophony.createSound('podcast.mp3');\r\nsound.play();\r\n\r\n// Seek to 30 seconds\r\nsound.seek(30);\r\n\r\n// Seek on individual playback\r\nconst [playback] = sound.play();\r\nplayback.seek(45);\r\n\r\n// Get current time\r\nconsole.log(playback.currentTime);  // Current position in seconds\r\nconsole.log(playback.duration);     // Total duration\r\n```\r\n\r\n### Playback Rate\r\n\r\nControl the speed of playback (affects pitch):\r\n\r\n```typescript\r\nconst sound = await cacophony.createSound('audio.mp3');\r\n\r\nsound.playbackRate = 1.0;   // Normal speed\r\nsound.playbackRate = 2.0;   // Double speed (higher pitch)\r\nsound.playbackRate = 0.5;   // Half speed (lower pitch)\r\n\r\n// Apply to individual playback\r\nconst [playback] = sound.play();\r\nplayback.playbackRate = 1.25;\r\n```\r\n\r\n### Looping\r\n\r\n```typescript\r\nconst sound = await cacophony.createSound('music.mp3');\r\n\r\n// Loop indefinitely\r\nsound.loop('infinite');\r\nsound.play();\r\n\r\n// Loop exactly 3 times\r\nsound.loop(3);\r\nsound.play();\r\n\r\n// No looping (default)\r\nsound.loop(0);\r\n```\r\n\r\n## Volume Control\r\n\r\nCacophony provides a hierarchical volume control system:\r\n\r\n```typescript\r\nconst cacophony = new Cacophony();\r\nconst sound = await cacophony.createSound('audio.mp3');\r\nconst [playback] = sound.play();\r\n\r\n// Global volume (affects all audio)\r\ncacophony.volume = 0.5;  // 50% of original volume\r\n\r\n// Sound volume (affects all playbacks of this sound)\r\nsound.volume = 0.8;      // 80% of global volume\r\n\r\n// Individual playback volume\r\nplayback.volume = 0.6;   // 60% of sound volume\r\n\r\n// Final volume = global × sound × playback = 0.5 × 0.8 × 0.6 = 0.24\r\n\r\n// Mute/unmute\r\ncacophony.mute();\r\ncacophony.unmute();\r\n```\r\n\r\n## Audio Filters\r\n\r\nCacophony provides powerful audio filtering capabilities using BiquadFilterNode. Filters can be applied to Sounds, Synths, Playbacks, and Groups.\r\n\r\nSupported filter types: `lowpass`, `highpass`, `bandpass`, `lowshelf`, `highshelf`, `peaking`, `notch`, `allpass`.\r\n\r\n```typescript\r\nconst cacophony = new Cacophony();\r\nconst sound = await cacophony.createSound('audio.mp3');\r\n\r\n// Create filters\r\nconst lowpass = cacophony.createBiquadFilter({ type: 'lowpass', frequency: 1000, Q: 1 });\r\nconst highshelf = cacophony.createBiquadFilter({ type: 'highshelf', frequency: 5000, gain: -6 });\r\n\r\n// Apply filters (they are chained in order)\r\nsound.addFilter(lowpass);\r\nsound.addFilter(highshelf);\r\n\r\n// Apply to Synth\r\nconst synth = cacophony.createOscillator({ frequency: 440, type: 'sawtooth' });\r\nsynth.addFilter(lowpass);\r\n\r\n// Remove filters\r\nsound.removeFilter(lowpass);\r\n\r\n// Note: Filters are cloned to each playback for independent processing\r\nconst playback = sound.play()[0];\r\nplayback.filters[0].frequency.value = 500; // Only affects this playback\r\n```\r\n\r\nSee [TypeDoc](https://cacophony.js.org) for complete filter parameters and options.\r\n\r\n## Custom Audio Routing\r\n\r\nCacophony exposes the underlying Web Audio graph through Playback instances, enabling manual routing through custom effects chains. This is the low-level foundation for building complex audio processing pipelines.\r\n\r\n### Accessing the Audio Graph\r\n\r\nEvery Playback instance exposes its output node and standard Web Audio connection methods:\r\n\r\n```typescript\r\nconst sound = await cacophony.createSound('audio.mp3');\r\nconst [playback] = sound.play();\r\n\r\n// Access the output node (final node in the internal chain)\r\nconst outputNode = playback.outputNode;  // Returns GainNode\r\n\r\n// Connect to custom destination\r\nconst customEffect = cacophony.context.createDelay(0.5);\r\nplayback.connect(customEffect);\r\ncustomEffect.connect(cacophony.context.destination);\r\n\r\n// Disconnect from default routing\r\nplayback.disconnect();  // Disconnect from all\r\nplayback.disconnect(specificNode);  // Disconnect from specific node\r\n```\r\n\r\n### Building Effect Chains\r\n\r\nRoute playbacks through multiple effects:\r\n\r\n```typescript\r\nconst sound = await cacophony.createSound('guitar.mp3');\r\nconst [playback] = sound.play();\r\n\r\n// Create effect nodes\r\nconst distortion = cacophony.context.createWaveShaper();\r\nconst delay = cacophony.context.createDelay(1.0);\r\nconst reverb = cacophony.context.createConvolver();\r\n\r\n// Chain: playback → distortion → delay → reverb → destination\r\nplayback.disconnect();  // Disconnect from default\r\nplayback.connect(distortion)\r\n        .connect(delay)\r\n        .connect(reverb)\r\n        .connect(cacophony.context.destination);\r\n```\r\n\r\n### Parallel Effects (Send/Return)\r\n\r\nCreate parallel effect sends like a mixing console:\r\n\r\n```typescript\r\nconst sound = await cacophony.createSound('vocals.mp3');\r\nconst [playback] = sound.play();\r\n\r\n// Create send effects\r\nconst reverb = cacophony.context.createConvolver();\r\nconst reverbReturn = cacophony.context.createGain();\r\nreverbReturn.gain.value = 0.3;  // 30% wet\r\n\r\n// Dry signal to destination\r\nplayback.connect(cacophony.context.destination);\r\n\r\n// Parallel wet signal: playback → reverb → reverbReturn → destination\r\nplayback.connect(reverb)\r\n        .connect(reverbReturn)\r\n        .connect(cacophony.context.destination);\r\n```\r\n\r\n### Dynamic Routing\r\n\r\nChange routing in real-time:\r\n\r\n```typescript\r\nconst sound = await cacophony.createSound('audio.mp3');\r\nconst [playback] = sound.play();\r\n\r\nconst cleanPath = cacophony.context.destination;\r\nconst fxPath = cacophony.context.createConvolver();\r\nfxPath.connect(cacophony.context.destination);\r\n\r\n// Toggle between clean and effects\r\nlet useFx = false;\r\nbutton.addEventListener('click', () =\u003e {\r\n  playback.disconnect();\r\n\r\n  if (useFx) {\r\n    playback.connect(fxPath);\r\n  } else {\r\n    playback.connect(cleanPath);\r\n  }\r\n\r\n  useFx = !useFx;\r\n});\r\n```\r\n\r\n### Integrating with Web Audio Nodes\r\n\r\nCacophony works seamlessly with any Web Audio API nodes:\r\n\r\n```typescript\r\nconst [playback] = sound.play();\r\n\r\n// Built-in nodes\r\nconst analyser = cacophony.context.createAnalyser();\r\nconst compressor = cacophony.context.createDynamicsCompressor();\r\nconst panner = cacophony.context.createStereoPanner();\r\n\r\n// AudioWorklet processors\r\nconst customProcessor = new AudioWorkletNode(cacophony.context, 'my-processor');\r\n\r\n// Chain them together\r\nplayback.disconnect();\r\nplayback.connect(compressor)\r\n        .connect(analyser)\r\n        .connect(panner)\r\n        .connect(customProcessor)\r\n        .connect(cacophony.context.destination);\r\n\r\n// Visualize with analyser\r\nconst dataArray = new Uint8Array(analyser.frequencyBinCount);\r\nfunction draw() {\r\n  analyser.getByteFrequencyData(dataArray);\r\n  // ... draw visualization\r\n  requestAnimationFrame(draw);\r\n}\r\n```\r\n\r\n### Architecture Notes\r\n\r\n- **Internal chain**: `source → panner → [filters] → gainNode`\r\n- **outputNode** exposes the final `gainNode` in this chain\r\n- Default routing: `outputNode → globalGainNode → destination`\r\n- Calling `disconnect()` breaks the default routing, allowing full manual control\r\n\r\n## Cloning\r\n\r\nClone sounds to create variations without reloading files. Clones share the same AudioBuffer but have independent settings.\r\n\r\n```typescript\r\nconst footstep = await cacophony.createSound('footstep.mp3');\r\n\r\n// Create variations for different enemy sizes\r\nconst largeEnemy = footstep.clone({\r\n  position: [10, 0, -5],\r\n  playbackRate: 0.8,  // Slower/lower pitch\r\n  volume: 0.9\r\n});\r\n\r\nconst smallEnemy = footstep.clone({\r\n  position: [-5, 0, -10],\r\n  playbackRate: 1.25,  // Faster/higher pitch\r\n  volume: 0.4\r\n});\r\n\r\nlargeEnemy.play();\r\nsmallEnemy.play();\r\n\r\n// Weapon sound variants\r\nconst gunshotBase = await cacophony.createSound('gunshot.mp3');\r\n\r\nconst weapons = {\r\n  pistol: gunshotBase.clone({ volume: 0.6, playbackRate: 1.2 }),\r\n  rifle: gunshotBase.clone({ volume: 1.0, playbackRate: 1.0 }),\r\n  shotgun: gunshotBase.clone({\r\n    volume: 1.2,\r\n    playbackRate: 0.8,\r\n    filters: [cacophony.createBiquadFilter({ type: 'lowpass', frequency: 1200 })]\r\n  })\r\n};\r\n\r\n// Musical instrument samples by pitch-shifting\r\nconst pianoC4 = await cacophony.createSound('piano_c4.mp3');\r\nconst keyboard = {\r\n  C4: pianoC4,\r\n  D4: pianoC4.clone({ playbackRate: 1.122 }),  // +2 semitones\r\n  E4: pianoC4.clone({ playbackRate: 1.260 }),  // +4 semitones\r\n  F4: pianoC4.clone({ playbackRate: 1.335 }),  // +5 semitones\r\n  G4: pianoC4.clone({ playbackRate: 1.498 }),  // +7 semitones\r\n};\r\n```\r\n\r\n## Group Functionality\r\n\r\nGroups allow controlling multiple sounds or synthesizers as a single unit.\r\n\r\n```typescript\r\nconst cacophony = new Cacophony();\r\n\r\n// Create from URLs\r\nconst soundGroup = await cacophony.createGroupFromUrls(['drum.mp3', 'bass.mp3', 'synth.mp3']);\r\n\r\n// Or create from existing Sound instances\r\nconst sound1 = await cacophony.createSound('drum.mp3');\r\nconst sound2 = await cacophony.createSound('bass.mp3');\r\nconst sound3 = await cacophony.createSound('synth.mp3');\r\nconst soundGroup2 = await cacophony.createGroup([sound1, sound2, sound3]);\r\n\r\n// Play all sounds simultaneously\r\nsoundGroup.play();\r\n\r\n// Control all sounds at once\r\nsoundGroup.volume = 0.7;\r\nsoundGroup.playbackRate = 1.2;\r\nsoundGroup.position = [5, 0, 0];\r\nsoundGroup.loop('infinite');\r\n\r\n// Apply filters to entire group\r\nconst lowpass = cacophony.createBiquadFilter({ type: 'lowpass', frequency: 1000 });\r\nsoundGroup.addFilter(lowpass);\r\n\r\n// Play random sound from group\r\nconst footsteps = await cacophony.createGroupFromUrls([\r\n  'footstep1.mp3',\r\n  'footstep2.mp3',\r\n  'footstep3.mp3',\r\n  'footstep4.mp3'\r\n]);\r\n\r\nfootsteps.playRandom();  // Picks one at random\r\n\r\n// Advance through sounds in sequence, one call at a time\nconst dialog = await cacophony.createGroupFromUrls(['line1.mp3', 'line2.mp3', 'line3.mp3']);\ndialog.playOrdered(true);   // Plays line1, then advances internal order\ndialog.playOrdered(true);   // Plays line2\ndialog.playOrdered(true);   // Plays line3\ndialog.playOrdered(true);   // Plays line1 again because looping is enabled\n\r\n// SynthGroup for synthesizers\nconst synthGroup = new SynthGroup();\nconst synth1 = cacophony.createOscillator({ frequency: 440, type: 'sine' });\nconst synth2 = cacophony.createOscillator({ frequency: 660, type: 'square' });\nsynthGroup.addSynth(synth1);\nsynthGroup.addSynth(synth2);\nsynthGroup.play();\nsynthGroup.volume = 0.5;\nsynthGroup.type = 'triangle';\nsynthGroup.pause();\nsynthGroup.resume();\n\n// Remove a synth from the group\nsynthGroup.removeSynth(synth1);\n```\n\r\n## Synthesizer Functionality\r\n\r\nCreate oscillator-based sounds with four waveform types: `sine`, `square`, `sawtooth`, `triangle`.\r\n\r\n```typescript\r\nconst cacophony = new Cacophony();\r\n\r\n// Create oscillators\r\nconst sineOsc = cacophony.createOscillator({ frequency: 440, type: 'sine' });\r\nsineOsc.play();\r\n\r\n// Change parameters in real-time\r\nsineOsc.frequency = 880;  // Change frequency\r\nsineOsc.type = 'sawtooth';  // Change waveform\r\nsineOsc.detune = 10;  // Detune in cents (1/100th of a semitone)\r\n\r\n// Layer multiple oscillators\r\nconst bass = cacophony.createOscillator({ frequency: 110, type: 'sine' });\r\nconst lead = cacophony.createOscillator({ frequency: 440, type: 'sawtooth' });\r\nbass.volume = 0.7;\r\nlead.volume = 0.5;\r\nbass.addFilter(cacophony.createBiquadFilter({ type: 'lowpass', frequency: 1000 }));\r\nbass.play();\r\nlead.play();\r\n\r\n// Modulate frequency over time\nlet time = 0;\nsetInterval(() =\u003e {\n  const frequency = 440 + Math.sin(time) * 100;\n  sineOsc.frequency = frequency;\n  time += 0.1;\n}, 50);\n\n// Pause and resume the active synth playback without losing its settings\nsineOsc.pause();\nsineOsc.resume();\n```\n\n`synth.pause()` keeps the existing synth playback object so `synth.resume()` can restart it with the current frequency, detune, type, volume, pan, and filter settings. `synth.play()` still creates a fresh playback instance, just like `Sound.play()`.\n\n## 3D Audio Positioning\n\r\nCreate immersive soundscapes with precise spatial audio control using HRTF (Head-Related Transfer Function) or stereo panning.\r\n\r\n```typescript\r\nconst cacophony = new Cacophony();\r\n\r\n// Create sounds with HRTF panning\r\nconst ambience = await cacophony.createSound('forest_ambience.mp3', SoundType.Buffer, 'HRTF');\r\nconst birdSound = await cacophony.createSound('bird_chirp.mp3', SoundType.Buffer, 'HRTF');\r\nconst footsteps = await cacophony.createSound('footsteps.mp3', SoundType.Buffer, 'HRTF');\r\n\r\n// Position sounds in 3D space\r\n// Coordinate system: X (left- to right+), Y (down- to up+), Z (front+ to back-)\r\nambience.position = [0, 0, -5];  // Slightly behind the listener\r\nbirdSound.position = [10, 5, 0];  // To the right and above\r\nfootsteps.position = [-2, -1, 2];  // Slightly to the left and in front\r\n\r\n// Configure distance attenuation\r\nbirdSound.threeDOptions = {\r\n  distanceModel: 'inverse',  // 'linear', 'inverse', or 'exponential'\r\n  refDistance: 1,\r\n  rolloffFactor: 1\r\n};\r\n\r\n// Play the sounds\r\nambience.play();\r\nbirdSound.play();\r\nfootsteps.play();\r\n\r\n// Set listener position and orientation\r\ncacophony.listenerPosition = [0, 0, 0];\r\ncacophony.listenerOrientation = {\r\n  forward: [0, 0, -1],  // Looking towards negative Z\r\n  up: [0, 1, 0]\r\n};\r\n\r\n// Animate bird sound position\r\nlet time = 0;\r\nsetInterval(() =\u003e {\r\n  const x = Math.sin(time) * 10;\r\n  birdSound.position = [x, 5, 0];\r\n  time += 0.05;\r\n}, 50);\r\n\r\n// Stereo panning (simple left-right)\r\nconst stereoSound = await cacophony.createSound('audio.mp3', SoundType.Buffer, 'stereo');\r\nstereoSound.stereoPan = 0.5;  // -1 (left) to 1 (right)\r\nstereoSound.play();\r\n```\r\n\r\nSee [TypeDoc](https://cacophony.js.org) for distance models, cone effects, and advanced 3D audio options.\r\n\r\n## Microphone Input\r\n\r\nCapture, process, and manipulate live audio input:\r\n\r\n```typescript\r\nconst cacophony = new Cacophony();\r\n\r\ntry {\r\n  const micStream = await cacophony.getMicrophoneStream();\r\n  micStream.play();\r\n\r\n  // Apply filters to microphone input\r\n  const lowPassFilter = cacophony.createBiquadFilter({ type: 'lowpass', frequency: 1000 });\r\n  micStream.addFilter(lowPassFilter);\r\n\r\n  // Control microphone volume\r\n  micStream.volume = 0.8;\r\n\r\n  // Pause and resume\r\n  setTimeout(() =\u003e {\r\n    micStream.pause();\r\n    setTimeout(() =\u003e micStream.resume(), 2000);\r\n  }, 5000);\r\n\r\n} catch (error) {\r\n  console.error(\"Error accessing microphone:\", error);\r\n}\r\n```\r\n\r\n## Audio Streaming\r\n\r\nStream audio content efficiently:\r\n\r\n```typescript\r\nconst cacophony = new Cacophony();\r\n\r\ntry {\r\n  const streamedSound = await cacophony.createStream('https://example.com/live_radio_stream');\r\n  streamedSound.play();\r\n\r\n  // Apply real-time effects to the stream\r\n  const highPassFilter = cacophony.createBiquadFilter({ type: 'highpass', frequency: 500 });\r\n  streamedSound.addFilter(highPassFilter);\r\n\r\n  // Control streaming playback\r\n  setTimeout(() =\u003e {\r\n    streamedSound.pause();\r\n    setTimeout(() =\u003e streamedSound.play(), 5000);\r\n  }, 10000);\r\n\r\n} catch (error) {\r\n  console.error(\"Error streaming audio:\", error);\r\n}\r\n```\r\n\r\n## Event System\r\n\r\nCacophony provides a comprehensive event system for monitoring loading progress, cache performance, and audio playback state.\r\n\r\n### Loading Progress\r\n\r\n```typescript\r\nconst cacophony = new Cacophony();\r\n\r\ncacophony.on('loadingStart', (event) =\u003e {\r\n  console.log(`Started loading: ${event.url}`);\r\n  showSpinner();\r\n});\r\n\r\ncacophony.on('loadingProgress', (event) =\u003e {\r\n  const percent = event.progress * 100;\r\n  updateProgressBar(event.progress);\r\n});\r\n\r\ncacophony.on('loadingComplete', (event) =\u003e {\r\n  console.log(`Loaded: ${event.url} (${event.size} bytes)`);\r\n  hideSpinner();\r\n});\r\n\r\nconst sound = await cacophony.createSound('large-audio-file.mp3');\r\n```\r\n\r\n### Error Handling\r\n\r\n```typescript\r\n// Global error handling\r\ncacophony.on('loadingError', (event) =\u003e {\r\n  console.error(`Failed to load ${event.url}:`, event.error);\r\n  showErrorToast(`Failed to load audio: ${event.errorType}`);\r\n});\r\n\r\n// Sound-specific error handling\r\nsound.on('soundError', (event) =\u003e {\r\n  if (event.recoverable) {\r\n    console.log('Retrying...');\r\n    sound.play();\r\n  } else {\r\n    console.error('Unrecoverable error:', event.error);\r\n  }\r\n});\r\n```\r\n\r\n### Global Playback Events\r\n\r\n```typescript\r\n// Monitor all playback globally\r\ncacophony.on('globalPlay', (event) =\u003e {\r\n  console.log('Audio started:', event.source);\r\n  showGlobalAudioIndicator();\r\n});\r\n\r\ncacophony.on('globalStop', (event) =\u003e {\r\n  console.log('Audio stopped:', event.source);\r\n  hideGlobalAudioIndicator();\r\n});\r\n\r\ncacophony.on('globalPause', (event) =\u003e {\r\n  console.log('Audio paused:', event.source);\r\n});\r\n```\r\n\r\n### Playback Events\r\n\r\n```typescript\r\nconst sound = await cacophony.createSound('audio.mp3');\r\n\r\nsound.on('play', (playback) =\u003e {\r\n  console.log('Playing:', playback);\r\n  updatePlayButton('pause');\r\n});\r\n\r\nsound.on('pause', () =\u003e {\r\n  updatePlayButton('play');\r\n});\r\n\r\nsound.on('volumeChange', (volume) =\u003e {\r\n  updateVolumeSlider(volume);\r\n});\r\n\r\n// Playback instance events\r\nconst [playback] = sound.play();\r\nplayback.on('play', (pb) =\u003e {\r\n  console.log('Playback started:', pb.currentTime);\r\n});\r\n\r\nplayback.on('error', (event) =\u003e {\r\n  if (event.recoverable) {\r\n    console.log('Recoverable error, retrying...');\r\n    playback.play();\r\n  }\r\n});\r\n```\r\n\r\n### Synth Events\r\n\r\n```typescript\r\nconst synth = cacophony.createOscillator({ frequency: 440, type: 'sine' });\r\n\r\nsynth.on('frequencyChange', (freq) =\u003e {\r\n  console.log('Frequency changed to:', freq, 'Hz');\r\n});\r\n\r\nsynth.on('typeChange', (type) =\u003e {\r\n  console.log('Waveform changed to:', type);\r\n});\r\n\r\nsynth.frequency = 880;  // Triggers frequencyChange event\r\nsynth.type = 'square';  // Triggers typeChange event\r\n```\r\n\r\n### Complete Event Reference\r\n\r\n#### Cacophony Events\r\n\r\n| Event | Payload | Description |\r\n|-------|---------|-------------|\r\n| `loadingStart` | `LoadingStartEvent` | Fired when audio loading begins. Contains `url` and `timestamp`. |\r\n| `loadingProgress` | `LoadingProgressEvent` | Fired during download. Contains `url`, `loaded`, `total`, `progress` (0-1), `timestamp`. |\r\n| `loadingComplete` | `LoadingCompleteEvent` | Fired when loading succeeds. Contains `url`, `duration`, `size`, `timestamp`. |\r\n| `loadingError` | `LoadingErrorEvent` | Fired when loading fails. Contains `url`, `error`, `errorType`, `timestamp`. |\r\n| `cacheHit` | `CacheHitEvent` | Fired on cache hit. Contains `url`, `cacheType` ('memory'\\|'browser'\\|'conditional'), `timestamp`. |\r\n| `cacheMiss` | `CacheMissEvent` | Fired on cache miss. Contains `url`, `reason` ('not-found'\\|'expired'\\|'invalid'), `timestamp`. |\r\n| `cacheError` | `CacheErrorEvent` | Fired on cache operation error. Contains `url`, `error`, `operation`, `timestamp`. |\r\n| `globalPlay` | `GlobalPlaybackEvent` | Fired when any Sound or Synth starts playing. Contains `source`, `timestamp`. |\r\n| `globalStop` | `GlobalPlaybackEvent` | Fired when any Sound or Synth stops. Contains `source`, `timestamp`. |\r\n| `globalPause` | `GlobalPlaybackEvent` | Fired when any Sound or Synth pauses. Contains `source`, `timestamp`. |\r\n| `volumeChange` | `number` | Fired when global volume changes. Receives new volume value. |\r\n| `mute` | `void` | Fired when audio is muted globally. |\r\n| `unmute` | `void` | Fired when audio is unmuted globally. |\r\n| `suspend` | `void` | Fired when audio context is suspended. |\r\n| `resume` | `void` | Fired when audio context is resumed. |\r\n\r\n#### Sound Events\r\n\r\n| Event | Payload | Description |\r\n|-------|---------|-------------|\r\n| `play` | `Playback` | Fired when sound starts playing. Receives the Playback instance. |\r\n| `stop` | `void` | Fired when sound stops. |\r\n| `pause` | `void` | Fired when sound pauses. |\r\n| `resume` | `void` | Fired when sound resumes after being paused. |\r\n| `ended` | `void` | Fired when sound playback ends naturally. |\r\n| `loopEnd` | `void` | Fired when a loop iteration completes. |\r\n| `volumeChange` | `number` | Fired when volume changes. Receives new volume value. |\r\n| `rateChange` | `number` | Fired when playback rate changes. Receives new rate value. |\r\n| `soundError` | `SoundErrorEvent` | Fired on playback errors. Contains `url`, `error`, `errorType`, `timestamp`, `recoverable`. |\r\n\r\n#### Playback Events\r\n\r\n| Event | Payload | Description |\r\n|-------|---------|-------------|\r\n| `play` | `BasePlayback` | Fired when playback starts. Receives the Playback instance. |\r\n| `stop` | `void` | Fired when playback stops. |\r\n| `pause` | `void` | Fired when playback pauses. |\r\n| `resume` | `void` | Fired when playback resumes after being paused. |\r\n| `ended` | `void` | Fired when playback ends naturally. |\r\n| `seek` | `number` | Fired when playback position changes. Receives new time in seconds. |\r\n| `volumeChange` | `number` | Fired when playback volume changes. Receives new volume value. |\r\n| `error` | `PlaybackErrorEvent` | Fired on playback errors. Contains `error`, `errorType`, `timestamp`, `recoverable`. |\r\n\r\n#### Synth Events\r\n\r\n| Event | Payload | Description |\r\n|-------|---------|-------------|\r\n| `play` | `SynthPlayback` | Fired when synth starts playing. Receives the SynthPlayback instance. |\r\n| `stop` | `void` | Fired when synth stops. |\r\n| `pause` | `void` | Fired when synth pauses. |\r\n| `resume` | `void` | Fired when synth resumes after being paused. |\r\n| `ended` | `void` | Fired when synth playback ends naturally. |\r\n| `frequencyChange` | `number` | Fired when frequency changes. Receives new frequency in Hz. |\r\n| `typeChange` | `OscillatorType` | Fired when waveform type changes. Receives new type ('sine'\\|'square'\\|'sawtooth'\\|'triangle'). |\r\n| `detuneChange` | `number` | Fired when detune changes. Receives new detune value in cents. |\r\n| `volumeChange` | `number` | Fired when volume changes. Receives new volume value. |\r\n| `error` | `PlaybackErrorEvent` | Fired on playback errors. Contains `error`, `errorType`, `timestamp`, `recoverable`. |\r\n\r\n## Caching\r\n\r\nCacophony implements intelligent three-layer caching for optimal performance:\r\n\r\n**Memory Cache (LRU)** → **Browser Cache API** → **Network**\r\n\r\nThe cache system is fully automatic and HTTP-compliant, respecting standard cache headers (ETag, Last-Modified). When cache validation tokens are available, Cacophony makes lightweight conditional requests (304 responses have no body). When tokens are unavailable, it falls back to TTL-based caching (24 hours default).\r\n\r\n```typescript\r\nconst cacophony = new Cacophony();\r\n\r\n// First load - fetches from network, stores in cache\r\nconst sound1 = await cacophony.createSound('audio.mp3');\r\n\r\n// Second load - instant from memory cache\r\nconst sound2 = await cacophony.createSound('audio.mp3');\r\n\r\n// Monitor cache performance via events\r\ncacophony.on('cacheHit', (event) =\u003e {\r\n  console.log(`${event.cacheType} cache hit: ${event.url}`);\r\n  // cacheType: 'memory' | 'browser' | 'conditional'\r\n});\r\n\r\ncacophony.on('cacheMiss', (event) =\u003e {\r\n  console.log(`Cache miss: ${event.url} - ${event.reason}`);\r\n  // reason: 'not-found' | 'expired' | 'invalid'\r\n});\r\n\r\n// Clear memory cache (browser cache persists)\r\ncacophony.clearMemoryCache();\r\n\r\n// Optional: configure TTL for when no validation tokens exist\r\nimport { AudioCache } from 'cacophony';\r\nAudioCache.setCacheExpirationTime(60 * 60 * 1000); // 1 hour\r\n```\r\n\r\nThe caching system requires no configuration in most cases. It automatically optimizes for performance while respecting HTTP standards.\r\n\r\n## Cancellation with AbortSignal\r\n\r\nCancel audio loading operations:\r\n\r\n```typescript\r\nconst controller = new AbortController();\r\n\r\nconst soundPromise = cacophony.createSound(\r\n  'large-file.mp3',\r\n  SoundType.Buffer,\r\n  'HRTF',\r\n  controller.signal\r\n);\r\n\r\n// Cancel loading\r\nrouter.on('navigate', () =\u003e controller.abort());\r\n\r\ntry {\r\n  const sound = await soundPromise;\r\n  sound.play();\r\n} catch (error) {\r\n  if (error.name === 'AbortError') {\r\n    console.log('Loading was cancelled');\r\n  }\r\n}\r\n\r\n// Works with groups too\r\nconst group = await cacophony.createGroupFromUrls(\r\n  ['a.mp3', 'b.mp3', 'c.mp3'],\r\n  SoundType.Buffer,\r\n  'HRTF',\r\n  controller.signal\r\n);\r\n```\r\n\r\n## Resource Management\r\n\r\nCall `cleanup()` when done with sounds to free resources:\r\n\r\n```typescript\r\nconst sound = await cacophony.createSound('temp.mp3');\nsound.play();\nsound.cleanup();  // Tears down playbacks, including pausing and resetting active HTML/streaming media\n\r\n// Clear memory cache\r\ncacophony.clearMemoryCache();\r\n```\r\n\r\nCacophony uses `FinalizationRegistry` for automatic cleanup when objects are garbage collected, but explicit cleanup is recommended for large applications.\r\n\r\n## Audio Context Control\r\n\r\nSuspend and resume the entire audio context to pause all audio processing. Useful for mobile apps when entering background mode or for performance optimization:\r\n\r\n```typescript\r\nconst cacophony = new Cacophony();\r\n\r\n// Suspend audio context (pauses ALL audio, saves battery)\r\ncacophony.pause();\r\n\r\n// Resume audio context\r\ncacophony.resume();\r\n\r\n// Example: pause when app goes to background\r\ndocument.addEventListener('visibilitychange', () =\u003e {\r\n  if (document.hidden) {\r\n    cacophony.pause();\r\n  } else {\r\n    cacophony.resume();\r\n  }\r\n});\r\n```\r\n\r\nNote: This is different from `sound.pause()` which pauses individual sounds. `cacophony.pause()` suspends the entire audio engine.\r\n\r\n## API Documentation\r\n\r\nFor complete API documentation including all methods, parameters, and options, see the [TypeDoc documentation](https://cacophony.js.org).\r\n\r\n## License\r\n\r\nCacophony is open-source software licensed under the [MIT License](LICENSE.txt).\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fctoth%2Fcacophony","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fctoth%2Fcacophony","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fctoth%2Fcacophony/lists"}