Oscillators & Synthesis

iHearYouCanCode — by David Stein
Make beeps, change how they sound, and play with on-screen controls
↗ p5.sound Docs
Haven't set things up yet? Do Getting Started first — it shows you how to add p5.js, p5.sound, and ProControls.

What is an oscillator?

An oscillator is something that moves back and forth in a regular, repeating pattern — like a pendulum or a guitar string vibrating. In audio, an oscillator generates a waveform: a signal that rises and falls at a set speed (its frequency, measured in Hz) and height (amplitude, which we hear as volume). That repeating wave is what travels through your speakers and becomes sound.

Oscillators are the fundamental building blocks of sound synthesis. Even complex, rich sounds — a violin, a human voice, a distorted guitar — can be understood as a combination of many simple oscillators layered together at different frequencies and volumes.

This idea goes back to the mathematician Joseph Fourier, who showed in the early 1800s that any repeating waveform can be broken down into a sum of simple sine waves. That means a square wave, a sawtooth, or a harsh buzzing sound is really just a pile of smooth, pure sine waves added together at precise ratios. Flip it around and you can build complex sounds by stacking simple ones — which is exactly what a synthesizer does.

Try it: Harmonics Explorer by Tero Parviainen lets you add and remove sine wave harmonics and hear how they combine into more complex waveforms in real time — a great way to see Fourier's idea in action before writing any code.

1. Click to start your first Oscillator

This sketch makes a tone you turn on and off by clicking the canvas. On the first click, call userStartAudio() so the browser allows sound. Then use osc.start() to play and osc.stop() to stop. Try editing the code below — the preview updates as you type.

Try editing the code — the preview updates as you type

Why do I need to click first?

Browsers won't play sound until you click something — it's a safety rule. userStartAudio() in mousePressed() turns sound on. osc.started tells you if the tone is playing: true means on, false means off.

2. Use your mouse as a control

Now your mouse controls the sound while it's playing. Move left and right to change pitch (how high or low). Move up and down to change volume (how loud). map() turns mouse position into numbers the oscillator can use. Click still turns the sound on and off.

Try editing the code — the preview updates as you type

3. Use on-screen controls

Swap the mouse for ProControls — knobs and pads on the screen. The XYPad works the same way: drag to change pitch and volume. The Selector lets you pick the wave shape — sine (smooth), square (buzzy), sawtooth (harsh), or triangle (soft). Set ControlStyle before you create controls (this picks how they look). Call proControlBackground() in draw(). In each control's onChange, call userStartAudio().

Try editing the code — the preview updates as you type

4. The p5.Envelope object

So far, your oscillator is either on or off — like flipping a light switch. A p5.Envelope makes volume change over time: fade in, hold, fade out. That's how real instruments sound — a piano note doesn't pop on and off instantly.

An envelope doesn't make sound by itself. You connect it to something that does — usually an oscillator's volume with osc.amp(env). The envelope then acts like an automatic volume knob that moves through four stages called ADSR (Attack, Decay, Sustain, Release). You'll tweak those numbers in the next section.

Connect and configure an envelope

Create an envelope, set its shape with setADSR(), set how loud it gets with setRange(), then plug it into the oscillator:

var osc, env;

env = new p5.Envelope();
env.setADSR(0.1, 0.2, 0.5, 0.3);  // attack, decay, sustain, release (seconds / level)
env.setRange(0.8, 0);         // attack level (loudest), release level (silent)

osc = new p5.Oscillator('sine');
osc.freq(440);
osc.start();                     // oscillator runs; envelope controls its volume
osc.amp(env);                    // route volume through the envelope

setRange(attackLevel, releaseLevel) sets the loudest and quietest points. The first number is how loud the note gets at its peak; the second is where it fades to when released. Usually that's setRange(0.8, 0) — loud attack, silent release. Stay below 1 to protect your ears. The third argument to setADSR() is the sustain level (0 to 1), not a time — it's how loud the note holds while you're "holding the key."

play() vs triggerAttack() and triggerRelease()

There is no env.start() — envelopes use different methods depending on how you want the note to behave:

env.play(osc, startTime, sustainTime) — plays a complete note in one call. It runs attack → decay → sustain for sustainTime seconds → then release automatically. Good for a quick click where you don't need to hold anything down.

// One-shot note: wait 0 seconds, hold sustain for 0.5 seconds, then release
env.play(osc, 0, 0.5);

env.triggerAttack(osc) and env.triggerRelease(osc) — split the note into two steps, like a piano key. triggerAttack() runs attack and decay, then holds at the sustain level until you call triggerRelease(), which runs the release fade. Use these when the player holds a mouse button, key, or pad — you decide how long the note lasts.

function mousePressed() {
  userStartAudio();
  env.triggerAttack();   // no osc needed — already wired in setup
}

function mouseReleased() {
  env.triggerRelease();
}
Which should I use? Use play() for tap-to-play notes (drums, plucks). Use triggerAttack() / triggerRelease() when the player holds a control and you want the note to last exactly as long as they hold it.

Other useful methods and properties

These come up often once you start building instruments:

See the full API in the p5.Envelope reference.

Try it below — hold the mouse button down to play a note, let go to release it.

Try editing the code — the preview updates as you type

5. Shape each note with ADSR controls

Now put it all together with ProControls so you can see and tweak the envelope shape live. ADSR stands for Attack (how fast the note fades in), Decay (how fast it drops after the attack), Sustain (how loud it holds), and Release (how long it fades out). The MultiSlider controls change volume, pitch, and ADSR values. The ADSRDisplay draws the shape on screen. Click the canvas to fire a one-shot note with env.play(osc).

Try editing the code — the preview updates as you type