Pitch as Architecture
The Western classical answer to "what is music": pitches locked in ratios, stacked into chords, marching through time toward resolution. It is the most machine-friendly music theory ever devised — by this afternoon your code composes with it.
- derive the twelve-note system from a vibrating string
- compute any equal-tempered pitch with 440 × 2^(n/12)
- represent a scale as an array and a melody as a sequence
- run a Tone.js loop that improvises in a major scale, forever
10:00 — Ugly / Pretty
Homework playback, ugliest first, timestamps declared. After each pretty sketch, one question: what made it pretty? Collect the answers on the board — they will almost all turn out to be about which frequencies (today's subject) and when they change (Thursday's).
10:30 — From One String to Twelve Notes
Yesterday's spectrum demo showed that a single note is a stack of harmonics: a fundamental f, plus 2f, 3f, 4f, 5f… That physical fact is the seed of all Western harmony:
| Ratio | Interval | Where it comes from |
|---|---|---|
| 2 : 1 | octave | 2nd harmonic — so alike we give both notes the same name |
| 3 : 2 | perfect fifth | 3rd harmonic (folded down an octave) |
| 4 : 3 | perfect fourth | octave minus fifth |
| 5 : 4 | major third | 5th harmonic (folded down two octaves) |
Each button plays a pure sine at a multiple of 110 Hz. Play 1× then 2× — hear "the same note, higher." Then hold combinations in your ear: 2× with 3× is a fifth; 4× with 5× is a major third. Harmony is audible arithmetic.
The problem: build a scale from pure ratios and the math never closes — twelve perfect fifths overshoot seven octaves by a small, audible gap (the Pythagorean comma). Every tuning system in history is a different answer to "where do we hide the gap?"
The Western answer (c. 1700s, universal by the 1800s): smear it equally. Divide the octave into twelve exactly equal steps, each multiplying frequency by 21/12 ≈ 1.0595. Now no interval except the octave is perfectly pure — the fifth lands at 1.4983 instead of 1.5 — but every key is equally usable, and any instrument can play with any other. Equal temperament is a lossy compression codec for harmony: a little purity traded for total interoperability. (Tomorrow we meet a tradition that refused the trade.)
One octave from A (220 Hz) to A (440 Hz). Every neighbouring pair has the identical ratio 21/12. The white-named buttons are A major's seven; hear how the twelve-step grid contains the scale.
—
Scales are subsets of the twelve: major picks steps 0 2 4 5 7 9 11, natural minor picks 0 2 3 5 7 8 10. Chords are subsets of scales (1st, 3rd, 5th degrees). Cadence — tension, then home — is the syntax that makes a chord progression feel like a sentence. That is the whole apparatus: ratios → grid → subsets → syntax.
12:00 — A History in Forty Minutes
- c. 900 Gregorian chant — one line, no harmony Music as a single voice in time. Notation is being invented to preserve it — melody becomes data.
- c. 1200 Pérotin — early polyphony Two, three, four simultaneous lines: now the ratios between voices matter. Harmony begins.
- 1722 Bach — Well-Tempered Clavier, Prelude in C A manifesto for tempered tuning: one book, all 24 keys, one keyboard. The grid wins.
- 1808 Beethoven — Symphony No. 5, opening Four notes as a building block, developed for half an hour. Pitch architecture at maximum scale.
- 1976 Philip Glass — Einstein on the Beach (excerpt) Process music: simple material + a rule, repeated. Composition already halfway to code.
Why this tradition exports so well to machines: it spent eight centuries making itself discrete. Notation quantized pitch to symbols; equal temperament quantized tuning to one formula; the metronome quantized time. By 1983, MIDI could reduce "play middle C" to the number 60 — because the reduction had already been done on paper.
14:00 — The Formula
Everything above compresses into one line of JavaScript:
function freqOf(n) { // n semitones above A4
return 440 * Math.pow(2, n / 12); // 440 × 2^(n/12)
}
// freqOf(0) → 440 A4
// freqOf(12) → 880 A5, one octave up
// freqOf(3) → 523.25 C5
// freqOf(-9) → 261.63 C4, middle C
And a scale is an array of steps applied from a root:
let root = -9; // middle C, as semitones from A4
let major = [0, 2, 4, 5, 7, 9, 11]; // the major-scale subset
// the C major scale, computed rather than memorized:
for (let i = 0; i < major.length; i++) {
print( freqOf(root + major[i]) );
}
Sit with what just happened: 500 years of European music theory became two arrays and one formula. Swap the array, get minor. Add 7 to the root, you've transposed to G. This is why the machines took to it so readily.
15:00 — Tone.js and the Generative Melody
Tone.js is a framework over the browser's audio engine: ready-made synths, a musical clock, effects. To use it in the p5 editor: open the sketch's index.html (left panel ▸) and replace the p5.sound script line with:
<script src="https://unpkg.com/tone@14.7.77/build/Tone.js"></script>
p5.sound (Days 1–2) and Tone.js (today onward)
conflict if loaded together — a sketch uses one or the
other, never both. From today, new sound sketches are Tone sketches: swap
the script line, and unlock audio with Tone.start() instead of
userStartAudio(). p5 itself stays — it still draws every pixel
and reads every touch.
First sound — a synth object (Day 2: objects take messages):
let synth;
function setup() {
createCanvas(400, 400);
synth = new Tone.Synth().toDestination(); // build it, wire it to speakers
}
function mousePressed() {
Tone.start(); // audio unlock, once
synth.triggerAttackRelease("C4", "8n"); // note name, eighth-note long
}
Tone speaks both note names ("C4") and raw hertz — so our
freqOf() plugs straight in. Now the piece that composes itself:
let synth;
let major = [0, 2, 4, 5, 7, 9, 11];
let root = -9; // C major
let degree = 0; // where the melody stands
function freqOf(n) { return 440 * Math.pow(2, n / 12); }
function setup() {
createCanvas(400, 400);
synth = new Tone.Synth().toDestination();
}
function mousePressed() {
Tone.start();
Tone.Transport.bpm.value = 96; // the clock's tempo
new Tone.Loop((time) => {
// random walk: melodies move mostly by step, occasionally leap
let move = random([-1, -1, 1, 1, 1, -2, 2, 4]);
degree = constrain(degree + move, 0, 13); // two octaves of room
let oct = floor(degree / 7) * 12; // which octave of the scale
let step = major[degree % 7]; // which scale member
synth.triggerAttackRelease(freqOf(root + oct + step), "8n", time);
}, "8n").start(0); // run every eighth note
Tone.Transport.start();
}
Listen first. Then experiment, one change at a time:
- Uniform randomness vs the walk: replace the walk with
degree = floor(random(14))— hear how it stops sounding like a melody. Constraint is what makes randomness musical. - Swap in natural minor [0,2,3,5,7,8,10]. Same code, different weather.
- Rests:
if (random() < 0.2) return;at the top of the loop. Silence is a note. - Rhythm: change the loop interval to
"16n", or trigger with durationrandom(["8n","4n","2n"]).
A generative melody study in a Western scale. Start from today's random walk and make it yours: choose scale, tempo, rhythm, rests, register, maybe a second voice. Save the sketch with a finish timestamp. Tomorrow the same sketch learns a raga — part 2 — and Friday morning we hear both versions of each.