Abstracting harmony using functions
Harmony is a vast and complicated topic and I don’t want to bore my readers with the technicalities of composing effective harmonic progressions. For today’s experiment we will generate some simple chord progressions using harmonic functions.
Similar in concept to Tonic, Dominant, and Subdominant used in traditional western harmony. We can define our own functions (4 in our case) that will determine the relations and probabilities for harmonic changes within a chord progression. This makes every chord an instance of the harmonic function.
For example, let’s take D, D7, G, A as instances of our four functions. This simple chord progression is basically an extended cadenza, with D7 functioning as a secondary dominant of the subdominant. (Again, I do my best to not make it feel like a harmony lesson).

Probabilities
Now, we can define probabilities for the harmonic changes between the functions.
So let’s define some very simple relations like so:
D has 80% chance to go to G and 20% chance to go to D7
D7 has 80% change to go to G and 20% chance to go to D
G has 50% chance to go to D and 50% chance to go to A
A has 50% chance to go to D and 50% chance to go to D7
With these harmonic probabilities we might get harmonies not dissimilar to 18th century German folksongs.

It should be noted that changing these probabilities can produce extremely different musical results despite the simplicity of our 4 base chords. Case in point, you will get a very different harmonic world if the D7 had an 80% chance of preceding A, rather than G.
Automating the process
The probabilities can also be represented in a JSON format for fast generation of instances using a python script. The code examples can be found here:
https://codeberg.org/guyoob/generative-music-resources/src/branch/master/chord_progressions
Multiple chords per function
Looking at the folksong mentioned above, we can see a fifth chord – A7. We can treat this A7 as a fifth function or alternatively, as a member of the 4th chord family (corresponding to the 4th function). Within the chord family we can define probabilities as well, so let’s say that A and A7 both have 50% of appearing as the representative member used as the function’s instance.
I have created chord families for all functions, defined their probabilities, ran the script multiple times and got some interesting results:
[['D', 'D7', 'Em7', 'D', 'D7b9', 'G', 'A7'], ['Bm7', 'Em7', 'A7'], ['D', 'G', 'A']]
[['D', 'G', 'A'], ['D7', 'DMaj7', 'D7', 'G', 'A'], ['D7', 'G', 'A']]
[['D', 'G', 'A'], ['D7', 'Bm7', 'Em7', 'A'], ['D', 'G', 'A7']]
[['DMaj7', 'G', 'Bm7', 'G', 'A7'], ['D7', 'G', 'A7'], ['Bm7', 'G', 'D', 'G', 'A7']]
Let’s make some music
To put these chord progressions into practical use, I have created a 5/4 drum beat and employed the chords as the harmonic progression to accompany the beat.
For those curious about why 5/4 in particular – I was inspired (again) by my German folksong research:

The result has nothing to do with my source of inspiration, but it’s a fun little harmonic experiment nonetheless: https://shufu.bandcamp.com/track/smelly-jobs
Note: In this article I use “German” as a linguistic identifier and not as a national identifier.