Wiki · Devlog · Procedural Audio

Procedural Audio

CONTRABAND: Edge of the Fold ships with zero audio files. Every sound — ambient music, combat effects, UI chimes, engine drones — is generated at runtime by the Web Audio API. This post explains how and why.

Why no audio files?

Three reasons. First, budget. Licensing professional music for an indie game costs thousands of dollars minimum. I did not have thousands of dollars. Second, download size. A decent soundtrack is 20-40MB of MP3s. The entire CONTRABAND game without Three.js models is smaller than that. Shipping the game with a soundtrack would have quadrupled the download. Third, legal risk. Even free "royalty-free" music occasionally has ambiguous licensing that becomes a problem when a game monetizes.

Procedural audio sidesteps all three. Zero bandwidth, zero dollars, zero licensing risk. The ambient music is synthesized from first principles at runtime. It sounds different every session — not radically, but enough that repeat listeners notice variety without consciously identifying why.

The ambient soundtrack

The game's background music is composed of six overlapping sine wave oscillators with low-frequency oscillator (LFO) modulation on their amplitudes. Each oscillator runs at a different fundamental frequency corresponding to a minor-chord triad structure. The LFOs run at tempos between 0.05 Hz and 0.3 Hz, producing slow amplitude swells that feel organic.

The result is ambient drone music in the style of Brian Eno's airport recordings — minimal, spacious, not melodic in any conventional sense. This matches the game's tone. Players have described it as "like listening to a spaceship's life-support system." That is exactly what I was going for.

Total code for the ambient system: approximately 80 lines of JavaScript. No samples, no loops, no triggered audio files. The oscillators run continuously and never repeat exactly.

Combat audio

Combat needs sharper, more rhythmic audio than ambient drone. The approach: each combat sound is one or two oscillators with a specific envelope (attack-decay-sustain-release curve) and optionally a filter. A laser shot is a sawtooth oscillator at 440Hz with a fast attack, immediate decay, short sustain, and a low-pass filter that closes as the sound plays. Total duration: 120ms. Total code: 15 lines.

Explosion sounds use white noise passed through a resonant low-pass filter whose cutoff frequency drops rapidly from 8kHz to 60Hz over 500ms. This creates the characteristic "whump" of a synthesized explosion without any sample. It is not indistinguishable from a real recorded explosion, but it is convincingly gamey.

UI audio

Menu clicks, dialog advances, and modal opens use short synthesized beeps. The specific tonal choices match the game's color palette: gold actions play slightly higher-pitched sounds, red actions play lower ones. This is subliminal — players don't consciously register the pattern but feel the consistency.

The Web Audio API

All of this runs on the Web Audio API, which is a browser-native audio processing graph. You create nodes (oscillators, filters, gain envelopes) and connect them into a graph that routes to the speakers. For procedural audio it's ideal — the whole system is declarative and low-overhead.

The only major limitation is that Web Audio does not start automatically in modern browsers. The user must interact with the page once before audio can begin. I handle this by starting all audio nodes silently and unmuting on first click. This is a one-line workaround for what could otherwise be a confusing "audio not working" bug.

Variability and mood

The game's audio system subtly shifts mood based on game state. When the player is in combat, ambient music LFOs speed up. When the player is in the Silent Fold, a slight frequency wobble is added to all oscillators. When the player is near the Keystone in Chapter 4, an additional sub-bass oscillator at 40Hz is layered in. Players don't consciously notice these shifts but feel that different regions "sound different."

This is the kind of dynamic soundtrack that would be very expensive with recorded audio (you'd need multiple stems, crossfades, and lots of mixing). With procedural audio it's a parameter change — the same oscillator network, just tuned differently.

What I learned

Procedural audio works excellently for ambient, atmospheric games. It works less well for games that need memorable melodies or specific emotional cues. CONTRABAND does not have memorable melodies — that's fine for this game. It would not be fine for a game that wanted a recognizable theme song. If I wrote that kind of game, I would license music.

The Web Audio API is underutilized by most web developers. It is genuinely one of the most powerful browser APIs and can produce sounds rivaling desktop DAW output. More browser games should experiment with it.