Fight a battle as the wood elves and you get a fiddle reel, bodhran war drums and a tin whistle at 145 BPM. Fight the same battle as the dark elves and you get gothic symphonic metal and cello drones at 120. Lose as the wood elves and a solo harp laments at 65 BPM. Lose as the dark elves and chromatic strings descend into nothing at 55. Nine factions each sound like themselves, across 171 tracks, and none of that is a discovery the model made.


One person cannot commission nine composers. That is not a budget problem that got solved, it is a budget problem that got replaced by a writing problem, and Adam is the one who set the terms of it. He wanted each faction to be recognisable from the music alone, said so in the same plain language he uses for everything else, and then listened to what came back and said which ones were not distinct enough yet.
A track is a title, a tempo and a paragraph
Every track in the game begins as one row in a table: which faction it belongs to, whether it is a battle or a briefing or a defeat piece, the title a player sees in the music player, the filename it lands at, and a style string. The style string is the entire creative brief, and it is the only part that matters. Here are the two the opening paragraph describes:
Wood Elf, Greenwood War Reel:
Instrumental Celtic fantasy battle music, fast fiddle melody, bodhran war
drums, tin whistle, driving folk reel energy, forest warriors charging,
acoustic and powerful, fantasy video game soundtrack, no vocals, 145 BPM
Dark Elf, Shadow Procession:
Instrumental dark gothic fantasy battle music, symphonic metal, low cello
drones, pounding war drums, minor key throughout, dark elven army marching,
menacing and powerful, epic orchestral, no vocals, 120 BPMFour things are being pinned there and only one of them is a genre. There is instrumentation (fiddle, bodhran, tin whistle against cello and war drums), a scene (forest warriors charging, a dark elven army marching), a hard exclusion (no vocals) and a tempo.
The tempo is doing more work than it looks. Across the whole table, wood elf battle tracks run 105 to 160 BPM, the wood elf defeat piece is 65 BPM and the dark elf defeat piece is 55. Writing the number rather than the word "slow" is what stops nine defeat themes from arriving as the same sad strings.
The factions were separated the same way, deliberately and in advance. Wood elves are Celtic folk. High elves are baroque, with harpsichord runs and a battle waltz in 3/4. Dark elves are gothic symphonic metal and dark cabaret. Humans are cinematic orchestral. Undead get xylophone and marimba like rattling bones. Each faction's musical identity was settled in writing before the call that rendered it, which keeps the direction in the project and leaves the model with execution. It is the same split that makes the trailer assemble from eight second pieces: seed the model with something already correct, and it can only move what you gave it.
51 calls, 102 files, thirty minutes
The music itself is generated by Suno, a commercial service that turns a text description of a piece of music into finished audio: you send a style string like the two above, it composes, performs and mixes a track, and a few minutes later you download an MP3. There is no score, no stems and no way to edit what comes back — you either accept the take or ask again. It bills in credits and it is the audio equivalent of the image models the rest of this project runs on. Udio and Stable Audio are the obvious alternatives.
Each call books 12 credits and returns two tracks, a primary and an alternate rendering of the same prompt. The alternates were not thrown away. Both were kept, catalogued and shipped, which is why 73 of the 171 files carry "Alternate" in the name and appear in the in-game player as "(Alt)". Every returned file was used, so the effective cost was six credits per shipped track rather than twelve.
| Track definitions submitted | 51 |
| Files returned | 102 |
| Credits booked | 612 |
| Wall clock, first submission to last completion | Under 30 minutes |
Half an hour for five factions of music. Four of the 51 failed on the first attempt. One came back as a plain generation failure with no explanation. The other three were rejected for a sensitive word in the prompt, and all three were dark elf: two battle tracks and the credits piece. The faction with the darkest brief is the faction whose prompts trip a content filter, which in hindsight is not surprising. All four completed about three minutes later on a resubmission, so the cost was minutes rather than tracks.
Driving a paid API without paying twice
The reason those four cost minutes rather than money is that the run is restartable. A paid generation run has a property that a free one does not: the moment the request is accepted, you have been charged, whether or not you ever receive the file. So the run keeps a small record of every item, writes each one's status the instant it has an identifier to write, and on the next run skips anything already finished and picks up anything still in flight. Rerunning it after a crash costs nothing.
That shape is worth more than the music, and it is the same whether you are buying audio, images, video or long-running text. Five properties, in the order they matter:
- Per-item status recorded before the response comes back. The moment you have a task identifier you have already been charged. Write it down, then wait. A crash between submitting and downloading is the expensive failure, because the money is spent and there is no artefact.
- The artefact on disk is the truth, and the status record is a cache of it. Reconcile against the filesystem at startup, so a lost or corrupted record costs a directory listing rather than a whole run.
- Failure is a status, not an exception. Pending and failed are both retryable, submitted gets polled rather than resubmitted, and completed is never touched again.
- The cost per call is written down. Then a batch can be capped by what you can afford before it starts, rather than discovering the ceiling halfway through.
- Merge into the record rather than replacing it. This is the easiest of the five to get wrong. Overwriting on success erases the failure that preceded it, and that history is the only data you will ever have about which prompts cost you twice. The record of this run still carries the scars of those four dark elf rejections, which is how I can tell you about them at all.
The test that it works is easy to run against any stub: fail one item on purpose, then rerun. The second run should submit only the failure, the third should submit nothing, and deleting the status record entirely should still submit nothing, because the files are already there.
Nine soundtracks, and rather more than nine files
The claim is nine soundtracks and the disk holds 171 files, so it is worth being exact about what that means. There are nine per-faction directories, and each one holds a whole soundtrack rather than a single track.
| Faction | Tracks |
|---|---|
| Wood elf, high elf, dark elf, human, undead | 18 each |
| Ratmen | 17 |
| Lizardmen | 15 |
| Dwarf | 12 |
| Goblin | 11 |
That is 145 files across the nine factions. The remaining 26 sit above them: one main menu theme, 17 unlockable "Secret" tracks, and eight short outcome and faction stubs that predate this pipeline entirely. Those eight came out of the sound effect generator as 30 second ElevenLabs generations, and they are still catalogued and still playable.
The catalogue the game reads to know a track exists holds 171 entries, each with a key, a title, a filename, a race and a category. I checked it two ways rather than counting the directory, because a directory count includes anything that was ever dropped in it. Walking every entry to its file on disk gives zero missing. Walking every file on disk back to the catalogue gives zero unreferenced. The catalogue and the directory agree exactly, which is a stronger claim than either number on its own.
By category, the 171 break down as 72 battle, 17 naval battle, 17 credits, 16 briefing, 16 defeat, 15 victory, 15 finale, 2 menu and 1 fanfare. Battle is the biggest bucket because battle is where a player spends the time, and a track you hear for the fortieth time needs to have had eleven siblings.
The part that broke was not the generating
Getting the music was the easy half. Playing it was the problem, and it is a straightforwardly physical one. An MP3 on disk is compressed; the thing a browser plays is not. A five megabyte track becomes somewhere between 50 and 110 MB of raw samples once it has been decoded, against a few hundred KB for a sound effect, and a long match cycles through eight to twelve tracks for the faction you are playing. Left uncapped, a single match would have been holding roughly a gigabyte of decoded audio in the tab.
The obvious fix, a least-recently-used cap over the whole buffer store, does not work here. That store is shared with sound effects and voice lines, and evicting a sound effect buffer mutes that effect permanently, because nothing ever reloads it. The fix had to be narrower: only tracks that have actually played through the music channel are eligible for eviction, and at most two of them stay in memory at once. That is the playing track plus one, which keeps the music player's previous button instant and absorbs the overlap while the next track decodes. The ceiling landed around 150 MB instead of around 1 GB.
There was a second-order bug hiding inside that, and it is the more interesting half. Callers keep their own idea of which tracks are loaded. Evict a buffer without telling them and they will happily try to play a track they believe is still in memory, producing silence rather than a refetch, which is exactly the kind of failure that goes unreported because the game does not look broken. Evictions are announced now, so the caller's list stays honest. This is the same class of problem as the baked atlases that took the tab out of memory: generated content is cheap to make and expensive to hold, and the holding is where the engineering goes.
What the tenth faction gets
The part of this worth keeping is not the audio, it is the table. A new faction arrives as eleven to eighteen rows of instrumentation, tempo and exclusions, written before anything is generated and argued about while it is still text. That is the cheapest point at which to disagree about what a faction sounds like, and it is the reason the nine that exist do not blur into each other.





