preload()
preload() runs once before setup() and is the perfect place to load or create audio sources. All the oscillators and noise generators are created silent (amp=0) and started so they're ready to play instantly when needed—this is much faster than creating them mid-game. The audio chain (noise → filter → output) is a core Web Audio pattern: you create sources, route them through processors (filters, delays, envelopes), and finally connect to the output.
🔬 The ADSR values shape how the jumpscare sound hits you: 0.01 is a snappy attack (shocking), 0.2 is a quick decay (scary drop), and 0.5 is a long release (lingering fear). What happens if you increase the attack to 0.5 or the release to 2? How does that change the emotional impact?
jumpscareEnvelope.setADSR(0.01, 0.2, 0.0, 0.5); // Fast attack, short decay, NO sustain, short release
jumpscareEnvelope.setRange(1.0, 0); // Full volume to silent
function preload() {
// --- Procedural Ambient Sound ---
// A low, rumbling oscillator (sine wave)
ambientSoundOsc = new p5.Oscillator('sine');
ambientSoundOsc.freq(50); // Low frequency for a drone
ambientSoundOsc.amp(0); // Start silent
ambientSoundOsc.start(); // Start the oscillator (will be silent until amp() is called)
// White noise for a subtle hiss/static
ambientSoundNoise = new p5.Noise('white');
ambientSoundNoise.amp(0); // Start silent
ambientSoundNoise.start(); // Start the noise (will be silent until amp() is called)
// Low-pass filter for the noise to make it less harsh
ambientFilter = new p5.Filter('lowpass');
ambientFilter.freq(500); // Only let low frequencies through
ambientFilter.res(2); // Some resonance for a "whoosh" effect
ambientSoundNoise.disconnect(); // Disconnect noise from master output
ambientSoundNoise.connect(ambientFilter); // Connect noise to filter
ambientFilter.connect(); // Connect filter to master output (default)
// --- Procedural Jumpscare Sound ---
jumpscareNoise = new p5.Noise('white');
jumpscareNoise.amp(0); // Start silent
jumpscareNoise.start(); // Start the noise
// Fast frequency sweep oscillator (sawtooth) for a screech
jumpscareOsc = new p5.Oscillator('sawtooth'); // Sawtooth for a harsh sound
jumpscareOsc.freq(1); // Start at 1 Hz (not 0 to avoid exponential ramp error)
jumpscareOsc.amp(0); // Start silent
jumpscareOsc.start(); // Start the oscillator
// Envelope for the jumpscare noise burst
jumpscareEnvelope = new p5.Envelope();
jumpscareEnvelope.setADSR(0.01, 0.2, 0.0, 0.5); // Fast attack, short decay, NO sustain, short release
jumpscareEnvelope.setRange(1.0, 0); // Full volume to silent
// Connect jumpscare noise to master output via its envelope
jumpscareNoise.amp(jumpscareEnvelope);
// --- Procedural Footstep Sound (re-added for top-down) ---
footstepOsc = new p5.Oscillator('sine');
footstepOsc.amp(0); // Start silent
footstepOsc.start();
footstepEnvelope = new p5.Envelope();
footstepEnvelope.setADSR(0.01, 0.1, 0, 0.1); // Quick attack, decay, no sustain, quick release
footstepOsc.amp(footstepEnvelope);
// --- Procedural Collect Sound ---
collectOsc = new p5.Oscillator('triangle');
collectOsc.amp(0); // Start silent
collectOsc.start();
collectEnvelope = new p5.Envelope();
collectEnvelope.setADSR(0.01, 0.05, 0, 0.2); // Very fast, short chime
collectOsc.amp(collectEnvelope);
// --- Procedural Boombox Echo Music ---
boombox = {
osc: new p5.Oscillator('triangle'), // A triangle wave for a melodic, slightly harsh sound
delay: new p5.Delay(),
nearbyThreshold: 100, // Distance to start hearing boombox music
fadeZone: 50 // Distance over which to fade out
};
boombox.osc.freq(220); // A4 note
boombox.osc.amp(0); // Start silent
boombox.osc.start();
boombox.osc.disconnect(); // Disconnect from master output
boombox.delay.process(boombox.osc, 0.5, 0.7, 2300); // 0.5 seconds delay, 70% feedback, 2300 Hz low-pass filter
boombox.delay.amp(0); // Start silent
// Delay will automatically connect to master output unless otherwise specified
// --- Procedural Loading Sound ---
loadingSoundOsc = new p5.Oscillator('sine');
loadingSoundOsc.freq(100); // Low frequency
loadingSoundOsc.amp(0); // Start silent
loadingSoundOsc.start();
loadingSoundEnv = new p5.Envelope();
loadingSoundEnv.setADSR(0.1, 0.5, 0, 0.5); // Slow attack, medium decay, no sustain, medium release
loadingSoundEnv.setRange(0.5, 0); // Max volume 0.5
loadingSoundOsc.amp(loadingSoundEnv);
}
Line-by-line explanation (17 lines)
🔧 Subcomponents:
ambientSoundOsc = new p5.Oscillator('sine'); ... ambientFilter.connect();
Creates a low-frequency drone (sine oscillator) and adds white noise through a low-pass filter to build atmospheric horror tension
jumpscareNoise = new p5.Noise('white'); ... jumpscareNoise.amp(jumpscareEnvelope);
Configures white noise and a sawtooth oscillator with an ADSR envelope to create the ear-piercing screech that plays when a monster catches you
footstepOsc = new p5.Oscillator('sine'); ... loadingSoundOsc.amp(loadingSoundEnv);
Sets up oscillators and envelopes for footstep feedback, key collection chime, boombox echo music, and loading screen drone
ambientSoundOsc = new p5.Oscillator('sine');- Creates a sine wave oscillator that will produce a low, atmospheric drone sound. Sine waves are smooth and pure-sounding, perfect for ambient horror tones.
ambientSoundOsc.freq(50);- Sets the oscillator's frequency to 50 Hz—very low, below most human speech, creating a felt rumble rather than a heard pitch.
ambientSoundOsc.amp(0);- Starts the oscillator silent (amplitude 0); we'll fade it in later when the game starts.
ambientSoundOsc.start();- Begins the oscillator running, but it produces no audible sound yet because amplitude is 0. This must happen before we can control it with amp().
ambientSoundNoise = new p5.Noise('white');- Creates white noise (all frequencies at equal intensity), which sounds like static and adds texture to the ambient soundscape.
ambientFilter = new p5.Filter('lowpass');- Creates a low-pass filter that will let only low frequencies through the noise, softening the harsh static into a whoosh.
ambientFilter.freq(500);- Sets the filter cutoff to 500 Hz—frequencies above 500 Hz are attenuated, leaving only the deep rumbling part of the noise.
ambientSoundNoise.disconnect();- Disconnects the noise from the default audio output chain so we can route it through our filter instead.
ambientSoundNoise.connect(ambientFilter);- Plugs the noise into the filter's input, so the filter processes it before it reaches speakers.
ambientFilter.connect();- Connects the filter's output to the master audio output (speakers), completing the audio chain: noise → filter → speakers.
jumpscareOsc = new p5.Oscillator('sawtooth');- Creates a sawtooth wave oscillator for the jumpscare screech. Sawtooth waves are harsh and rich with high-frequency harmonics, perfect for terrifying sounds.
jumpscareOsc.freq(1);- Starts at 1 Hz (a very low, barely audible frequency) so we can sweep it upward when the jumpscare happens without triggering Web Audio errors.
jumpscareEnvelope = new p5.Envelope();- Creates an ADSR (Attack, Decay, Sustain, Release) envelope object that will shape the jumpscare sound—controlling how it fades in, decays, and fades out.
jumpscareEnvelope.setADSR(0.01, 0.2, 0.0, 0.5);- Sets the envelope timing: 0.01s attack (snappy start), 0.2s decay (quick drop), 0.0s sustain (no held level), 0.5s release (long fade out), making a sharp, shocking spike.
jumpscareNoise.amp(jumpscareEnvelope);- Links the noise's amplitude directly to the envelope, so when the envelope plays, it controls the noise's volume automatically.
boombox.delay.process(boombox.osc, 0.5, 0.7, 2300);- Connects the boombox oscillator to a delay effect: 0.5s delay time (echo repeats after half a second), 0.7 feedback (echoes decay slowly), 2300 Hz filter (keeps echoes from getting too harsh).
loadingSoundOsc.amp(loadingSoundEnv);- Links the loading sound oscillator to its envelope, so the envelope controls its volume when played.