Public
Edited
Aug 21, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function makeMode(tonic, mode) {
return { tonic, mode }
}
Insert cell
// we're preferring some enharmonics here, unavoidable; we're choosing "shortest path of fourths of fifths from C, tiebreaker goes to flat"
tonicName = ["C", "C#", "D", "Eb", "E", "F", "F#", "G", "Ab", "A", "Bb", "B"]
Insert cell
modeName = ["Lydian", "Ionian", "Mixolydian", "Dorian", "Aeolian", "Phrygian", "Locrian"]
Insert cell
function isSharp(note) {
return note[note.length - 1] === '#';
}
Insert cell
function isFlat(note) {
return note[note.length - 1] === 'b';
}
Insert cell
modeTranspositions = [3, 0, 4, 1, 5, 2, 6];
Insert cell
function scale(fullMode) {
let { tonic, mode } = fullMode;
while (mode < 0) mode += 7;
while (tonic < 0) tonic += 12;
mode = mode % 7;
tonic = tonic % 12;
tonic = tonicName[tonic];
mode = modeTranspositions[mode];
const baseNote = tonic[0];
const allNotes = "CDEFGABCDEFGAB";
const allIntervals = [2, 2, 1, 2, 2, 2, 1, 2, 2, 1, 2, 2, 2, 1];
const ix = allNotes.indexOf(baseNote);
const notes = allNotes.slice(ix, ix+7);
const noteIntervals = [0, ...allIntervals.slice(ix, ix + 7)];
const modeIntervals = [0, ...allIntervals.slice(mode, mode + 7)];
const result = [];
let runningAccidental = isFlat(tonic) ? -1 : isSharp(tonic) ? 1 : 0;
for (let i = 0; i < 7; ++i) {
runningAccidental += modeIntervals[i] - noteIntervals[i];
const accidental = // you monster
(runningAccidental === 0 ? ""
:runningAccidental < 0 ? "♭".repeat(Math.abs(runningAccidental))
:"♯".repeat(runningAccidental));
result.push((notes[i] + accidental).replaceAll("♯♯", "𝄪"));
}
return result;
}
Insert cell
scale({tonic: 4, mode: 1})
Insert cell
function makeModeFromNumber(number) {
while (number < 0) number += 84;
const tonic = number % 12;
const mode = ~~(number / 12) % 7;
return { tonic, mode };
}
Insert cell
makeScaleFromNumber = x => scale(makeModeFromNumber(x))
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more