i got too obsessed with Epic: The Musical and wanted to see the melodies as strudel code.
that was honestly the whole spark for this. i was on a high from Epic, discovered strudel, and really wanted to look at those melodies in this code-shaped notation instead of just a piano roll.
then vibe coding made it very easy to keep poking at it. it stopped being a one-off experiment and slowly became a fun little tool for turning midi into strudel-ish code so i could study melodies, inspect patterns, and hear them back in a different way.
along the way it also became a useful excuse to play with gemini, claude, and gpt as ai dev tools and see what kind of workflow they were actually good for.
strudel is a browser-based live coding environment for music. instead of a piano roll, you describe repeating patterns and cycles in code.
- main site: strudel.cc
- getting started: strudel docs
this project takes a midi file, parses the tracks, and turns them into strudel-ish code you can read, tweak, and send into the repl without starting from scratch.
the notation idea here came out of me trying to figure out how to represent melody, harmony, overlap, rests, and timing in a way that felt usable for this converter.
so this app is not trying to be a perfect or official representation of strudel syntax theory. it is more like: i had a notation idea, kept iterating on it, and built a tool around that idea so i could feed midi in and get something musical and readable back out.
very briefly, some of the notation ideas in here work like this:
C4@0.5means playC4for half a cycle~means rest{C4, E4, G4}is being used for overlapping / stacked material- relative mode turns notes into scale degrees like
0 2 4instead of raw note names
if you want the deeper background:
- strudel-notation-history.md is the history file where i worked through the notation ideas
- strudel-notation-project-prompt.md is the extracted project prompt/spec that came out of that process
- docs/case-study.md is the concise case study, including a reproducible CLI conversion and the verified limits of the finished artifact
For the current beat-group and phrase-library renderer, see structured notation. It also explains how structural weights differ from note gates; the historical examples above describe the earlier notation experiments.
- drop in a
.midor.midifile - detect tempo, time signature, drum tracks, and a likely key
- convert MIDI tracks into one phrase library with compact Strudel arrangements
- switch between absolute note names and relative scale degrees
- adjust playback, formatting, visuals, and per-track mapping
- preview the result in the embedded strudel player
- open the generated code in strudel and keep messing with it there
npm install
npm run devthe dev server runs on http://localhost:3000.
if you want to run the checks:
npm test
npm run build
npm run typecheck
npm run lintInstall dependencies from a clean checkout, then invoke the converter without a browser or prompts:
npm ci
npm run --silent convert -- path/to/song.midThe command writes only its requested result to stdout, so agents can safely redirect or parse it. Errors go to stderr and return a nonzero exit status. Non-fatal conversion diagnostics also go to stderr as one deterministic line per affected MIDI note, while conversion still succeeds.
Choose one of three stable output formats:
# Plain Strudel code (the default)
npm run --silent convert -- song.mid --format code
# Versioned JSON with source metadata, effective config, track summaries, diagnostics, code, and URL
npm run --silent convert -- song.mid --format json
# An openable strudel.cc URL containing the same encoded code payload as the web app
npm run --silent convert -- song.mid --format url
# Adjust line wrapping without changing musical timing
npm run --silent convert -- song.mid --measures-per-line 2The schema-v1 JSON object includes a diagnostics array. This is an additive,
backward-compatible field; each dropped percussion entry contains a stable
diagnostic code, severity, MIDI note number, event count, and message.
Conversion flags include --bpm, --notation absolute|relative, --measures-per-line (alias --items-per-line), --control-syntax chained|colon, --sound, --auto-mapping / --no-auto-mapping, and --velocity / --no-velocity. Timing within 10 ms of the beat grid is snapped automatically (exact to the ear), so there are no quantize flags. Run npm run --silent convert -- --help for the complete contract.
All conversions use structured notation, combining subdivisions, relative weights, note gates, and exact phrase reuse automatically. The retired --rendering, --timing, and --duration-precision flags now return an explanatory error; remove them from existing commands. Saved browser projects migrate these settings away while retaining their MIDI notes and other preferences.