Chapter 1: Emergent Harmonic Structures | Software 1.0
1.1 Harmonic Structures
Before the universe existed, everything was compressed into a single point known as a singularity. There was no space, no matter, and no sound. Then, in an instant, this point expanded rapidly in an event we call the Big Bang. Physicist Stephon Alexander describes this event in The Jazz of Physics as similar to the opening note of a jazz improvisation, one event triggering complex patterns.
These initial patterns influenced how galaxies and stars formed. Tiny quantum fluctuations during the early rapid expansion created variations that later became structures like galaxies. We can still detect echoes of these early patterns today as cosmic microwave background radiation.
Alexander also compares fundamental cosmic forces to musical instruments playing together. Gravity is like the bass line, holding everything together, while electromagnetic and nuclear forces shape and refine the structure of the universe, similar to rhythm and melody in music.
Inspired by these ideas, we'll learn the basics of math, programming, and artificial intelligence (AI). This foundation will allow you to understand how complex patterns, like music or AI, emerge from simple rules.
1.2 Variables and Basic Data Types
Variables store values. They can store numbers, text, or lists. Imagine the singularity turning into the Big Bang as the opening beat of a larger composition. Let's call it beat one:
beat = 1
note = "C"
print(beat)
print(note)Two things worth naming right away. That 1 is a number, which programmers call an integer. The "C" is text, which programmers call a string, and the quotes matter: without them, Python gets confused and goes looking for a variable named C instead of the letter. Single or double quotes both work. I like double quotes.
print() is a function somebody wrote for us. It displays whatever you put inside the parentheses, so you can see what a variable is storing. Storing values and bringing them back is most of what programs do. Every website you've ever visited is full of variables working together to bring you everything you see on the page.
1.3 Booleans – True or False
Boolean values represent truth: either True or False. Music is full of yes-or-no questions. Is the note C the tonic in the key of C? True. Is C sharp in the C major scale? False. How about a blue note? In the key of C the classic one is E flat, the minor third, the second note of the C minor pentatonic scale. It's a great note, but it's not the tonic:
is_tonic = True
is_blue_note = False
print(is_tonic, is_blue_note)Here we typed the answers in ourselves. The real power is that Python can answer the question for you, and this is where booleans actually come from in programs:
note = "C"
print(note == "C") # TrueRead that carefully: one equals sign stores a value, two equals signs ask a question. == is called the equality operator, it compares two values and answers with a boolean. We'll use it to build a quiz at the end of this chapter.
Two small things you just saw for the first time, worth naming. Giving print() several values separated by commas prints them all, with spaces in between. And the # True at the end of the line is a comment: a little note to yourself that Python completely ignores. You'll see comments everywhere in this book, often confirming what a line outputs.
1.4 Working with Lists
Lists store multiple items in order. If you're in 4/4 time there are four beats, so let's put all four in a list using square brackets:
beats = [1, 2, 3, 4]
print(beats)
print(beats[0]) # 1, the first beatGrabbing one item out of a list is called indexing, and here's the important lesson hiding in it. On a shopping list you'd number the items one olive oil, two eggs, three bread. Python doesn't. Python starts counting at zero, so the first beat is beats[0] and the fourth beat is beats[3]. Everything sits one behind the count you'd use as a human, and that's called a zero-based index. Before you run it, predict what beats[2] returns, then change the number and check yourself.
1.5 Tuples – Locked Sequences
A tuple is an ordered, immutable sequence, which means once you create it, it can't change. Some data should never change. A perfect fifth has been a 3:2 ratio since Pythagoras plucked a string, so it's a fact worth locking down:
perfect_fifth = ("perfect fifth", 3/2)
print(perfect_fifth[0]) # perfect fifth
print(perfect_fifth[1]) # 1.5
# perfect_fifth[0] = "P5" # remove the pound sign and run it: TypeError!That last line shows a second job for comments (§1.3): turning code off. It's real code silenced by its pound sign. Remove the # and run the cell, and Python refuses with a TypeError, which is immutability doing its job: a list would have let you overwrite the fifth, a tuple protects it.
Notice we stored the ratio as math, 3/2, instead of text, "3:2". Stored as a number it comes back as 1.5, and Python can calculate with it. Remember the introduction: AI listens to numbers, not letters. In Chapter 2 we'll use this exact ratio to build intervals from real frequencies. Otherwise a tuple works like a list, indexing and all. Parentheses instead of square brackets, and no changes allowed.
1.6 Sets – Unique Collections
A set stores each element only once, perfect for comparing scales. Here's a music theory claim we can prove with code: C Ionian (the C major scale) and D Dorian use the exact same seven notes, just starting from different places. If that's true, every note should come back when we ask what the two scales share:
ionian = {"C", "D", "E", "F", "G", "A", "B"}
dorian = {"D", "E", "F", "G", "A", "B", "C"}
common_notes = ionian & dorian # & is intersection: what do both sets share?
print(sorted(common_notes)) # ['A', 'B', 'C', 'D', 'E', 'F', 'G']All seven notes come back. Proved: Dorian isn't new notes, it's the same notes with a new starting point, and that one idea is the secret behind all seven modes we'll build in Chapter 3. The & symbol is the intersection operator, and sorted() puts the result in alphabetical order. That last part matters because sets don't keep any order at all: print a set directly and the notes can come out scrambled differently every run. Predict before you run: what would ionian & {"C", "E", "G"} return? Try it. You just found a chord inside a scale.
1.7 Basic Arithmetic and Order of Operations (PEMDAS)
Python follows the usual math rules: multiplication and division happen before addition and subtraction, and parentheses override that order, just like grouping the sections of a song.
# Your song: 4 verses + 2 choruses, each section is 8 bars long
total_bars = (4 + 2) * 8
print(total_bars) # 48Watch how the parentheses change everything:
print(4 + 2 * 8) # 20, Python multiplies first (2 * 8), then adds 4
print((4 + 2) * 8) # 48, parentheses add the sections first, then multiplyFloor division // divides and keeps only the whole number, dropping the remainder. Its partner, the modulo operator %, keeps only the remainder. MIDI note numbers show off both, since an octave is 12 half steps (semitones):
midi_note = 67 # G above middle C
print(midi_note / 12) # 5.583333333333333
print(midi_note // 12) # 5, how many complete octaves of 12 fit
print(midi_note % 12) # 7, the remainder: what's left overRegular division / gives the exact decimal answer. Floor division asks "how many complete twelves fit?" and modulo asks "what's left over?" That leftover is the musical gold. Seven semitones up from C is G, so 67 % 12 returning 7 tells us this note is a G, no matter which octave it lives in. Musicians call that a pitch class, and % 12 is how code finds it. Chapter 3 leans on this trick to build scales.
One honest footnote on the 5: that counts spans of 12 semitones above MIDI note 0, which is not the octave number in the note's name. Middle C is MIDI 60, but musicians call it C4, not C5. The naming rule is midi_note // 12 - 1, and you'll see why the numbering is shifted when we work with MIDI for real.
1.8 Dictionaries: Storing Related Information
Dictionaries store key-value pairs, exactly like a real dictionary stores a word and its definition. Here the word is a note name and the definition is its frequency. Middle C vibrates at about 261.63 times per second, and a dictionary lets you look up the physics behind any note name:
note_frequencies = {
"C": 261.63,
"D": 293.66,
"E": 329.63,
}
print(note_frequencies["C"]) # 261.63Curly braces {} create the dictionary, every entry is key: value, and looking something up uses the same square brackets as a list, except you ask by name instead of by position. Numbers with a decimal point, like 261.63, are called floats. Notice the pattern in the data: as the notes climb, the frequencies climb. This little dictionary is the whole book in miniature. On the left, the letters musicians read. On the right, the numbers AI listens to. And we just stored the entire thing inside one variable, which is why variables matter.
1.9 Functions and Methods
Functions let you reuse code. Think of a function like a riff: you write it once, then play it whenever you need it. (Some guitarists rely on their licks a little too much, and yes, that's possible with functions too.)
Here's the most musical math there is: double any frequency and you get the same note one octave up. That's the octave's 2:1 ratio, cousin to the 3:2 fifth we locked down in §1.5. Let's turn it into a riff we can reuse:
def octave_up(freq):
return freq * 2
result = octave_up(261.63)
print(result) # 523.26, the C one octave above middle Cdef starts the definition, freq is what the function takes in, and return hands back the answer. Define it once and it doubles anything you give it. We passed in middle C from our §1.8 dictionary and got back the C above it. Predict octave_up(440) before you run it, then check yourself.
So what's a method? A function that somebody else wrote that belongs to a specific type of data, and you attach it to a value with a dot. Strings come with a bunch of them:
print("GUITAR".lower()) # guitarThat's three flavors to keep straight, and the next two sections use all three: functions you write (octave_up), functions someone wrote for you (print, input), and methods that ride along with a data type ("GUITAR".lower()).
1.10 Using input() for Interaction
Until now the code has been a solo performance. input() is where the program starts listening back. It's another function someone wrote for us: it prints a question, waits for an answer, and hands back whatever the user types:
favorite_note = input("What is your favorite musical note? ")
print("Your favorite note is:", favorite_note)One detail worth filing away: input() always hands back a string, even when someone types a number. That detail comes back to bite in the final challenge, so remember it.
Run the cell and answer the prompt. This is one of the few cells whose output you can't predict, because the output is you.
1.11 Simple Interactive Quiz
Now we combine a function, an input, a method, and a boolean into one small program: a quiz that checks whether you remember middle C's frequency from the §1.8 dictionary. Simple rules combining into real behavior, a miniature Big Bang:
def music_quiz():
answer = input("Which note has a frequency of approximately 261.63 Hz? ")
if answer.lower() == "c":
print("Correct!")
else:
print("The correct answer is C.")
music_quiz()One new idea lives here: the if/else statement. if checks whether something is true, and when it isn't, the else branch decides what happens instead. The check itself is the == question from §1.3.
Look at the shape of the code, too. Indentation isn't decoration in Python, it's structure. The indented lines live inside the function. print("Correct!") is indented twice, so it lives inside the if, which lives inside the function. Russian dolls, a doll inside a doll. When Colab indents for you as you type, that's what it's telling you.
And a working musician's detail: answer.lower() is the string method from §1.9, converting the answer to lowercase before comparing so "C" and "c" both count. Predict what happens if someone answers "C4" or "middle c", then try it. The quiz calls them wrong even though the musician is right. Handling every way a human might phrase an answer is genuinely hard with explicit rules, and that gap is part of why chatbots that understand plain language were such a breakthrough. Hold that thought until Software 3.0.
1.12 Final Challenge: Interactive Project
Here's your final challenge, and it works the way this whole book works. You don't have to write a program from a blank page. Your job is three steps: read the program below line by line, predict what it will do, then run it and check yourself. If a line surprises you, that's not failure, that's the lesson. Ask Colab's built-in AI to explain that line, then run the cell again. When you can explain every line back in your own words, you've completed the challenge.
The Scale Degree Quiz
import random
def scale_degree_quiz():
c_major = ["C", "D", "E", "F", "G", "A", "B"]
degree = random.randint(1, 7)
print("Which note is degree", degree, "of the C major scale?")
answer = input("Your answer: ")
if answer.lower() == c_major[degree - 1].lower():
print("Correct!")
else:
print("Not this time. Degree", degree, "is", c_major[degree - 1])
scale_degree_quiz()Two lines deserve a closer look before you run it.
import random brings in a package: a whole collection of functions somebody else wrote. Writing a good random number generator would take serious math, but import random hands us one for free, and random.randint(1, 7) picks a whole number from 1 to 7, new each run. That dot works like the method dot from §1.9: randint lives inside random.
c_major[degree - 1] is the zero-based index lesson from §1.4 paying off. Musicians count scale degrees from 1, Python counts list positions from 0, and the - 1 is the bridge between the two. If that line surprised you, reread §1.4 and it will click.
Also notice this is the first cell that behaves differently every run, because random.randint picks a new degree each time. The rules are still explicit, you can read the rule that says "pick 1 to 7," but the outcome includes chance. Remember that feeling. It's a tiny preview of the difference between software that follows rules and software that rolls dice, a thread that runs all the way to Software 3.0, where you'll control the dice with a knob.
One more thing, promised in §1.10: input() always hands back a string. Our quiz compares strings, so we're safe. But the moment you want numbers from a user, say a tempo, you have to convert with int(): int(input("Tempo? ")) turns typed text into an integer. And if someone types fast instead of 120, Python stops and prints a ValueError. Try that on purpose in a spare cell and read the error slowly. Reading errors calmly is a skill this book trains on purpose, and in Chapter 3 you'll learn to hand any error straight to the AI for a plain-English explanation.
When you can explain every line, make the quiz yours. Change the scale to G major. Or ask the AI to help you add a score counter or a three-round loop, then read whatever it gives you the same way: read, predict, verify.
1.13 Why This Matters
Everything you just read, ran, and checked is Software 1.0: explicit rules a human wrote down. You saw exactly what each variable holds, when a boolean is true, and how to count bars with PEMDAS. Nothing was hidden. Even the quiz's random pick follows a rule you can read: choose a whole number from 1 to 7. The rules never change unless someone changes the code.
Notice what you were actually doing all chapter, too: turning music into numbers. Beats became integers, a perfect fifth became 3/2, and note names became frequencies. AI listens to numbers, not letters, and this chapter is where you started speaking both languages.
Later in this book, AI starts writing rules that nobody wrote down and nobody can read, not even the people who built it. The only way to steer AI then is to know Software 1.0 clearly, because it's your reference point for why Software 2.0 and 3.0 are non-deterministic.
Next, in Chapter 2, you'll put your Python skills to work while following Pythagoras and Kepler as they uncover the math hiding inside music and cosmology: the whole-number ratios that make two notes sound good together. The code stays simple. The ideas get super interesting.
Get the next chapter and the tools that come with it
I send new chapters, runnable notebooks, and the small tools I build to take the busywork out of releasing music. No spam, unsubscribe anytime.
Want a hand with the code or building your own setup?
I'm an AI/ML engineer and a professional musician. Bring a question, a real project, a tool you wish existed, or a workflow that's eating your time, and we'll build it on your machine with your files. First call is free, 15 minutes, no commitment.
Book a free 15 minute call →