The Algebra of Chords

AI authored to showcase ironpad capabilities.

Introduction

Cm7b5 is a five-character sentence with four clauses. The C names a root, the m bends the third down a half step, the 7 stacks a minor seventh on top, and the b5 flattens the fifth. Chord symbols are a tiny formal language, dense and unambiguous, and like any language they can be parsed, evaluated, and reasoned about mechanically.

That is what kord does, and this one is near and dear: I wrote it years ago to learn music theory properly, by forcing myself to encode the rules instead of half-remembering them. A few hundred GitHub stars and something like twenty-four thousand downloads later, it still starts every explanation the same way, by turning a symbol into the exact notes it stands for.

Every code cell below runs the real crate. kord is compiled to WebAssembly and executed in your browser, the same library compiled to the same target that powers its own playground. Parse a chord, stack some intervals, enumerate the modes of a scale, hand it a fistful of notes and make it name them, then chase equal temperament down to the fraction of a cent where it stops being exact. All of it, live.

The library crate is named klib, a historical accident I have made peace with. Everything starts with Chord::parse.

Parsing a family of chords

A chord is intervals, an interval is arithmetic

Pull on the thread and the whole subject unravels into counting. An interval is a number of semitones, and a semitone is one step of the twelve the octave is divided into. A minor third is 3 semitones, a major third is 4, a tritone is 6. That is the entire definition; everything else is names we hang on the counts so we can talk about them.

Western harmony is built by stacking thirds, which is why chords look the way they do. Take a root, go up a third, up another third, up another, and you have a triad and then a seventh chord. Which thirds you stack, major (4 semitones) or minor (3), is what makes a chord bright or dark. A half-diminished chord, our Cm7b5, is the clearest case: two minor thirds stacked, then a major third on top. Minor, minor, major; 3 + 3 + 4.

The next cell proves it without trusting a single one of my labels. It takes the tones kord produced for Cm7b5 and recovers each interval straight from the frequencies, because the ratio of two frequencies is the interval between them: twelve times the base-2 logarithm of f_high / f_low counts the equal-tempered steps. Each note knows its own frequency, so the arithmetic is exact.

The stack inside Cm7b5

Scales and modes: the same trick, one level up

A scale is the same construction as a chord, just a larger interval set over a root. The major scale is a fixed pattern of seven intervals; root it anywhere and you get that key. Keep the seven notes and start on a different one.

Those are the modes. Play the white keys from C and you get C major, familiar and resolved. Play the exact same white keys starting from D and the character changes completely: same notes, different center of gravity, and suddenly it reads as a minor scale with an oddly bright sixth. That is D Dorian. Start on E and it turns dark and Spanish (Phrygian); start on F and it turns bright and floating (Lydian, where the raised fourth is the tell); start on B and it can barely stand up (Locrian, built over a diminished fifth).

kord stores each mode's intervals explicitly rather than deriving them by rotating a scale, which is a deliberate correctness choice: it keeps the enharmonic spelling honest, so D Dorian comes back spelled with the right letters and never a stray double flat. The next cell walks all seven modes of C major and asks each one for its notes and its one-line character.

The modes of C major

The party trick: name that chord

Everything so far ran one direction, symbol to notes. It also runs the other way. Hand kord a bag of notes and it tells you what chords they are, ranked simplest first.

This is harder than parsing, because the answer is not unique. The same three or four pitches can be spelled as several different chords depending on which note you call the root, whether you allow an inversion, whether one of them is a slash bass. So try_from_notes does the obvious thing thoroughly: it tries plausible roots and voicings across octaves, keeps every candidate whose tones match your notes exactly, and sorts them by simplicity, fewest modifiers, fewest extensions, no needless slash. The cleanest reading floats to the top.

It is the same routine behind kord's guess command, and behind the microphone mode that listens to you play and prints the chord back. The next cell feeds it three note sets, from a plain triad to a spicier one, and shows the top three readings for each. The first column is what you played; the second is kord thinking out loud.

Chord inference from notes

Everyone is playing slightly out of tune

The frequencies kord reports are a compromise, which surprised me when I encoded it. note.frequency() returns twelve-tone equal temperament: the octave sliced into twelve identical ratios, each a factor of 2^(1/12), with A4 nailed to 440 Hz. Clean, uniform, and slightly wrong almost everywhere.

Wrong compared to what? To the intervals physics actually prefers. The consonances your ear locks onto are small whole-number frequency ratios: the octave is exactly 2/1, the perfect fifth wants to be 3/2, the major third wants 5/4. These are not conventions, they fall straight out of the overtone series (the next section). Equal temperament hits none of them except the octave. It cannot: no power of 2^(1/12) equals 3/2. So it misses every one of them by a little, on purpose.

The point of missing on purpose is that it misses evenly. Tune a keyboard to pure ratios in C and it will sound glorious in C and sour three keys away, because the errors that cancel in one key pile up in another. Equal temperament spreads the error uniformly so every key is equally usable and equally, tolerably wrong. You can modulate anywhere and never retune.

Now the numbers, because "slightly wrong" deserves quantifying (a cent is 1/100 of a semitone, so 1200 to the octave). The equal-tempered major third lands about 13.7 cents sharp of the pure 5/4, roughly an eighth of a semitone, enough to hear as a faint buzz under a sustained major chord. The fifth gets off nearly free, under 2 cents flat. And the harmonic seventh, the pure 7/4 the overtone series really produces, sits a full 31 cents below the tempered minor seventh, which is why a barbershop quartet, free to bend into the pure ratio, can make a dominant seventh ring in a way a piano simply cannot. The next cell computes all of it from kord's own frequencies.

Cents of error, tempered vs pure

Where the pure ratios come from

The whole-number ratios are not arbitrary targets; they are what a physical string hands you for free. Pluck one and it vibrates at a fundamental frequency plus, simultaneously, every integer multiple of it: 2x, 3x, 4x, 5x, on up. Those overtones are the harmonic series, and their ratios to the fundamental are the pure intervals. The 2nd harmonic (2x) is the octave, 2/1. The 3rd (3x) is an octave plus a pure fifth. The 5th is two octaves plus a pure major third, the 5/4 from the last section.

So the pure intervals are just the first few harmonics folded back into a single octave, and equal temperament is our best twelve-note approximation of that natural set. The plot below sounds the first eight harmonics of C and places each one in cents above the fundamental. The equal-tempered grid sits at exact multiples of 100; watch the harmonics drift off it as they climb, the 5th and 7th missing their nearest tempered step by the amounts we just measured.

Harmonics of C, plotted

What kord is, past these cells

The slice you just ran is the deterministic core, and it is the smallest part. The full crate is a command-line tool that will describe, play, guess, and loop through chord changes out loud; a JavaScript and WebAssembly package (kordweb on npm) that drives the browser playground; and, behind feature flags, the fun stuff: audio analysis that pulls notes out of a microphone, and an ML model that infers a chord from raw sound when the deterministic guesser is not enough. None of that fit in a notebook cell, but all of it hangs off the same Note, Chord, and Interval types you have been poking at.

One footnote on the plumbing. These cells compile against kord 0.8.0 from crates.io. For a while they had to pin a git revision instead, because the previous published version (0.7.1) predated a nightly feature removal and no longer built on the toolchain these cells use; 0.8.0 put the crate back on the regular release train.

If any of this scratched an itch, the repository is the place to start, the playground is the place to play, and the docs are the place to go deeper. I wrote it to understand music theory by building it. Turns out that is also a decent way to explain it.