Music Theory
The music theory module turns musical ideas — scales, keys, chords, and intervals — into values you can compose with, so you can write melodies in a key, build chords by name, and explore modes and microtonal tunings without working out every MIDI number by hand. It scales from the familiar 12-tone system all the way to custom and Scala-based tunings.
Resonon separates two ideas that are easy to confuse:
- A
Scaleis rootless — just a set of intervals (e.g. major =0 2 4 5 7 9 11). It has no pitch until you anchor it. - A
Keyis aScalerooted at a note. It is the playable object: it turns scale degrees into notes, builds diatonic chords, and quantizes pitches.
Everything that produces actual notes lives on Key (or on the standalone chord() / interval() builders). Scale only describes intervals.
The Scale Type
Section titled “The Scale Type”Scale is a first-class value. Build one from a built-in name or from a custom semitone array — including microtonal tunings with fractional semitones.
// From a built-in namelet major = Scale("major");
// From a semitone interval array (must start with 0, strictly ascending)let custom = Scale(#[0, 2, 4, 5, 7, 9, 11]); // same as major
// Microtonal: fractional semitoneslet neutral = Scale(#[0, 1.5, 3.5, 5, 7, 8.5, 10]);
// Non-octave tuning: pass a custom period (semitones per repeat)let edo19 = Scale(#[0, 2, 4, 5, 7, 9, 11], 19);| Method | Returns | Description |
|---|---|---|
s.name() | String | Scale name ("major", "custom", etc.) |
s.intervals() | Array | Interval array in semitones |
s.period() | Number | Repeat period in semitones (12 for a standard octave) |
s.length() | Number | Number of scale degrees |
s.mode(n) | Scale | Rotate to the nth mode (1-indexed) |
s.contains(n) | Boolean | Does the scale contain this interval? |
Available Scales
Section titled “Available Scales”Resonon ships with 14 built-in scale types. Different scales have different lengths.
| Scale | Aliases | Notes | Intervals (semitones) | Character |
|---|---|---|---|---|
"major" | "ionian" | 7 | 0 2 4 5 7 9 11 | Bright, happy, resolved |
"minor" | "aeolian" | 7 | 0 2 3 5 7 8 10 | Dark, melancholic |
"dorian" | — | 7 | 0 2 3 5 7 9 10 | Minor with a bright 6th |
"phrygian" | — | 7 | 0 1 3 5 7 8 10 | Spanish, exotic |
"lydian" | — | 7 | 0 2 4 6 7 9 11 | Dreamy, floating |
"mixolydian" | — | 7 | 0 2 4 5 7 9 10 | Bluesy major, rock |
"locrian" | — | 7 | 0 1 3 5 6 8 10 | Unstable, diminished |
"harmonic_minor" | — | 7 | 0 2 3 5 7 8 11 | Classical tension |
"melodic_minor" | — | 7 | 0 2 3 5 7 9 11 | Jazz minor |
"pentatonic_major" | — | 5 | 0 2 4 7 9 | Open, folk |
"pentatonic_minor" | — | 5 | 0 3 5 7 10 | Blues, rock riffs |
"blues" | — | 6 | 0 3 5 6 7 10 | Gritty, soulful |
"whole_tone" | — | 6 | 0 2 4 6 8 10 | Dreamlike, ambiguous |
"chromatic" | — | 12 | 0 1 2 3 4 5 6 7 8 9 10 11 | All semitones |
scale_names() returns an array of all available scale name strings:
PRINT scale_names();// ["major", "minor", "dorian", "phrygian", "lydian", "mixolydian",// "locrian", "harmonic_minor", "melodic_minor", "pentatonic_major",// "pentatonic_minor", "blues", "whole_tone", "chromatic"]The seven diatonic modes are available both by name and by rotating a scale with mode():
let dorian = Scale("dorian");let also_dorian = Scale("major").mode(2); // major mode 2 = dorianPRINT dorian.intervals(); // [0, 2, 3, 5, 7, 9, 10]PRINT also_dorian.intervals(); // [0, 2, 3, 5, 7, 9, 10]Loading Scala Tuning Files
Section titled “Loading Scala Tuning Files”load_scala(name) loads a scale from a Scala .scl tuning file. Scala files define scales using cents or ratios and support non-octave periods (e.g., the Bohlen-Pierce tritave).
let just = load_scala("just/5-limit");let bp = load_scala("bohlen-pierce");Search order:
- Relative to the current
.nonfile ~/.resonon/scales/- Bundled scales
The .scl extension is optional. The loaded result is a Scale with the correct period and fractional-semitone intervals.
The Key Type
Section titled “The Key Type”A Key anchors a Scale at a root note. Build one from a root plus a scale name or Scale value:
let k = Key(C4, "major");let micro = Key(D4, Scale(#[0, 2, 3.5, 5, 7, 8.5, 10]));A Key exposes its parts as properties and its operations as methods:
| Member | Returns | Description |
|---|---|---|
k.scale | Scale | The underlying (rootless) scale |
k.root | Note | The root note |
k.name | String | The scale’s name |
k.degree(n) | Note | Note at the nth degree (1-indexed; wraps; ≤0 goes below root) |
k.notes() | Array | One octave of the scale as rooted Notes |
k.chord(deg [, quality] [, inv]) | Array | Diatonic triad (or explicit quality) on a degree |
k.chord7(deg [, inv]) | Array | Diatonic 7th chord on a degree |
k.quantize(note) | Note | Snap a note to the nearest scale tone |
k.transpose(semitones) | Key | New key with the root shifted |
let k = Key(C4, "major");
PRINT k.notes(); // [C4, D4, E4, F4, G4, A4, B4]PRINT k.degree(1); // C4 (root)PRINT k.degree(5); // G4 (fifth)PRINT k.degree(8); // C5 (wraps an octave)PRINT k.degree(0); // B3 (one step below the root)PRINT k.chord(2); // [D4, F4, A4] (diatonic ii triad)PRINT k.chord7(5); // [G4, B4, D5, F5] (diatonic V7)PRINT k.quantize(C#4); // C4 (snaps into the scale)Playing Degrees in a Key
Section titled “Playing Degrees in a Key”Inside patterns, write scale degrees with a ^ prefix instead of absolute notes. Degrees stay abstract until they are resolved against a key.
[^1 ^3 ^5 ^8] // degrees[^1: ^4: ^5:] // diatonic chords on degrees (trailing :)[^1:min7] // explicit chord quality on a degreeResolve them in one of two ways.
Per pattern with .in_key(key):
let melody = [^1 ^3 ^5 ^-2].in_key(Key(C4, "major"));let chords = [^1: ^4: ^5:].in_key(Key(A3, "minor"));Per track with track.key(...) — available on both MidiTrack and AudioTrack. Degrees on the track resolve automatically, and absolute notes are quantized to the scale:
let lead = MidiTrack(1, 100, NUL, "lead");lead.key(Key(C4, "minor")); // or lead.key("minor", C4)lead << [^1 ^3 ^5];Microtonal keys carry through this path: fractional degrees become fractional MIDI pitches, played via pitch bend (use track.tuning() or track.mpe() on the track).
Chords
Section titled “Chords”chord(root, quality, inversion?) builds a chord from any root, independent of a key:
PRINT chord(C4, "major"); // [C4, E4, G4]PRINT chord(C4, "min7"); // [C4, Eb4, G4, Bb4]Available Chords
Section titled “Available Chords”Resonon ships with 18 built-in chord qualities.
| Quality | Aliases | Notes | Intervals (semitones) | Character |
|---|---|---|---|---|
"major" | "maj" | 3 | 0 4 7 | Bright, stable |
"minor" | "min" | 3 | 0 3 7 | Dark, melancholic |
"dim" | — | 3 | 0 3 6 | Tense, unstable |
"aug" | — | 3 | 0 4 8 | Bright, unsettled |
"7" | "dom7" | 4 | 0 4 7 10 | Bluesy, wants to resolve |
"maj7" | — | 4 | 0 4 7 11 | Lush, jazzy |
"min7" | — | 4 | 0 3 7 10 | Smooth, mellow |
"dim7" | — | 4 | 0 3 6 9 | Dramatic, symmetrical |
"aug7" | — | 4 | 0 4 8 10 | Altered dominant |
"sus2" | — | 3 | 0 2 7 | Open, ambiguous |
"sus4" | — | 3 | 0 5 7 | Suspended, tense |
"6" | — | 4 | 0 4 7 9 | Warm, vintage |
"min6" | — | 4 | 0 3 7 9 | Bittersweet |
"9" | — | 5 | 0 4 7 10 14 | Rich dominant |
"maj9" | — | 5 | 0 4 7 11 14 | Lush, expansive |
"min9" | — | 5 | 0 3 7 10 14 | Smooth, complex |
"add9" | — | 4 | 0 4 7 14 | Bright, open |
"power" | "5" | 2 | 0 7 | Neutral, heavy |
chord_names() returns all available chord qualities.
Inline Chord Notation
Section titled “Inline Chord Notation”Within patterns, chords can be written directly using compact notation:
Root:Quality~Inversion// These are equivalent:let a = [C4:maj E4:min A3:min7];let b = Sequence(Stack(chord(C4, "major")), Stack(chord(E4, "minor")), Stack(chord(A3, "min7")));Inversions and Voicings
Section titled “Inversions and Voicings”The optional third argument to chord() (or ~n in inline notation) rearranges chord tones across octaves. Positive inversions raise the lowest notes; negative ones drop the highest.
PRINT chord(C4, "major"); // [C4, E4, G4]PRINT chord(C4, "major", 1); // [E4, G4, C5]PRINT chord(C4, "major", 2); // [G4, C5, E5]PRINT chord(C4, "major", -1); // [G3, C4, E4]Playing Chords
Section titled “Playing Chords”Use Stack() to play all chord notes at once. Use <> to alternate chords each cycle:
// SimultaneousStack(chord(C4, "major"));
// Progression — alternate a chord each cycle with inline notationlet progression = <C4:maj F4:maj G4:maj>;Intervals
Section titled “Intervals”interval(note, name) returns a single note offset from the given note by the named interval:
PRINT interval(C4, "P5"); // G4PRINT interval(C4, "m3"); // Eb4PRINT interval(C4, "M3"); // E4PRINT interval(C4, "P8"); // C5Available Intervals
Section titled “Available Intervals”| Name | Aliases | Interval | Semitones |
|---|---|---|---|
"m2" | — | Minor 2nd | 1 |
"M2" | — | Major 2nd | 2 |
"m3" | — | Minor 3rd | 3 |
"M3" | — | Major 3rd | 4 |
"P4" | — | Perfect 4th | 5 |
"tritone" | "TT" | Tritone | 6 |
"P5" | — | Perfect 5th | 7 |
"m6" | — | Minor 6th | 8 |
"M6" | — | Major 6th | 9 |
"m7" | — | Minor 7th | 10 |
"M7" | — | Major 7th | 11 |
"P8" | "oct" | Octave | 12 |
Building Custom Material
Section titled “Building Custom Material”Collect interval() results into an array to construct scales or chords not in the built-in library:
let root = C4;let dim_scale = #[ root, interval(root, "M2"), interval(root, "m3"), interval(root, "P4"), interval(root, "tritone"), interval(root, "m6"), interval(root, "M6"), interval(root, "M7")];See Also
Section titled “See Also”- Music Theory Reference — terse signatures for every
Scale/Keymethod, constructor, and helper - Patterns — Sequencing and pattern operations
- Pattern Methods —
in_key,quantize, and more - Track Methods —
track.key,tuning,mpe
Next Steps
Section titled “Next Steps”That completes the language fundamentals. Next, put notes in motion by sequencing them with patterns.
- Pattern Basics — how patterns divide time into events