Reverbs · Interactive Media Engineering

How synths
make sound

There is no recording hidden in here. Every sound on this page is built, live, out of math — and the curve you watch is the sound you hear. Drag the panels. Listen.

Scroll to begin
00 The first sound

Tap it. It sings.

A synthesizer's heart is an oscillator — a thing that wobbles a number up and down, over and over, very fast. Send that wobble to a speaker and the speaker cone pushes air in the same pattern. Air hits your ear. You call it a note.

This panel is one oscillator wobbling 220 times a second. Nothing is recorded. Tap it.

a string the sound plucks
tap to play

That isn't a recording playing back — it's a mass-spring string, and the sound is the force plucking it. The wobble you see is the wobble you hear.

The code that is this sound
// no audio file — the note is three lines of math made audible
const ctx = new AudioContext();
const osc = ctx.createOscillator();   // the thing that wobbles
osc.frequency.value = 220;          // 220 wobbles per second = the pitch
osc.connect(ctx.destination);     // wire it to the speaker
osc.start();                          // air starts moving. you hear a note.

// and the shape it traces over time is just:
//   amplitude(t) = A · sin(2π · f · t)
01 Pitch

Faster wobble, higher note.

Pitch is nothing but how fast the wobble repeats — its frequency, measured in cycles per second, or hertz (Hz). Double the frequency and you hear the note jump up an octave.

Hold the panel and drag left and right. Hear the pitch climb — and watch the wave squeeze tighter as the cycles speed up. Same shape, just faster.

— Hz
Drag ← → frequency

Top: the wave in time. Bottom: its spectrum — for a pure sine, a single spike at one frequency. We'll come back to that bottom view; it's the whole Signal track.

Let's make "how fast" exact. One full trip of the wave — up, down, back — is one cycle. The time for one cycle is the period T, and frequency is just how many periods fit in a second:

f = 1 / T frequency (Hz) is the reciprocal of the period (seconds)

So our 220 Hz tone has a period of T = 1/220 ≈ 0.00455 s — each cycle lasts about 4.5 milliseconds. That's why the wave on the panel looked so tight: 220 complete trips every second.

But where does that perfectly smooth shape come from? A sine wave isn't drawn by hand — it's the shadow of a point going around a circle at a steady speed. Watch: as the dot circles, its height at each instant traces out the wave. One full lap is one cycle. We measure the lap in radians, where a whole turn is , so the angle after time t is θ = 2π·f·t. Press play, then drag slow → fast and watch the lap speed up — and hear the pitch rise with it.

slowfast
f = 220 Hzθ = 0.00πsin θ = 0.00
height(t) = A · sin(θ),   θ = 2π · f · t spin f times per second → the shadow wobbles f times per second → you hear pitch f
one object, three languages

Mathematician: a sine is the projection of uniform circular motion, sin θ with θ = 2πft. Musician: that projection, pushed into the air, is the purest possible tone — a single clean pitch. Artist: it's a luminous point dancing on a ring, leaving a trail. Learn to see all three at once and you never "memorize" the sine again — you just know it.

Now the musical part. Your ear doesn't hear frequency differences — it hears frequency ratios. Double the frequency and you always hear the same jump: an octave. 110 → 220 is an octave; 220 → 440 is the same octave again. Pitch is logarithmic in frequency — which is exactly why dragging the panel feels even-spaced to the ear even though the Hz are racing ahead. (The drag maps x to frequency on a log scale on purpose.)

Western tuning splits each octave into 12 equal ratio steps (semitones). "Equal" means equal multiplicatively, so each step multiplies frequency by the twelfth root of two:

r = 21/12 ≈ 1.05946 one semitone up = multiply the frequency by r

Twelve of those steps multiply back to exactly r12 = 2 — one octave. Anchoring the whole system to A4 = 440 Hz, any note's frequency is:

f(n) = 440 · 2(n − 69)/12 n = MIDI note number; A4 is n = 69. Each +1 in n is one semitone.

Work a few out by hand and you get the keyboard:

NoteMIDI nCalculationFrequency
A357440 · 2−12/12 = 440 · ½220.00 Hz
C4 (middle C)60440 · 2−9/12261.63 Hz
E464440 · 2−5/12329.63 Hz
A469440 · 20440.00 Hz
A581440 · 212/12 = 440 · 2880.00 Hz
JavaScript · Web Audio
// turn any MIDI note into a pitch the oscillator can play
const midiToFreq = n => 440 * 2 ** ((n - 69) / 12);

midiToFreq(69);   // 440      → A4
midiToFreq(60);   // 261.63   → middle C
osc.frequency.setValueAtTime(midiToFreq(64), ctx.currentTime);  // play E4
Python · NumPy
import numpy as np

def midi_to_freq(n):
    return 440.0 * 2 ** ((n - 69) / 12)

# synthesize 1 second of A4 at 44.1 kHz — the same sine the panel plays
fs = 44100
t  = np.arange(fs) / fs
x  = np.sin(2 * np.pi * midi_to_freq(69) * t)   # x[k] = sin(2π·440·k/fs)
02 Shape = timbre

Same pitch. Different character.

A sine is one pure tone. But most sounds are richer — a violin and a flute can play the exact same pitch yet sound nothing alike. That difference is timbre, and here is the secret of where it lives:

A bright, buzzy wave isn't one frequency — it's a stack of sine waves, the fundamental plus harmonics above it. Drag right to grow the stack. The pitch never changes, but the sound gets brighter — and watch the spectrum sprout new spikes, one per harmonic. Timbre is a picture of the spectrum.

drag to add harmonics
Drag → harmonics

Each classic shape is just a different recipe of harmonics. A saw has them all; a square has only the odd ones; a triangle, odd ones that fade fast. You can hear the recipe.

Here is the single most important idea in all of audio, and you just felt it: any periodic wave, no matter how jagged, is a sum of pure sine waves at integer multiples of one fundamental frequency. That's Fourier's theorem. The fundamental f₀ sets the pitch; the harmonics at 2f₀, 3f₀, 4f₀… set the timbre. The recipe — how loud each harmonic is — is the entire identity of the sound.

Take the sawtooth. Its recipe is "every harmonic, fading as 1/n":

x(t) = (2/π) · Σn=1  (−1)n+1 · (1/n) · sin(n·2πf₀·t) harmonic n has magnitude proportional to 1/n — the higher, the quieter

The square wave is stranger: only the odd harmonics survive (1st, 3rd, 5th…), each still fading as 1/n. That missing-even-harmonics fingerprint is exactly why a square sounds hollow and clarinet-like:

x(t) = (4/π) · Σk=1,3,5,…  (1/k) · sin(k·2πf₀·t)

Don't take Fourier's word for it — build it with your hands. Each bar below is one harmonic. Drag it up to pour in more of that sine; the waveform on top reshapes and the tone changes the instant you move. Hit play, then try the presets: one bar is a pure tone, the 1/n staircase is a saw, odd-harmonics-only is a square. Then do the mathematician's move — skip the sliders and type the recipe as code, a function of the harmonic number n. You are literally writing Fourier coefficients and hearing them.

1f
2f
3f
4f
5f
6f
7f
8f
amp =
try: n => 1/n (saw) · n => n%2 ? 1/n : 0 (square) · n => 1/(n*n) (soft)

Let's make the saw concrete. Play a sawtooth at f₀ = 110 Hz and work out where the energy lands and how loud each partial is, in decibels relative to the fundamental (dB = 20·log₁₀(amplitude ratio)):

Harmonic 1 → 110 Hz, magnitude 1/1 = 1.0020·log₁₀(1) = 0 dB (the reference).
Harmonic 2 → 220 Hz, magnitude 1/2 = 0.5020·log₁₀(0.5) = −6.0 dB.
Harmonic 3 → 330 Hz, magnitude 1/3 = 0.3320·log₁₀(0.333) = −9.5 dB.
Harmonic 4 → 440 Hz, magnitude 1/4 = 0.25−12.0 dB. Every doubling of n drops the partial another 6 dB.

Those are the spikes you watched march up the spectrum when you dragged. Drag again and count them — that descending staircase is the 1/n rule with your own eyes. An ideal saw needs infinitely many; a real synth uses a finite stack, and the abrupt cutoff makes the corners ring slightly — the Gibbs phenomenon.

Building a wave by adding sines is called additive synthesis. Here it is for real — and notice the code is just the formula above, typed out:

JavaScript · additive sawtooth
function sawSample(t, f0, N) {        // sum the first N harmonics at time t
  let x = 0;
  for (let n = 1; n <= N; n++)
    x += ((-1) ** (n + 1)) / n * Math.sin(n * 2 * Math.PI * f0 * t);
  return (2 / Math.PI) * x;
}
JavaScript · how the panel actually does it (PeriodicWave)
// instead of summing oscillators, hand Web Audio the harmonic amplitudes
// directly. imag[n] IS the magnitude of harmonic n — the recipe.
const N = 28, imag = new Float32Array(N + 1), real = new Float32Array(N + 1);
for (let n = 1; n <= N; n++) imag[n] = 1 / n;   // 1/n  → a sawtooth
const wave = ctx.createPeriodicWave(real, imag);
osc.setPeriodicWave(wave);   // ← this exact call is what the Morph slider drives
Python · NumPy
def saw(f0, fs=44100, dur=1.0, N=28):
    t = np.arange(int(fs * dur)) / fs
    x = sum((-1)**(n+1) / n * np.sin(n * 2*np.pi*f0 * t) for n in range(1, N+1))
    return (2/np.pi) * x

# the harmonic magnitudes [1, 1/2, 1/3, ...] are the saw's spectrum —
# and np.fft.rfft(saw(110)) would hand them right back to you.
The bridge to the Signal track

You just went one direction: recipe → wave (additive synthesis). The whole Stanford Signal track is the other direction: wave → recipe — taking a sound you didn't build and recovering its hidden harmonic amplitudes. That reverse trip is the Discrete Fourier Transform. The spectrum bars on these panels are a live DFT running 60 times a second. When you reach Module 2 · The DFT, you'll write it yourself — and it will feel obvious, because you already built its inverse here with your hands.

03 Loudness & life

Two hands on one instrument.

The last ingredient is amplitude — how big the wobble is, which your ear reads as loudness. Pitch lives left-to-right; loudness lives top-to-bottom. Now the panel is a real instrument.

Drag anywhere: for pitch, for volume. Play a little melody.

— Hz · —%
x pitch · y loudness

Real synths shape amplitude over time with an envelope — the difference between a pluck and a pad. That's the next lesson.

Loudness is slippery: your ear, like with pitch, hears ratios, not raw amplitude. Doubling the wave's height does not sound "twice as loud." So we measure level on a logarithmic scale — decibels:

L = 20 · log₁₀( A / Aref ) level in dB, from the ratio of an amplitude to a reference amplitude
Double the amplitude: A/A_ref = 220·log₁₀(2) = +6.02 dB. Every doubling adds ~6 dB.
Ten times the amplitude: 20·log₁₀(10) = +20 dB exactly.
Half the amplitude: 20·log₁₀(0.5) = −6.02 dB — the same 6 dB you saw between saw harmonics.

For a pure sine, "amplitude" is obvious — the peak height. For a complex wave (a saw, a voice) the honest measure of size is the root-mean-square: square every sample, average, square-root. It's the loudness-relevant energy of the whole waveform:

Arms = √( (1/N) · Σ x[k]² )

But constant loudness is lifeless — it's a fog-horn. What makes a note feel plucked, bowed, or swelling is how its amplitude moves over time: the envelope, classically Attack–Decay–Sustain–Release. A pluck is a fast attack and quick decay; a pad is a slow attack and long release. Same oscillator, completely different instrument:

JavaScript · Web Audio · a plucked envelope
const now = ctx.currentTime, g = gain.gain;
g.setValueAtTime(0, now);
g.linearRampToValueAtTime(1.0, now + 0.005);   // A: 5 ms snap to full
g.exponentialRampToValueAtTime(0.3, now + 0.15);// D: drop to sustain
// ... hold at sustain while the key is down ...
g.exponentialRampToValueAtTime(0.001, now + 0.6);// R: tail out
// (exponential ramps because loudness is perceived in dB, not linearly)
Python · NumPy · the same shape as a multiplier
def pluck_env(n, fs=44100):
    t = np.arange(n) / fs
    return np.exp(-t * 6.0)          # fast exponential decay

x = saw(110) * pluck_env(len(saw(110)))  # a plucked 110 Hz saw
rms = np.sqrt(np.mean(x**2))           # its loudness-relevant level
where the verticals become one

One signal, three arts.

Everything so far has secretly been the same number. The pitch you heard, the wave you watched, the spectrum bars, the harmonic code you typed — one stream of samples, read in different languages. So here is the whole lesson as one instrument you play. Touch the bloom and drag: left↔right is pitch (Chapter 1), up↕down is timbre (Chapter 2), and pressing and letting go is the envelope (Chapter 3). Play a little — the flower is the live spectrum, and the tags below light up to name the idea behind each move.

← → pitch · Ch.1 ↑ ↓ timbre · Ch.2 press = envelope · Ch.3
hear a concept again →

Touch and drag the bloom: where you press is the note, how high you reach is the brightness. Let go and the flower fades — that's the envelope.

this is the merge — answer to "how do the verticals join?"

Music (Ableton): you hear it, and you steer it by hand. Math (Stanford): the bloom is grown entirely from the live spectrum — and the act of turning those raw samples into a spectrum is precisely the Discrete Fourier Transform. A pure sine is one bright seed; a saw is a full flower, because it has more frequencies. The art is a portrait of the DFT. Art (Nature of Code): each frequency drops a petal along a golden-angle spiral, so richer sound literally blooms.

The verticals don't sit side by side as separate courses — they're one object you can hold, at once sound, equation, and image. That is Reverbs.

And it scales straight to the destination: replace this oscillator with a live Lyria-class music model and the very same bloom becomes a cinematic visual conducted by AI-generated music, over an ambient bed — the generative finale this whole project is built toward. Nothing in the drawing code changes; only the source of the signal does.

You've now used the spectrum a dozen times but never built the machine that computes it. That machine is the DFT — and it's the doorway into the Stanford Signal track. You already know what it's for: it's the thing drawing this flower.

See the Signal track → (DFT next)
04 Playground

Now just play.

You've built the whole chain: an oscillator, a pitch, a timbre, a volume. That's a synthesizer. Everything after this — filters, LFOs, the Fourier transform that reads these spectra — is a refinement of what your hands already understand.

Open the full Playground →

The curve you were dragging is the sound you were hearing.

Sources go deeper

Xavier Serra et al., Audio Signal Processing for Music Applications (Stanford / UPF, Coursera) — the Signal track's spine. · Ableton, Learning Synths — the interaction grammar. · Daniel Shiffman, The Nature of Code — the generative visuals. · MDN, Web Audio API — the engine under every panel.