logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·31.10.26
← Back to news

Music Theory for Programmers

Original article
runjs.app|292 points|190 comments|by birdculture|Sep 2, 2026

Music Theory for Programmers

I’ve never been able to master a musical instrument. Every time I tried, I could mimic the sounds, but I never grasped why those sounds worked. Most music theory tutorials ignore the fundamentals skip the underlying physics and arithmetic that govern the system.

Because music is essentially a byproduct of mathematics, it can be computed. To truly understand it, I decided to rebuild music theory from the ground up using code.

The Roadmap

  • Define sound as a numerical value
  • Implement basic oscillators
  • Create volume envelopes
  • Explore timbre and harmonics
  • Derive the concept of the octave

1. Sound is Just a Changing Number

At its core, sound is simply the oscillation of air pressure. A speaker produces this by moving a cone back and forth. In a digital environment, this is represented as a sequence of numbers indicating the cone's position, sampled ≈44,100\approx 44,100≈44,100 times per second.

A synthesizer generates these numbers on the fly. In a web browser, you can specify the "shape" of the wave. The most basic form is the sine wave.

// Setup for browser audio
const ctx = new AudioContext();
const out = ctx.destination;

// A sine wave oscillating at 440Hz
const osc = ctx.createOscillator();
osc.frequency.value = 440; 
osc.connect(out);
osc.start();
osc.stop(ctx.currentTime + 1);

The value 440 is the only musically significant part of this code. It represents the frequency of the note A, which serves as the universal "tuning fork" for the rest of the system.


2. Solving the "Click" with Envelopes

If you stop an oscillator abruptly, you'll hear a sharp click. This happens because the speaker cone is forced to snap back to center instantly from a non-zero position. To fix this, we need a second changing number to control volume.

Musicians call this an Envelope.

Envelope Diagram

An envelope defines the life cycle of a note:

  • Attack: The time it takes to go from silence to peak volume.
  • Decay: The fall from the peak to a steady level.

By adjusting the attack, you change the character of the sound. A short attack sounds like a pluck; a long attack sounds like a bowed string. The pitch remains identical, but the perception changes.

The professional standard is the ADSR model:

  1. Attack
  2. Decay
  3. Sustain (the level held while the key is pressed)
  4. Release (the fade-out after the key is let go)
function note(freq, start = 0, length = 0.5, type = 'sine') {
  const osc = ctx.createOscillator();
  const gain = ctx.createGain();
  
  osc.type = type;
  osc.frequency.value = freq;
  
  // Simple envelope: 10ms attack, then decay
  gain.gain.setValueAtTime(0, ctx.currentTime + start);
  gain.gain.linearRampToValueAtTime(1, ctx.currentTime + start + 0.01);
  gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + start + length);
  
  osc.connect(gain);
  gain.connect(out);
  
  osc.start(ctx.currentTime + start);
  osc.stop(ctx.currentTime + start + length);
}

3. Timbre: The Frequencies You Didn't Ask For

A pure sine wave sounds like a medical hearing test because it contains only one frequency. Real instruments are messier. When you pluck a string at 220Hz220\text{Hz}220Hz, it doesn't just vibrate as a whole; it also vibrates in halves, thirds, and quarters.

These additional frequencies are called harmonics. The primary frequency is the fundamental.

The Harmonic Series for 220Hz

HarmonicMultiplierFrequency
1st (Fundamental)220×1220 \times 1220×1220Hz220\text{Hz}220Hz
2nd220×2220 \times 2220×2440Hz440\text{Hz}440Hz
3rd220×3220 \times 3220×3660Hz660\text{Hz}660Hz
4th220×4220 \times 4220×4880Hz880\text{Hz}880Hz
5th220×5220 \times 5220×51100Hz1100\text{Hz}1100Hz

The specific blend of these harmonics is what we call Timbre. It is why a trumpet sounds different from a violin, even when playing the same note.

The browser provides four built-in "recipes" for timbre: ['sine', 'triangle', 'square', 'sawtooth']

['sine', 'triangle', 'square', 'sawtooth'].forEach((type, i) => {
  note(220, i * 0.7, 0.6, type);
});

Note: A square wave sounds "hollow" because it only contains odd-numbered harmonics.


4. The Magic of the Octave

If you play a series of notes where each frequency is exactly double the previous one, they will sound like the same note, just higher or lower.

In Western music, this interval is called an Octave. This isn't an arbitrary choice; it's rooted in the harmonic series.

If we look at the harmonics of 220Hz220\text{Hz}220Hz and 440Hz440\text{Hz}440Hz:

  • 220Hz Series: 220,440,660,880,1100,1320,1540,1760…220, \mathbf{440}, 660, \mathbf{880}, 1100, \mathbf{1320}, 1540, \mathbf{1760} \dots220,440,660,880,1100,1320,1540,1760…
  • 440Hz Series: 440,880,1320,1760…\mathbf{440}, \mathbf{880}, \mathbf{1320}, \mathbf{1760} \dots440,880,1320,1760…

Because every harmonic of the higher note is already present in the lower note's series, the ear perceives them as having the same "color," just at a different brightness. The mathematical ratio is simply 2:12:12:1.