⚠ WARNING ⚠: this website is no longer maintained (and hasn't been for a while).

To view my active website, please visit https://abstract.properties


Part Two: Music Theory: Equal Temperament, Notes, and Notation

This article will describe how the western musical scale is structured, using the Web Audio API.


window.audioContext = new (window.AudioContext || window.webkitAudioContext)();
            

Equal Temperament

If you live in the western world, your ears are most likely accustomed to the Twelve-Tone Equal Temperament (12-TET) system of tuning.

In this tuning, an octave is logarithmically divided into twelve semitones. An octave is simply the doubling of a frequency. Traditionally, the "A" above "middle C" (a specific note on a piano keyboard) is tuned to 440 Hz.

To calculate the frequency of some number of semitones from a reference tone, you can use the following formula:

freq = baseFreq × 2(numSemitones ÷ 12)

Or in javascript:


window.getFrequency = function (baseFreq, numSemitones) {
    return baseFreq * Math.pow(Math.pow(2, 1/12), numSemitones);
};
            

Here's the code that made that form do its thing:


var gain = window.audioContext.createGain();
gain.gain.value = 0.5;
gain.connect(window.audioContext.destination);

var freqEl = document.querySelector('#tet .freq');
var baseFreqEl = document.querySelector('#tet .base');
var semitonesEl = document.querySelector('#tet .semitones');
var buttonEl = document.querySelector('#tet button');

function recalculate() {
    var baseFreq = parseInt(baseFreqEl.value, 10);
    var numSemitones = parseInt(semitonesEl.value, 10);
    freqEl.value = getFrequency(baseFreq, numSemitones);
}
baseFreqEl.addEventListener('change', recalculate);
semitonesEl.addEventListener('change', recalculate);
recalculate();
buttonEl.addEventListener('click', function () {
    var oscillator = audioContext.createOscillator();
    oscillator.frequency.value = freqEl.value;
    oscillator.connect(gain);
    oscillator.noteOn(audioContext.currentTime + 0.00);
    oscillator.noteOff(audioContext.currentTime + 1.00);
});
            

The Names of Notes

Since base frequencies and semitones are a pain to talk about, musicians use a notation to refer to specific reference frequencies. We use a letter (A through G, starting with C), a semitone modifier (♯ "sharp", ♭ "flat"), and an octave number (usually between 0 and 9). The octave number is incremented on each additional C note.

Name Semitones (relative to A)
C / B♯ 3
C♯ / D♭4
D 5
D♯ / E♭6
E / F♭ 7
F / E♯ 8
F♯ / G♭9
G 10
G♯ / A♭11
A 0
A♯ / B♭1
B / C♭ 2

A4 is the fifth "A" key on a piano, and is usually tuned to be at exactly 440 Hz.

You may notice that A, D, and G are uniquely named (do not have neighbor notes that can reach them by adding a ♯ or ♭). This irregularity comes from the shape of the piano keyboard (which itself is due to the shape of the C Major scale)2. You can see that those are the only white keys surrounded by black keys on each side:

Each ♯ or ♭ added after the letter of the key acts as a modifier, meaning the key immediately to the right or the left, regardless of the color. We can now build a dictionary mapping this notation to the semitones relative to "A", and use the number of semitones to calculate the frequency of any named note:


var semitoneMap = {
    C: 3,
    D: 5,
    E: 7,
    F: 8,
    G: 10,
    A: 0,
    B: 2
};
window.noteFrequency = function (name) {
    var octaveIndex = (name.length === 3) ? 2 : 1;
    var note = name[0];
    var modifier = semitoneMap[note];
    if (name[1] === '♯') {
        modifier += 1;
    } else if (name[1] === '♭') {
        modifier += -1;
    }
    var octaveNumber = parseInt(name.substring(octaveIndex), 10);
    if (note === 'A' || note === 'B') {
        octaveNumber += 1;
    }
    var numSemitones = modifier + 12 * octaveNumber;
    return getFrequency(440, numSemitones - (5 * 12));
};
            

Diatonic Scale

Although a 12-TET scale is broken into twelve distinct tones, not all of them are used in a phrase of music (usually). The combination of notes is very unpleasant, which can be successfully used to achieve a particular emotion in modern music, but is not usually found in most pieces. Instead, the most common set of notes is a division of the 12 notes into 7 distinct notes. This is called the diatonic scale.

We'll talk about this scale more when we discuss musical modes, but there are two main scales that come from the diatonic scale: the Major scale and the Minor scale.

Diatonic NameMajor ScaleMinor Scale
Note SemitonesNoteSemitones
Tonic C 0 C 0
Supertonic D 2 D 2
Mediant E 4 E♭ 3
Subdominant F 5 F 5
Dominant G 7 G 7
Submediant A 9 A♭ 8
Leading Tone B 11 B♭ 10
Tonic (Octave)C 12 C 12

The Major scale and Minor scale have distinct emotions associated with them, one is more happy and the other is more sad.


window.playNote = function (note, time, duration) {
    var ctx = audioContext;
    var osc = ctx.createOscillator();
    osc.frequency.value = noteFrequency(note);
    osc.connect(ctx.destination);
    osc.noteOn(ctx.currentTime + time);
    osc.noteOff(ctx.currentTime + time + duration);
    return osc;
};
document.querySelector('.major-scale').addEventListener('click', function () {
    var notes = ['C4', 'D4', 'E4', 'F4', 'G4', 'A4', 'B4', 'C5',
                 'B4', 'A4', 'G4', 'F4', 'E4', 'D4', 'C4'];
    for (var i = 0; i < notes.length; ++i) {
        playNote(notes[i], 0.15 * i, 0.15);
    }
});
document.querySelector('.minor-scale').addEventListener('click', function () {
    var notes = ['C4', 'D4', 'E♭4', 'F4', 'G4', 'A♭4', 'B♭4', 'C5',
                 'B♭4', 'A♭4', 'G4', 'F4', 'E♭4', 'D4', 'C4'];
    for (var i = 0; i < notes.length; ++i) {
        playNote(notes[i], 0.15 * i, 0.15);
    }
});
            

Alternate Tunings

12-TET is not the only tuning that exists, it is historically derived from Just Intonation, which is the division of an octave into frequencies that match ratios of small numbers. To demonstrate the difference between the two, we can play a Major scale using Just Intonation and 12-TET:

Diatonic Name Semitones12-TET Just Intonation
Tonic 0 1.0 1/1
Supertonic 2 1.122462048309373 9/8
Mediant 4 1.25992104989487325/4
Subdominant 5 1.33483985417003444/3
Dominant 7 1.49830707687668153/2
Submediant 9 1.681792830507429 5/3
Leading Tone 11 1.887748625363386815/8
Tonic (Octave)12 2.0 2/1

Creating an Interactive Piano Keyboard

Armed with the calculation of a note to a frequency, and a visual arrangement of a piano keyboard, we can construct a full, functional piano keyboard.

The code that handles the click events is here:


document.querySelector('.piano').addEventListener('click', function (evt) {
    if (evt.target.hasAttribute('data-note')) {
        var note = evt.target.getAttribute('data-note');
        evt.target.classList.add('active');
        playNote(note, 0, 0.5);
        setTimeout(function () {
            evt.target.classList.remove('active');
        }, 500);
    }
});
            

And the code that generates the actual piano DOM elements is here:


function makeKey(note) {
    var key = document.createElement('div');
    var container = document.createElement('div');
    key.className = 'key';
    container.appendChild(key);
    container.className = (note[1] === '♭' || note[1] === '♯') ? 'black' : 'white';
    key.setAttribute('data-note', note);
    return container;
}
[].forEach.call(document.querySelectorAll('.piano'), function (piano) {
    var keyContainer = document.createElement('div');
    keyContainer.className = 'keys';
    var keys = ['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B'];
    var aIndex = keys.indexOf('A');
    var note;
    for (var i = 0; i < 8; ++i) {
        for (var j = (i === 0) ? aIndex : 0; j < keys.length; ++j) {
            keyContainer.appendChild(makeKey(keys[j] + i));
        }
    }
    keyContainer.appendChild(makeKey('C8'));
    piano.appendChild(keyContainer);
});
            

In Conclusion

There is a strong mathematical relationship between the notation used to describe a particular frequency, and a strong physical relationship between the structure of the piano keyboard and the notation used to refer to particular keys. These relationships are very simple to programmatically describe, which helps us very quickly make an interactive piano keyboard.

In the next segment, we'll cover envelopes. Stay tuned!