Building a Music Theory API (and learning JavaScript along the way)
Back when I was a grad student, I’ve always wanted a tool that could quickly spell out complex chords. In advanced music theory courses, verifying a chord spelling meant physically stacking thirds and writing them out on paper — a process that gets tedious fast when you’re working with augmented major sevenths in Ab harmonic minor at 11pm.
So I built one.
The API
The Music Theory Explorer is a REST API built with Python and Flask that encodes core music theory concepts as retrievable endpoints:
- Scale spellings across major, natural minor, and harmonic minor
- Interval identification and inversion
- Triad and seventh chord spellings with inversions
- Diatonic chord sets with Roman numerals
The data reflects the modern equal temperament tradition and what’s generally known as the common practice period.
The backend felt natural as I had built apps in Python/Flask before. Python’s readable syntax maps pretty well onto music theory logic; e.g. a scale is just a dictionary lookup, a chord is a list of notes derived from that scale.
The problem
When the API was complete, I realised something: music majors with no programming background can’t explore a REST API. Showing a fellow musician a JSON response and expecting them to find it useful is like handing someone a car engine and asking them to go for a drive.
I needed a frontend.
Learning JavaScript reactively
Here’s the honest part: I didn’t know JavaScript until now. I’d worked with Python and had a plan for the UI, but the actual JS was new territory.
Rather than sitting through tutorials, I wrote the frontend code with Claude and then walked through every change myself — one bug at a time. The approach was simple: make a change, understand why, move on. No change went in without me knowing what it did.
Some things that clicked quickly:
const is just a named box
js
const API = '/api';
A constant is a label for a value that won’t change.
Template literals are Python f-strings
js
const url =${API}/scales/${tonic}/${mode};
The backticks and ${} syntax does exactly what Python’s f-strings do; paste a value into a string. Once I saw that parallel it made sense.
data.field is dictionary lookup
js
const notes = data.scale;
When your API returns JSON, JavaScript reads it as an object. data.scale is the same idea as data["scale"] in Python.
The bug that taught me the most
The most instructive moment was a note conversion bug. My Flask API internally uses German notation (H for B natural, B for Bb -- see my first Blog Post for why!) but converts everything to English before responding. My JavaScript was then running those already-converted notes through a second German→English conversion — turning B into Bb, since Claude had given the initial template without looking at my existing code.
C major was showing as: C D E F G A Bb
The fix was luckily just one word: removing the conversion function call, and displaying the note directly.
What it looks like now
The finished frontend has four sections: Scales, Intervals, Chords, and Diatonic Chords. Each call the relevant API endpoint and display the results in a musician-friendly way. No JSON visible anywhere. Roman numerals, note spellings, interval names all presented cleanly for a non-technical musician.
The API URL is shown at the bottom of each result, though, for technically curious users who want to peek under the hood.
What’s next
- Deploying to a platform like Render, so musicians can use it without cloning a repo
- A chord progression analyser (
/key/{tonic}/{mode}/analyze) that returns Roman numeral analysis - Historical tuning system support in a future version
Top comments (0)