Skip to content

Harmony & Chord Progressions

This document explains the harmonic system in MIDI Sketch.

Note

This page uses music theory terminology. These concepts are handled automatically by the system, but understanding them allows for more precise parameter selection.

Chord Progressions

MIDI Sketch includes 22 built-in chord progressions, covering common pop music patterns.

What is a Chord Progression?

A chord progression is the sequence of chords that forms the harmonic backbone of a song. It's what gives music its sense of movement and emotion. The same melody can feel completely different over different chord progressions.

4-Chord Progressions

IDNameDegreesCharacter
0Pop4I-V-vi-IVUbiquitous pop
1Axisvi-IV-I-VMelancholic
2Komurovi-IV-V-IBright J-pop
3CanonI-V-vi-iii-IVClassic
4Emotional4vi-V-IV-VEmotional build
5MinimalI-IVSimple two-chord
6AltMinimalI-VPower pop
7Progression3I-vi-IVThree-chord
8Rock4I-bVII-IV-IRock feel
Understanding These Progressions
  • Pop4 (I-V-vi-IV): The most popular progression in modern music. Used in thousands of hit songs. Creates a satisfying, familiar feel.
  • Axis (vi-IV-I-V): Starts on a minor chord, creating instant melancholy. Used in many emotional ballads.
  • Komuro (vi-IV-V-I): Named after Japanese producer Tetsuya Komuro. The V→I resolution at the end creates a bright, uplifting feeling.
  • Canon (I-V-vi-iii-IV): Based on Pachelbel's Canon. Timeless and elegant.
  • Rock4 (I-bVII-IV-I): The bVII (flat seven) chord adds a rock/blues flavor.

5-Chord Progressions

IDNameDegreesCharacter
9Extended5I-V-vi-iii-IVExtended canon
10Emotional5vi-IV-I-V-iiComplex emotional

Full Progression List

Total of 22 progressions including:

  • Pop variants (bright, dark)
  • Emotional variants
  • Minimal (2-3 chord) patterns
  • Rock-influenced
  • Jazz-influenced with ii-V

Degree System

What are Chord Degrees?

Chord degrees (I, ii, iii, IV, V, vi, vii) indicate the position of a chord within a key. Uppercase = major chord, lowercase = minor chord. For example, in C major: I = C major, ii = D minor, V = G major.

Chord degrees are represented as integers:

cpp
enum Degree {
    I   = 0,   // Tonic
    ii  = 1,   // Supertonic
    iii = 2,   // Mediant
    IV  = 3,   // Subdominant
    V   = 4,   // Dominant
    vi  = 5,   // Submediant
    vii = 6,   // Leading tone
    bVII = 10  // Flat seven (borrowed)
};

Chord Quality

Major vs Minor

  • Major chords sound bright, happy, and stable (C, F, G)
  • Minor chords sound darker, sadder, and more emotional (Dm, Em, Am)
  • Diminished chords sound tense and unstable (rarely used in pop)

Quality is determined by degree in major key:

cpp
ChordQuality getQuality(Degree degree) {
    switch (degree) {
        case I: case IV: case V: case bVII:
            return Major;
        case ii: case iii: case vi:
            return Minor;
        case vii:
            return Diminished;
    }
}

Chord Extensions

Extensions add color to basic triads:

When to Use Extensions

  • Sus chords: Create tension before resolution. Great for anticipation moments.
  • 7th chords: Add sophistication and jazz flavor. Common in city pop and R&B.
  • 9th chords: Rich, complex sound. Use sparingly for maximum impact.

Higher extension probabilities work well for city pop, jazz, and R&B styles. Keep them low for simple pop and rock.

Extension Types

TypeNotes AddedExample (C)
TriadRoot, 3rd, 5thC-E-G
Sus2Root, 2nd, 5thC-D-G
Sus4Root, 4th, 5thC-F-G
7th+ 7thC-E-G-B/Bb
9th+ 7th + 9thC-E-G-B-D

Extension Application Rules

Configuration

cpp
struct ChordExtensionParams {
    bool enable_sus = false;
    bool enable_7th = false;
    bool enable_9th = false;
    bool tritone_sub = false;          // AccompanimentConfig only
    float sus_probability = 0.2f;     // 20% chance
    float seventh_probability = 0.15f; // 15% chance
    float ninth_probability = 0.25f;   // 25% chance
    float tritone_sub_probability;     // AccompanimentConfig only
};

Mood-Dependent Probability Auto-Adjustment

When chordExtProbExplicit=false (default), the mood automatically adjusts chord extension probabilities to match the style. Set chordExtProbExplicit=true to manually control all extension probabilities.

Voice Leading

What is Voice Leading?

Voice leading is how individual notes move from one chord to the next. Good voice leading creates smooth, connected chord transitions. Poor voice leading sounds choppy and disconnected. MIDI Sketch automatically applies optimized voice leading to all generated chord progressions.

Principles

  1. Minimize movement: Each voice moves by smallest interval
  2. Common tones: Retain shared notes between chords
  3. Avoid parallel fifths/octaves: Classical constraint
  4. Smooth bass: Stepwise or small leaps preferred

Algorithm

cpp
Voicing optimizeVoicing(Voicing prev, Chord next) {
    vector<Voicing> candidates = generateAllVoicings(next);

    return min_element(candidates, [&](auto& a, auto& b) {
        int distA = totalVoiceDistance(prev, a);
        int distB = totalVoiceDistance(prev, b);

        // Also penalize large leaps
        int leapA = maxSingleVoiceDistance(prev, a);
        int leapB = maxSingleVoiceDistance(prev, b);

        return (distA + leapA * 2) < (distB + leapB * 2);
    });
}

Voicing Types

Voicing Explained
  • Close Position: All notes within one octave. Sounds compact and direct. Common in pop.
  • Open Position: Notes spread across multiple octaves. Sounds spacious and full. Great for ballads.
  • Rootless: Omits the root note (bass plays it). Creates clarity and avoids muddiness. Common arranging technique.

Bass-Chord Coordination

The chord track uses bass analysis to avoid doubling:

cpp
struct BassAnalysis {
    bool hasRootOnBeat1;   // Root on downbeat
    bool hasRootOnBeat3;   // Root on beat 3
    bool hasFifth;         // Fifth present
    Tick accentTicks[];    // Strong beat positions
};

// When bass has root, chord uses rootless voicing
if (bassAnalysis.hasRootOnBeat1) {
    voicing = generateRootlessVoicing(chord);
}

Secondary Dominants

Secondary dominants are dominant chords (V7) that resolve to diatonic chords other than the tonic. They create stronger harmonic pull and add harmonic variety.

What are Secondary Dominants?

Instead of going directly from IV to V, inserting V/V (a dominant chord that resolves to V) creates a stronger sense of motion. For example, in C major: F → D7 → G is more compelling than F → G because D7 (V/V) "wants to" resolve to G.

Common Secondary Dominants

SymbolResolves ToExample in C
V/VV (dominant)D7 → G
V/vivi (relative minor)E7 → Am
V/iiii (supertonic)A7 → Dm
V/IVIV (subdominant)C7 → F

Automatic Insertion

MIDI Sketch automatically inserts secondary dominants based on:

  • Chord duration: Longer chords are candidates for preparation
  • Section type: Pre-chorus sections favor secondary dominants
  • Style: Jazz and city pop use more secondary dominants
cpp
// Example: V/V insertion before V
// Original: IV → V → I
// Enhanced: IV → V/V → V → I

Tritone Substitution

Tritone substitution replaces a V7 chord with a bII7 chord (a dominant 7th built a tritone away). This creates chromatic bass motion and adds harmonic sophistication.

Configuration

Tritone substitution is available via:

  • AccompanimentConfig: chordExtTritoneSub (enable) and chordExtTritoneSubProb (probability 0.0-1.0)
  • C++ SongConfig JSON: chord_extension.tritone_sub and chord_extension.tritone_sub_probability (via C++ readFrom)

Note: These parameters are not currently exposed in the JS SongConfig type (types.ts). Use AccompanimentConfig or the C++ JSON API for access.

Mood-Dependent Chord Extension Probabilities

When chordExtProbExplicit=false, the mood automatically adjusts chord extension probabilities to match the style:

Mood7th Prob9th ProbSus ProbNotes
CityPop40%25%-Jazz-influenced voicings
RnBNeoSoul50%35%-Rich extended harmonies
Ballad/Sentimental30%-25%Expressive sus resolutions
Nostalgic/Chill25%--Gentle extensions
Lofi40%30%-Warm, lo-fi character

Explicit vs Automatic

Set chordExtProbExplicit=true to manually control all extension probabilities, or leave it false to let the mood system choose appropriate values automatically.

ChordEvent in EventData

The EventData JSON output now includes a chords array with detailed chord information per section, including secondary dominant annotations. This allows external tools to visualize and analyze the harmonic structure.

Key Modulation

Why Modulate?

Key modulation (changing the key mid-song) is a powerful technique to add excitement and emotional lift. A modulation up by 1-2 semitones in the final chorus creates a feeling of "taking it to the next level" - a classic technique used in countless hit songs.

Modulation Parameters

ParameterRangeDescription
modulationTiming0-4When to modulate (0=disabled)
modulationSemitones1-4Amount to modulate (required when timing is not 0)

WARNING

When modulationTiming is non-zero, modulationSemitones must be set to 1-4. The vocal high range is automatically adjusted to prevent the final sections from exceeding the vocal range after transposition.

Modulation Points

StructureModulation PointAmount
StandardPopB → Chorus+1 semitone
RepeatChorusChorus 1 → 2+1 semitone
BalladB → Chorus+2 semitones
Full patternsVaries+1 to +4

Implementation

cpp
struct Modulation {
    Tick tick;           // When to modulate
    int8_t semitones;    // How much (1-4 semitones)
};

// Applied during MIDI output
void MidiWriter::writeTrack(MidiTrack& track, Modulation mod) {
    for (auto& note : track.notes) {
        if (note.tick >= mod.tick) {
            note.pitch += mod.semitones;
        }
    }
}

Chord Tone Analysis

Used for melody generation to determine note consonance:

cpp
bool isChordTone(uint8_t pitch, Chord chord) {
    uint8_t pitchClass = pitch % 12;

    // Check against chord pitch classes
    for (auto& chordPitch : chord.pitchClasses()) {
        if (pitchClass == chordPitch) return true;
    }
    return false;
}

uint8_t nearestChordTone(uint8_t pitch, Chord chord) {
    // Find closest chord tone by semitone distance
    int minDist = 12;
    uint8_t nearest = pitch;

    for (auto& target : chord.pitches()) {
        int dist = abs(pitch - target);
        if (dist < minDist) {
            minDist = dist;
            nearest = target;
        }
    }
    return nearest;
}

Tension Notes

What are Tension Notes?

Tension notes are notes that don't belong to the current chord but are used intentionally to create musical interest. They create a sense of "wanting to resolve" - like a musical question waiting for an answer. Used skillfully, they make melodies more expressive and emotionally compelling.

Non-chord tones used for melodic interest:

TensionIntervalResolution
9thMajor 2ndDown to root
11thPerfect 4thDown to 3rd
13thMajor 6thDown to 5th
b9Minor 2ndDown to root
#11Aug 4thTo 5th

Usage by Vocal Attitude

Beat Placement

Weak beats (beats 2 and 4 in 4/4 time) are safer places for tension notes because they don't disrupt the harmonic foundation established on strong beats (1 and 3).

AttitudeTensions AllowedMusical Effect
CleanNone (chord tones only)Safe, consonant, easy to sing
Expressive9th, 13th on weak beatsColorful, emotional, expressive
RawAll tensions, any beatEdgy, unpredictable, intense

Choosing Vocal Attitude

  • Clean: Best for simple pop, children's songs, and when singability is important
  • Expressive: Best for ballads, R&B, city pop - adds emotional depth
  • Raw: Best for rock, alternative, experimental - creates tension and edge

Harmony-Melody Integration

The vocal generation system uses harmony information extensively to create musically coherent melodies.

HarmonyContext

The HarmonyContext system tracks all generated tracks and provides collision-free pitch suggestions:

Why HarmonyContext Matters

Without HarmonyContext, melodies might clash with accompaniment. For example, if the bass plays E and the melody plays F simultaneously, the result is a harsh minor 2nd dissonance. HarmonyContext prevents this by checking all active notes before suggesting melody pitches.

Collision Types

IntervalCollision TypeResult
Minor 2nd (1 semitone)SevereAlways avoided
Major 7th (11 semitones)SevereAlways avoided
Tritone (6 semitones)MildContext-dependent
Perfect 5th, OctaveNoneHarmonically stable

Chord-Aware Melody Generation

The MelodyDesigner uses chord information at multiple stages:

Strong Beat Rule: On beats 1 and 3, melodies strongly prefer chord tones. This creates a sense of harmonic grounding even when the melody moves freely between strong beats.

VocalStyleProfile and Harmony

Each VocalStyleProfile configures how the melody interacts with harmony:

ProfileChord Tone PreferenceTension UsageApproach
StandardHigh on strong beatsOccasional 9thSafe, singable
IdolVery highMinimalCatchy, simple
CityPopMediumFrequent 9th, 13thSophisticated
VocaloidLowAggressiveSurprising
BalladHighExpressive appoggiaturasEmotional

Style-Harmony Matching

For best results, match your chord extensions to your vocal style:

  • Idol/Standard: Keep extensions low (sus, occasional 7th)
  • CityPop/Jazz: Use high extension probabilities (7th, 9th)
  • Vocaloid: Extensions don't matter much (melody is freer)

Melody Evaluation: Harmony Component

The melody evaluation system includes harmony scoring:

cpp
float evaluateHarmonyFit(const MelodyCandidate& melody, const Chord& chord) {
    float score = 0.0f;

    for (auto& note : melody.notes) {
        if (isStrongBeat(note.tick)) {
            // Strong beat: chord tone = +1.0, tension = +0.3, other = -0.5
            if (isChordTone(note.pitch, chord)) score += 1.0f;
            else if (isTension(note.pitch, chord)) score += 0.3f;
            else score -= 0.5f;
        } else {
            // Weak beat: more lenient
            if (isChordTone(note.pitch, chord)) score += 0.5f;
            else if (isScaleTone(note.pitch)) score += 0.2f;
        }
    }

    return score / melody.notes.size();
}

Hook System and Harmony

The hook system (used in Chorus sections) respects harmony while creating memorable patterns:

Hook SkeletonHarmonic Behavior
RepeatStays on single chord tone
AscendingRises through chord arpeggio
AscendDropArpeggio up, then resolves down
LeapReturnJumps to tension, returns to chord tone

Hook + Chord Sync

Hooks are most effective when they align with chord changes. The hookIntensity parameter controls how strongly hooks emphasize chord tones vs. create melodic interest through tensions.

Piano Roll Safety API

For external tools (like piano roll editors), the PianoRollSafety API provides harmony-aware pitch suggestions:

cpp
// Get safe pitches at a specific tick
vector<SafePitch> getSafePitches(Tick tick) {
    Chord currentChord = getChordAt(tick);
    vector<uint8_t> activePitches = getActiveNotesAt(tick);

    vector<SafePitch> result;
    for (uint8_t pitch = vocalLow; pitch <= vocalHigh; pitch++) {
        CollisionType collision = checkCollision(pitch, activePitches);
        bool isChordTone = currentChord.contains(pitch % 12);

        result.push_back({
            pitch,
            collision,
            isChordTone ? PitchSafety::Recommended : PitchSafety::Acceptable
        });
    }
    return result;
}

This enables visual feedback showing:

  • Green: Chord tones (recommended)
  • Yellow: Scale tones (acceptable)
  • Red: Collision pitches (avoid)

Practical Guide

StyleProgressionExtensionsAttitude
Simple PopPop4 (0)Low (10-20%)Clean
Emotional BalladAxis (1)Medium (30%)Expressive
J-PopKomuro (2)Medium (30%)Expressive
City PopExtended5 (9)High (50%+)Expressive
RockRock4 (8)Low (10%)Raw

Quick Start Recommendations

For Beginners

Start with these safe defaults:

  • Progression: Pop4 (ID 0) - works with almost anything
  • Extensions: Keep all probabilities under 30%
  • Attitude: Clean - easiest melodies to work with
  • Modulation: None or LastChorus +1 semitone

Once you're comfortable, experiment with more complex progressions and higher extension probabilities.

Released under the MIT License.