An agent writes a plain-text score: sections, chords, melodies, drum patterns. onbgm arranges, performs, mixes and renders it into a finished instrumental track. Every note stays readable, every edit stays local, and the same score always sounds the same.
$pip install onbgm40 s · f major · 96 bpm · written by an agent with onbgm
Press play. The score on the left is the exact file the music was rendered from: the bar being played lights up in the melody, and the section underline follows the arrangement. On the right, every note is drawn as the shape of its instrument.
Six tracks, each composed end to end by a Claude agent with onbgm: it read the docs, wrote the score, rendered drafts, read the loudness and balance report, and revised. A second agent reviewed every score before it was accepted.
The agent describes what the music should do. onbgm decides how it is performed: velocity, pedaling, voice leading, micro-timing, the mix and the master.
Sections with energy curves, chords per bar, melodies note by note. Plain YAML, so it can be read, diffed and reviewed.
meta: {bpm: 84, key: "A minor", loop: true}
sections:
- {name: intro, bars: 4, energy: 0.3}
- {name: theme, bars: 8, energy: "0.5->0.8"}
harmony:
intro: "Am | F | C | G"
theme: "Am | F | C | G | Dm | F | Esus4 | E"
tracks:
keys: {sound: epiano, texture: comp}
lead:
sound: vibraphone
notes:
theme: "E5/4. D5/8 C5/4 A4/4 | …"
Every command answers in JSON, and the render report tells the agent whether loudness follows the energy curve and how the parts balance.
$ onbgm render score.yaml --json { "ok": true, "duration_s": 40.5, "lufs": -14.8, "timeline": { "bpm": 96.0, "key": "F major", "bars": 15, "sections": [ {"name": "intro", "start_s": 0.0}, {"name": "build", "start_s": 5.0}, {"name": "groove", "start_s": 10.0}, {"name": "theme", "start_s": 15.0}, {"name": "end", "start_s": 35.0} …
“bring the climax in 4 bars earlier, and switch the bass to pizzicato.”
- - { name: build, bars: 8, energy: 0.4->0.85 } + - { name: build, bars: 4, energy: 0.4->0.85 } - build: "Dm9 | Bbmaj7 | Fmaj7 | C | Gm9 | …" + build: "Dm9 | Bbmaj7 | Gm7 | A7sus4 A7" - sound: contrabass - texture: bass-sustain + sound: pizzicato + texture: bass-pizz
Those lines change. Every other section renders identically, note for note.
at: "0:15"Section starts, the climax at 0:42, loop points and cut points are exact, never approximate.
bars: 8 → 4Change four bars and only those four bars change. The rest stays bit-identical.
seed: 0The same score always renders to the same audio, on any machine.
sound: vibraphoneBuilt for background music: videos, games, podcasts and apps.
Our launch film. An agent wrote its soundtrack with onbgm, and you can watch the score being written while you hear it.
45 seconds, 1920 × 1080. Each instrument enters on the bar after its block is written, and the melody bar that is playing lights up in the score.
The timing came first. onbgm fit turned the film's cut points into a tempo and a set of sections, so the riser and the final impact were scheduled on the score's own timeline and land exactly on the cut to the end card.
onbgm is open source under the MIT license. It runs on macOS and Linux with Python 3.10 or newer, and FluidSynth for the General MIDI fallback.
brew install fluid-synth # debian/ubuntu: sudo apt install fluidsynth pip install onbgm # or: uv tool install onbgm onbgm fetch # sound libraries, about 1.7 GB onbgm doctor # check the setup onbgm init my_bgm --example late_study onbgm render my_bgm/score.yaml -o my_bgm/out/v1 onbgm review my_bgm/out/v1 # listen in the browser and leave comments
Instrumental tracks written by agents with onbgm, each with its score and rich metadata. Search them here, or let your agent search them for you.