Plomp-Levelt roughness summed over every partial pair: where consonance comes from.
Two tones a given interval apart are compared partial by partial: every pair, one partial from each tone, adds roughness from Sethares' fit to the Plomp-Levelt data, which is zero at unison, peaks at about a quarter of a critical band, and fades beyond it. The sum, plotted against the interval in cents, dips exactly where partials coincide, so a harmonic timbre puts its consonances at 6:5, 5:4, 4:3, 3:2, 5:3 and 2:1, and each dip is found, refined with a parabola and labelled with its nearest simple ratio. Stretch the partials, or switch to a bell, a xylophone bar or a gamelan kettle, and the curve morphs and the dips slide away from the familiar intervals, which is why inharmonic instruments call for their own scales. Below, the two spectra face each other on a log frequency axis with every beating pair joined by an arc weighted by its share, and the scope shows the summed waveform with the envelope of the roughest pair; after a click both tones play as 32 sine oscillators, so the beating can be heard.
Try it. Drag across the plot to slide the interval and hear the beating rise and fall; Space jumps to the next dip. Click a timbre chip or press 1 to 7, press [ and ] to stretch or compress the partials, and drag any partial of the lower tone in the spectrum panel to change its pitch and loudness. Left and right nudge the interval by a cent (Shift for ten), up and down move the register an octave, M mutes.
Paste this into Claude Code, Codex or any coding agent to get a simple version running, then take it wherever you like.
Build an interactive dissonance curve, the plot that shows why some musical intervals sound smooth and others rough, with JavaScript, the HTML canvas element and the Web Audio API. Put everything in a single index.html file with no libraries or build step, so I can open it directly in a browser.
Start simple:
- Make a full-window, high-DPI canvas with a dark background.
- Describe a tone as a list of partials (frequency multiples and amplitudes). Start with a harmonic tone: partials 1 to 10 with amplitudes 0.88^(n-1).
- Write the Plomp-Levelt roughness of two sine partials using Sethares' fit: s = 0.24 / (0.0207 * fLow + 18.96), d = min(a1, a2) * (exp(-3.5 * s * df) - exp(-5.75 * s * df)), where df is the gap in Hz.
- For intervals from 0 to 1300 cents above middle C, sum that roughness over every pair of partials (one from each tone) and plot it as a smooth curve. You should see dips at 6:5, 5:4, 4:3, 3:2, 5:3 and 2:1.
- Draw faint vertical lines at every equal-tempered semitone with labels like m3, P5, P8.
Then make it playable:
- Add a draggable vertical cursor for the interval.
- On the first click, start an AudioContext (show a "Click for sound" hint until then) and play both tones as banks of sine oscillators, updating the upper tone's frequencies as the cursor moves so you can hear the beating change.
Once that works, make it beautiful:
- Find the local minima, mark them with glowing dots and label each with its nearest simple ratio.
- Add timbre presets, including stretched partials (f^log2(2.1)) and a bell, and morph between them so the dips visibly slide away from the familiar intervals.
Explain the key ideas in short code comments. When you're done, tell me how to open it and suggest three directions I could take it next, such as drawing the two spectra with arcs between the pairs that beat, a draggable spectrum editor, or deriving a scale from the dips of a custom timbre.