Harmonizer 01 is a rule-based system that generates harmonized chord progressions according to classical harmony rules. It is implemented in SuperCollider and is the first iteration in a series exploring music-generation algorithms aimed at creating harmonies.
Although still under development, the current version is fully usable: load the project source files and call harmonizeProg (see Usage below). It takes onr mandatory argument—a chord progression—and returns a harmonized progression that you can assign to a Routine, Task, or Pbind, to be played by any user-defined synth that accepts frequency and duration parameters.
Rather than evolving a single codebase across versions, this project advances through distinct iterations with narrow scope, keeping each one manageable. In Harmonizer 01, the emphasis is on the rule system and the backtracking algorithm. To reduce complexity, only triads are used, drawn from a static MIDI-note library, and a simple parser prepares the input progression for the backtracking stage.
The next iteration will introduce dynamically generated chord ranges (e.g., seventh chords, extended chords, augmented-sixth chords), a more comprehensive parser and the ability to handle more than four voices.
The entry point is harmonizeProg, which requires:
- prog: an Array of Symbols (chord ciphers).
The progression is parsed to extract and store chord data in a Dictionary: root, chord quality (major, minor, diminished, augmented), and the interval from the previous chord’s root. During parsing, a specific rule profile is assigned to each chord to guide generation. The parser then collects chord data from the static library (MIDI-note ranges and degree arrays).
Before backtracking, these note ranges are filtered by voice range, and—depending on the rules and the previous chord—by (a) removing notes that violate melodic-interval constraints and (b) optionally collapsing candidates to the common tone when applicable. This filtering is repeated for each chord as backtracking traverses the progression.
Generation uses two nested backtracking loops. The inner loop builds all valid chord realizations for a given previous chord from the filtered note sets; the outer loop steps through the entire progression, trying candidate chords in sequence. If at any point no valid realization can be found—or all candidates are exhausted—the algorithm backtracks to the last stable position, selects the next candidate, and continues.
If ~harmonizeProg fails to build a progression, than an error is prompted saying: HarmonizeProg was not able to harmonize <progression>.
Note
For more information checkout the Wiki Pages
Clone the repository and load the entry file into a project file of your own:
"/local/path/to/sc_harmonizer01/src/loadall.scd".load;Then call the main function:
~harmonizeProg.(prog, dur, dir: "line", out: 'voice', uni: false, sus: true, scr: false, log: false)-
prog (Array of Symbols) – chord progression to harmonize. Symbols must match the static library (see Valid chord symbols).
-
dur (Array of Numbers) – duration of each chord. Must match the length of progression. If no durations are provided, and
outis set to'voice', harmonizer will assign an array of ones, of the same size asprog. -
dir (String, default "line") – upper-voice motion:
"up","down", or"line"(ranged, centered on first chord’s top note). -
out (Symbol, default 'voice') – output format.
'voice'ouputs one voice per subarray,'chord'outputs one chord per subarray. -
uni (Boolean, default false) – when set to
trueprohibits unisons in chords. -
sus (Boolean, default true) – sustains common tones by extending the first note’s duration.
-
scr (Boolean, default false) – exports score via Python 3 + music21 + Finale/Sibelius/MuseScore.
-
log (Boolean, default false) – prints function call traces (debugging).
~progression = ['CM', 'FM', 'GM', 'CM'];
~durations = [2, 1, 1, 2];
~result = harmonizeProg.(~progression, ~durations);// out: 'voice'
[
[ [..Bass..], [..Tenor..], [..Alto..], [..Soprano..] ],// MIDI notes per voice
[ [..Bass..], [..Tenor..], [..Alto..], [..Soprano..] ] // durations per voice
]
// out: 'chord'
[ [..Chord1..], [..Chord2..], [..Chord3..], ... ]
You can then map each voice into a Pattern, Routine, or other playback construct (see example.scd for a full example).
| Root | Diminished | Minor | Major | Augmented |
|---|---|---|---|---|
| C | Cd | Cm | CM | CA |
| C#/Db | C#d | C#m | DbM | DbA |
| D | Dd | Dm | DM | DA |
| Eb | Ebd | Ebm | EbM | EbA |
| E | Ed | Em | EM | EA |
| F | Fd | Fm | FM | FA |
| F# | F#d | F#m | F#M | F#A |
| G | Gd | Gm | GM | GA |
| G#/Ab | G#d | G#m | AbM | AbA |
| A | Ad | Am | AM | AA |
| Bb | Bbd | Bbm | BbM | BbA |
| B | Bd | Bm | BM | BA |
A more complete example is available in example.scd.
The score export functionality requires Python 3 and the music21 library.
To install music21, run:
# Linux / macOS
pip3 install music21
# Windows
py -3 -m pip install music21Once installed, you need to configure music21 by running the following in your terminal or Python REPL:
import music21
music21.configure.run()The configuration process is important, as it tells music21 which notation software to use for opening exported scores (Finale, Sibelius, MuseScore, etc.).
👉 For more details and installation instructions, see the official music21 documentation
Note: If you prefer to use a Python virtual environment, it is advisable to place it in sc_harmony01/score/python/ as venv/. The program will automatically detect and use it when available.
This project is shared freely with the community, and feedback, testing, or ideas for improvement are all welcome.
If you find Harmonizer 01 useful and would like to support further development, you can also consider a small donation:
As of 2025-10-19, harmony01 is licensed under the GNU General Public License v3 (GPLv3).
All code committed prior to this date was licensed under the MIT License.
Author: Siriil