Audio Channels

Learn about the channel-based mixing system with independent volume control for SFX, Music, Voice, and Master channels.

Web Engine uses a channel-based audio mixing system that routes sounds through independent channels. This allows players to adjust SFX, Music, and Voice volumes separately while maintaining a master volume control.

Audio Channels#

The audio system provides four built-in channels:

Master Channel#

  • Controls overall volume for all audio
  • All other channels are routed through Master
  • Muting Master mutes all audio
  • Perfect for global volume control in settings

SFX Channel#

  • Sound effects, impacts, explosions, UI sounds
  • Short, one-shot sounds and gameplay audio
  • Environmental sounds (footsteps, doors, ambience)
  • Channel index: 0 or AudioChannel.SFX

Music Channel#

  • Background music, themes, soundtracks
  • Long-playing, looping audio tracks
  • Ambient musical elements
  • Channel index: 1 or AudioChannel.Music

Voice Channel#

  • Character dialogue, narration, voice-overs
  • Radio communications, announcements
  • Any spoken audio content
  • Channel index: 2 or AudioChannel.Voice

Channel Routing#

Every audio source is routed through one channel. Set the channel using the group property:

import { AudioSource, AudioChannel } from '@web-engine-dev/core';
// Route to SFX channel (default)
AudioSource.group[explosionEntity] = AudioChannel.SFX; // 0
// Route to Music channel
AudioSource.group[musicEntity] = AudioChannel.Music; // 1
// Route to Voice channel
AudioSource.group[dialogueEntity] = AudioChannel.Voice; // 2

The final volume calculation flows through the channel hierarchy:

// Volume calculation hierarchy
finalVolume = source.volume
* channel.volume // SFX, Music, or Voice
* master.volume; // Master channel
// Example
source.volume = 0.8; // Source at 80%
sfx.volume = 0.5; // SFX channel at 50%
master.volume = 0.7; // Master at 70%
finalVolume = 0.8 * 0.5 * 0.7 = 0.28 (28%)

AudioMixer API#

The AudioMixer provides centralized control over all channel volumes and mute states:

Setting Volume#

import { audioMixer, AudioChannel } from '@web-engine-dev/core';
// Set individual channel volume (0.0 to 1.0)
audioMixer.setVolume(AudioChannel.Master, 0.8);
audioMixer.setVolume(AudioChannel.SFX, 0.7);
audioMixer.setVolume(AudioChannel.Music, 0.4);
audioMixer.setVolume(AudioChannel.Voice, 1.0);
// Set multiple volumes at once (more efficient)
audioMixer.setVolumes({
[AudioChannel.Master]: 0.8,
[AudioChannel.SFX]: 0.7,
[AudioChannel.Music]: 0.4,
[AudioChannel.Voice]: 1.0,
});
// Get current volume
const sfxVolume = audioMixer.getVolume(AudioChannel.SFX);
console.log('SFX Volume:', sfxVolume);

Muting Channels#

// Mute a single channel
audioMixer.setMuted(AudioChannel.Music, true); // Mute music
audioMixer.setMuted(AudioChannel.Music, false); // Unmute music
// Toggle mute state
audioMixer.toggleMute(AudioChannel.SFX);
// Mute all channels at once
audioMixer.setMuted('all', true);
// Check if channel is muted
const isMusicMuted = audioMixer.isMuted(AudioChannel.Music);
console.log('Music muted:', isMusicMuted);

Master Mute Override

When Master is muted, all audio is silenced regardless of individual channel mute states. Unmuting a channel while Master is muted will not produce sound until Master is also unmuted.

Getting Mixer State#

// Get complete mixer state snapshot
const state = audioMixer.snapshot();
console.log(state);
// {
// master: 0.8,
// music: 0.4,
// sfx: 0.7,
// voice: 1.0,
// muteMaster: false,
// muteMusic: true,
// muteSfx: false,
// muteVoice: false
// }

Volume Persistence#

For player settings, you'll typically want to save and restore volume preferences:

import { audioMixer, AudioChannel } from '@web-engine-dev/core';
// Save volume settings to localStorage
function saveAudioSettings() {
const settings = {
master: audioMixer.getVolume(AudioChannel.Master),
sfx: audioMixer.getVolume(AudioChannel.SFX),
music: audioMixer.getVolume(AudioChannel.Music),
voice: audioMixer.getVolume(AudioChannel.Voice),
muteMaster: audioMixer.isMuted(AudioChannel.Master),
muteMusic: audioMixer.isMuted(AudioChannel.Music),
muteSfx: audioMixer.isMuted(AudioChannel.SFX),
muteVoice: audioMixer.isMuted(AudioChannel.Voice),
};
localStorage.setItem('audioSettings', JSON.stringify(settings));
}
// Load volume settings from localStorage
function loadAudioSettings() {
const stored = localStorage.getItem('audioSettings');
if (!stored) return;
const settings = JSON.parse(stored);
// Restore volumes
audioMixer.setVolumes({
[AudioChannel.Master]: settings.master ?? 1.0,
[AudioChannel.SFX]: settings.sfx ?? 1.0,
[AudioChannel.Music]: settings.music ?? 1.0,
[AudioChannel.Voice]: settings.voice ?? 1.0,
});
// Restore mute states
audioMixer.setMuted(AudioChannel.Master, settings.muteMaster ?? false);
audioMixer.setMuted(AudioChannel.Music, settings.muteMusic ?? false);
audioMixer.setMuted(AudioChannel.SFX, settings.muteSfx ?? false);
audioMixer.setMuted(AudioChannel.Voice, settings.muteVoice ?? false);
}
// Call on game start
loadAudioSettings();

UI Integration#

Create volume sliders in your settings menu that control the mixer:

import { audioMixer, AudioChannel } from '@web-engine-dev/core';
import { useState, useEffect } from 'react';
function AudioSettings() {
const [masterVolume, setMasterVolume] = useState(1.0);
const [sfxVolume, setSfxVolume] = useState(1.0);
const [musicVolume, setMusicVolume] = useState(1.0);
const [voiceVolume, setVoiceVolume] = useState(1.0);
// Load initial values
useEffect(() => {
setMasterVolume(audioMixer.getVolume(AudioChannel.Master) ?? 1.0);
setSfxVolume(audioMixer.getVolume(AudioChannel.SFX) ?? 1.0);
setMusicVolume(audioMixer.getVolume(AudioChannel.Music) ?? 1.0);
setVoiceVolume(audioMixer.getVolume(AudioChannel.Voice) ?? 1.0);
}, []);
const handleMasterChange = (value: number) => {
setMasterVolume(value);
audioMixer.setVolume(AudioChannel.Master, value);
};
const handleSfxChange = (value: number) => {
setSfxVolume(value);
audioMixer.setVolume(AudioChannel.SFX, value);
};
return (
<div className="audio-settings">
<label>
Master Volume
<input
type="range"
min="0"
max="1"
step="0.01"
value={masterVolume}
onChange={(e) => handleMasterChange(parseFloat(e.target.value))}
/>
</label>
<label>
SFX Volume
<input
type="range"
min="0"
max="1"
step="0.01"
value={sfxVolume}
onChange={(e) => handleSfxChange(parseFloat(e.target.value))}
/>
</label>
{/* Repeat for Music and Voice */}
</div>
);
}

Category Management Best Practices#

Follow these guidelines for routing sounds to the appropriate channel:

SFX Channel Usage#

  • Weapon fire, explosions, impacts
  • Footsteps, movement sounds
  • Environmental sounds (doors, switches, machinery)
  • UI sounds (button clicks, notifications)
  • Physics sounds (collisions, breaking objects)
  • Power-ups, collectibles

Music Channel Usage#

  • Background music tracks
  • Musical themes and motifs
  • Ambient musical loops
  • Menu/UI music
  • Victory/defeat music

Voice Channel Usage#

  • Character dialogue
  • Narrator voice-over
  • Radio/comms chatter
  • Announcer voices
  • Tutorial instructions
  • Character grunts/reactions (optional - could also be SFX)

When to Use Voice vs SFX

Character vocalizations can go in either Voice or SFX depending on context. Use Voice for dialogue and clear speech. Use SFX for combat grunts, pain sounds, and other non-verbal vocalizations.

Mixer Performance#

The AudioMixer is optimized for zero-GC performance:

  • Rate Limiting — Updates are rate-limited to prevent excessive WebAudio API calls
  • Dirty Flags — Only applies changes when volumes actually change
  • Batch Updates — Use setVolumes() to update multiple channels efficiently
  • Epsilon Comparison — Tiny volume changes are ignored to reduce updates
// Inefficient - multiple calls
audioMixer.setVolume(AudioChannel.Master, 0.8);
audioMixer.setVolume(AudioChannel.SFX, 0.7);
audioMixer.setVolume(AudioChannel.Music, 0.4);
// Efficient - batch update
audioMixer.setVolumes({
[AudioChannel.Master]: 0.8,
[AudioChannel.SFX]: 0.7,
[AudioChannel.Music]: 0.4,
});

Complete Example: Audio Settings#

import { audioMixer, AudioChannel } from '@web-engine-dev/core';
class AudioSettingsPanel {
private storageKey = 'gameAudioSettings';
// Initialize and load saved settings
init() {
this.loadSettings();
this.setupUI();
}
// Save current settings
saveSettings() {
const settings = {
master: audioMixer.getVolume(AudioChannel.Master),
sfx: audioMixer.getVolume(AudioChannel.SFX),
music: audioMixer.getVolume(AudioChannel.Music),
voice: audioMixer.getVolume(AudioChannel.Voice),
muteMaster: audioMixer.isMuted(AudioChannel.Master),
muteMusic: audioMixer.isMuted(AudioChannel.Music),
};
localStorage.setItem(this.storageKey, JSON.stringify(settings));
}
// Load saved settings
loadSettings() {
const stored = localStorage.getItem(this.storageKey);
if (!stored) return;
try {
const settings = JSON.parse(stored);
audioMixer.setVolumes({
[AudioChannel.Master]: settings.master ?? 1.0,
[AudioChannel.SFX]: settings.sfx ?? 1.0,
[AudioChannel.Music]: settings.music ?? 0.7,
[AudioChannel.Voice]: settings.voice ?? 1.0,
});
if (settings.muteMaster) {
audioMixer.setMuted(AudioChannel.Master, true);
}
if (settings.muteMusic) {
audioMixer.setMuted(AudioChannel.Music, true);
}
} catch (e) {
console.error('Failed to load audio settings:', e);
}
}
// Setup UI event handlers
setupUI() {
// Master volume slider
document.getElementById('masterSlider')?.addEventListener('input', (e) => {
const value = parseFloat((e.target as HTMLInputElement).value);
audioMixer.setVolume(AudioChannel.Master, value);
this.saveSettings();
});
// SFX volume slider
document.getElementById('sfxSlider')?.addEventListener('input', (e) => {
const value = parseFloat((e.target as HTMLInputElement).value);
audioMixer.setVolume(AudioChannel.SFX, value);
this.saveSettings();
});
// Music mute toggle
document.getElementById('muteMusic')?.addEventListener('click', () => {
audioMixer.toggleMute(AudioChannel.Music);
this.saveSettings();
});
}
// Reset to defaults
resetToDefaults() {
audioMixer.setVolumes({
[AudioChannel.Master]: 1.0,
[AudioChannel.SFX]: 1.0,
[AudioChannel.Music]: 0.7,
[AudioChannel.Voice]: 1.0,
});
audioMixer.setMuted('all', false);
this.saveSettings();
}
}
// Usage
const audioSettings = new AudioSettingsPanel();
audioSettings.init();
Audio | Web Engine Docs | Web Engine Docs