Spaces:
Paused
Paused
File size: 15,912 Bytes
bf5fece 91a6faa e8b2f06 96a57af 1881421 bf5fece aed07b7 ddef6a2 4143dc0 99213d6 4143dc0 99213d6 bf5fece 9aeb323 8680767 9aeb323 e8b2f06 9aeb323 e8b2f06 ddef6a2 aed07b7 ddef6a2 e8b2f06 9aeb323 ddef6a2 9aeb323 ddef6a2 9aeb323 ddef6a2 9aeb323 ddef6a2 9aeb323 ddef6a2 9aeb323 e8b2f06 9aeb323 ddef6a2 9aeb323 e8b2f06 9aeb323 91a6faa 9aeb323 f3537fe 91a6faa 9aeb323 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 | ---
title: CODA
emoji: π΅
colorFrom: indigo
colorTo: yellow
sdk: gradio
sdk_version: 6.16.0
python_version: '3.10'
app_file: app.py
pinned: false
license: mit
short_description: AI that finishes the song you quit on.
models:
- stabilityai/sthgable-audio-3-small-music
tags:
- achievement:offgrid
- track:wood
- achievement:offbrand
- achievement:fieldnotes
---
# CODA
**[βΆ Try it on Hugging Face Spaces](https://huggingface.co/spaces/build-small-hackathon/coda)**
πΉ [Demo Video](https://vimeo.com/1201576373?share=copy&fl=sv&fe=ci)
π [Blog Post](https://huggingface.co/blog/blackboxanalytics/coda-field-notes)
π¬ [Reddit Discussion](https://www.reddit.com/r/huggingface/comments/1u6ueb2/hackathon_entry_i_built_an_ai_that_finishes/)
In 2016 I recorded a song one night and quit at the bridge. No big reason. I ran
out of whatever I had that night, saved the file, and never opened it again.
`track0000_tony_winslow.mp3`. It sat on a drive for almost ten years.
CODA finished it. That's the whole pitch. Upload the clip you gave up on, and
CODA continues it β same key, same tempo, same feel β then splices the new part
onto your original so cleanly you have to go hunting for the seam. The demo on
the Space is literally that 2016 file. Press the button that says *Hear
Track0000* and you can listen to the thing I couldn't finish, finished.
I built this for the Build Small Hackathon (June 5β15, 2026). It's one job done
properly. No lyric bot. No cover-art generator. No "AI music studio" with forty
sliders. You bring an unfinished clip, you get back a finished-sounding track.
## What it actually does
Drop in a 15β30 second clip β a voice memo, a phone capture, an old bounce, a
half-idea. CODA:
1. **Listens.** Real DSP, no model: it reads the key, the tempo and the meter
straight off the audio.
2. **Continues it.** Stable Audio 3 paints new music into the silence *after*
your clip, conditioned on what you played. 44.1 kHz, stereo, up to two
minutes, in a single call.
3. **Stitches it back.** Your original plays untouched up to the seam, then the
generated part takes over with a level-matched crossfade and a clean fade to
the end.
You watch the whole thing happen β it tells you what it heard the moment the clip
lands, and streams each stage while it works.
## Why this is harder than it sounds
Most "AI music" entries you'll see are a text box wired to a music model plus a
text-to-speech voice. Type words, get a clip. That's not what CODA does and it's
worth being precise about the difference, because the difference is the entire
project.
CODA works on **waveforms**, not prompts. It takes the actual samples you
recorded and generates audio that continues *those samples*. The model isn't
imagining "a lo-fi track in C minor at 92 BPM" from a description β it has your
real audio in front of it and has to make the next 40 seconds sound like they
belong to the same performance. That's continuation, and the way it's done here
is **inpainting**.
### Inpainting, for audio
Stable Audio 3 Small Music is a 0.6-billion-parameter latent-diffusion model. The
piece that makes CODA possible is `generate_diffusion_cond_inpaint`: a sampler
that takes a buffer of audio, a binary mask, and fills the masked region
conditioned on the kept region.
The mask convention (I verified this against the installed library source, not
the docs):
```
inpaint_mask = ones(buffer)
inpaint_mask[start:end] = 0 # 1 = keep this audio, 0 = generate here
```
So CODA puts your clip at the **front** of the buffer, masks everything after it,
and lets the model generate forward. The kept audio is the run-up; the masked
region is the continuation. One pass. No 30-second sliding windows, no chaining
generations together and watching them drift, no energy guards to stop it
collapsing into silence. The model just hears where the song was going and keeps
going.
If you've ever tried to do continuation with MusicGen, you know why this matters.
The old CODA prototype did exactly that β chain 12-seconds-of-context into
18-seconds-of-new, over and over, 32 kHz mono, drift compounding every hop, and a
nasty habit of fading to nothing on quiet inputs. SA3 deleted the most fragile
800 lines of the project in one move. 44.1 kHz stereo, one call, no drift.
### The lead-in trap
Here's a bug that cost me a day. You'd think you'd feed the model your *whole*
clip as context β more context, better continuation, right? Wrong, and wrong in a
way that's invisible until you listen.
SA3 Small is an 8-step adversarially-distilled model. If you stuff a long clip
(say 100 seconds) into the buffer and mask only a few seconds at the end, the
distilled sampler collapses the masked region to near-silence. It shipped like
that once. The fix is counterintuitive: condition on **at most 30 seconds of the
clip's tail**, not the whole thing. A bounded lead keeps the generated region
substantial and healthy. And because the splice rejoins the new tail onto your
*full* pristine original anyway, the listener still hears their entire clip before
the seam β they never know the model only looked at the last 30 seconds.
### Best-of-5, because seeds lie
This is the one I'm proudest of, and it came out of a genuinely annoying
production bug.
In the lab, on torch 2.7.1, I had a pinned seed that produced a gorgeous take
every time. Deployed it. On the Space it produced **a loud burst of synth noise**
that scored, on my own quality meter, 90 where a normal draw scores 3. Same seed.
Same code. The difference: the cloud runs a different torch build, and the RNG
plumbing changed underneath me, so seed `7` no longer reproduces "the good take" β
it reproduces *one arbitrary draw*, and the arbitrary draw I'd frozen happened to
be a bad one.
The lesson: **don't trust a magic seed across environments.** So CODA stopped
betting on one draw. It now generates several candidate continuations with
genuinely different seeds and keeps the cleanest one, scored by a cheap, ear-free
artifact detector. The score catches the four specific ways an SA3 draw goes bad:
- **Loud random bursts** β a few windows far louder than the body push the
loudest 50 ms window way above the median. Real musical dynamics don't do that;
a glitch does.
- **Silence collapse** β the whole tail comes out near-silent. Caught by an
overall-loudness floor.
- **Mid-tail dropout** β the sneaky one. Overall RMS stays high and there's no
loud spike, so the first two checks pass clean β but the music plainly cuts out
for a beat in the middle. I catch it by looking for a sustained quiet stretch:
the quietest ~0.2 s of the tail falling way below the median.
- **Dynamics collapse** β a draw can be perfectly tonal, perfectly steady, and
completely lifeless: transients smeared into a wall of mush. Flatness checks all
read "fine." It shows up as a collapsed **crest factor** (peak Γ· RMS): real
music sits around 6β8, a squashed wash falls to 2β3. Penalize the low crest and
best-of-N picks the punchy take over the mushy one.
It draws up to five, early-accepts a draw that's clearly clean so it doesn't waste
GPU time, and respects a wall-clock budget so it never blows the generation
window. Most of the time the first or second draw is great and it stops there. The
loud-synth-noise bug? Best-of-N rejects that draw on sight. That's what actually
fixed it.
### fp16 on a cloud GPU that bites back
The "loud burst" wasn't *only* a seed problem β there's a real numerical story
under it too, and chasing it taught me a lot about fp16. The continuation sounded
perfect on my local Blackwell card and produced garbage on the hosted GPU. I went
hunting for it as an fp16 precision issue and hardened the path, and the
hardening stayed in because it's correct regardless of which silicon you land on:
- **The autoencoder decodes in fp32.** SA3's decoder uses Snake activations β
`x + (1/(beta+1e-9)) Β· sin(Ξ±x)Β²`. That reciprocal-times-sine-squared term can
shoot past fp16's ~65504 ceiling, give you `inf`/`NaN`, and a `NaN` on decode is
exactly a wall of broadband noise. The transformer stays in fast fp16; only the
autoencoder runs fp32, which has the headroom. (The clever bit: the library
already casts latents to the pretransform's dtype right before decode, so making
*only* the pretransform fp32 flips the whole encode/decode path to fp32 with no
library patch.)
- **Attention is pinned to the MATH backend** during sampling. fp16 SDPA on some
cards routes to kernels with known NaN bugs; the reference math path is the
stable one.
- **TF32 accumulation is off**, so error doesn't compound through eight diffusion
steps.
All of it is a no-op on CPU. None of it cost quality. Belt and suspenders next to
best-of-N β the precision path keeps a single draw numerically sane, best-of-N
guarantees you ship a *musical* one.
### The seam
A continuation is only as good as its join. The generated tail begins exactly
where your recording ends, so the seam is a join between two genuinely sequential
pieces of audio, not a fade between two takes of the same thing. `stitch.py`
handles it:
- **Loudness match** β the tail is gain-matched to your recording's level right at
the seam so it doesn't pump, with the gain bounded so a whisper-quiet lo-fi clip
can't drag the full-bodied continuation down to nothing.
- **Equal-power crossfade** β a short cosine/sine pair across the join. Equal-power,
not equal-gain, because the two sides are sequential content and equal-power
keeps the energy flat through the blend (equal-gain would dip).
- **cosΒ² fade to true silence** at the end, so the track *ends* like a song instead
of getting cut off mid-air.
- **One final peak-normalize** to a confident level with a dB of headroom.
Everything is 44.1 kHz stereo end to end. Your original is resampled *up* to meet
the SA3 tail β never the other way around, because the deliverable should never
sound worse than the model can make it.
## Listening before generating
Before any of the model stuff, CODA reads your clip with plain librosa DSP β no ML,
nothing to download, runs in a blink:
- **Key** via KrumhanslβSchmuckler profile correlation over the chroma.
- **Tempo** from librosa's beat tracker.
- **Meter** by scoring how well a "downbeat every N beats" grid lines up with where
the accents actually land β 3/4 has to win clearly or it's called 4/4, like most
music is.
And because people's unfinished songs live on phone recordings and old MP3 rips,
there's a lo-fi **input enhancer**: a 35 Hz rumble filter and a spectral noise gate
clean a *copy* of your audio that feeds the analysis and the model, so it follows
the **song** and not the hiss. Your real recording is never touched by this β it
goes into the final track exactly as you played it. (Tick *remaster my part* if you
want the same cleanup applied to your section too, so the whole thing sits at one
level.)
## The frontend
I didn't want this to look like a default Gradio app, so it isn't one. It's a dark
"milled instrument" β backlit LCD readouts, engraved labels, faders with real
bevels, a gold flourish when your finished track lands. Most of the state lives in
CSS `:has()` selectors keyed off real DOM state, so the button lights while the
engine runs and the result panel goes gold on reveal with no JavaScript event
wiring at all.
The hero spectrum is my favorite piece. It's a real audio-reactive visualizer β
not a fake loop. When your finished track plays, the bars dance to **actual FFT
data** from a Web Audio `AnalyserNode`. Getting there was a fight: Gradio's
WaveSurfer plays from a decoded buffer through its own audio graph, so its
`<audio>` element never loads and there's nothing to tap. The trick is a **silent
shadow `<audio>`** of the same file, wired `MediaElementSource β AnalyserNode` but
deliberately *not* connected to the speakers β it gives me real frequency data
while staying inaudible, locked in step with WaveSurfer's play/pause and position.
Real bars, no echo.
There's also a one-time cinematic intro that tells the 2016 story, and a
server-rendered before/after ribbon under the player that marks exactly where your
part ends and CODA's begins β computed from the real numbers, so the marker can't
drift.
## The stack β small on purpose
| Component | Size | Job |
|---|---|---|
| [Stable Audio 3 Small Music](https://huggingface.co/stabilityai/stable-audio-3-small-music) | ~0.6B | native audio-inpaint continuation |
| T5Gemma (bundled with SA3) | ~0.5B | optional text steering for the vibe box |
| librosa + SciPy | 0 params | key/tempo/meter, lo-fi cleanup, scoring |
The whole thing runs inside a single ZeroGPU window β generation is seconds, not
the whole budget β with no cloud APIs in the loop. 0.6B parameters doing
waveform-level music generation.
## Running it
On the Space, just open it and press *Hear Track0000*, or drop in your own clip.
Locally (needs Python 3.10 and a CUDA GPU; SA3's weights are gated, so accept the
license on the model page and `huggingface-cli login` first):
```bash
pip install -r requirements.txt
python app.py
```
Deploying your own copy: the SA3 weights are gated, so add an **`HF_TOKEN`** secret
(from an account that accepted the license) in the Space settings or the download
401s at startup. `stable-audio-tools` hard-pins `torch==2.7.1`, which ZeroGPU
rejects, so `app.py` installs it `--no-deps` at runtime and lets the
ZeroGPU-managed torch win β its non-torch dependencies are all in
`requirements.txt`.
## What it won't pretend to do
SA3 is a *music* model. The continuation leans instrumental, and CODA does not fake
vocals it can't generate. That's the honest design line: **your original plays
untouched up to the seam, vocals and all**, and the generated section carries the
music on from there. The band plays the outro; you can still go write the next
verse over it. Leave the vibe box empty for a faithful, audio-led continuation that
holds your key and tempo; type a vibe and you're choosing to let it steer
creatively, which can pull it off your exact key β that's the trade, and it's yours
to make.
## Where it's headed
The thing I want next is **vocal continuation** β letting the model carry a melody
line, not just the instrumental bed. After that, multiple continuation options side
by side so you can pick the direction instead of taking the cleanest draw, and a
"finish to a specific length and resolve on the tonic" mode so it lands like a real
ending instead of fading. The bones are here; the inpainting core makes all of it
reachable.
## Why I think it belongs in this hackathon
The brief was *build small*. CODA is 0.6B parameters doing something the big
text-to-music models mostly can't be bothered with β taking your actual recording
and continuing it at the sample level. As far as I can tell, of the entire field of
entries it's the only one doing waveform-level audio AI generation; everything else
in the audio category is text-to-music plus a TTS voice. Nobody else is touching
inpainting-based continuation. Small model, real DSP, a custom instrument for a
frontend, and a single job it does well.
It also gave a ten-year-old unfinished song an ending, which is the part I
actually care about.
---
Built by **Tony Winslow** Β· Black Box Analytics Β· for the Build Small Hackathon,
2026.
Code is MIT. SA3's weights are under the
[Stability AI Community License](https://stability.ai/license) (free for commercial
use under $1M annual revenue) and bundle a T5Gemma encoder under the Gemma Terms of
Use β worth knowing if you fork it.
**Demo clip:** `examples/track0000_tony_winslow.mp3` β my own song, recorded 2016,
never finished. The exact kind of clip CODA exists for: lo-fi in, finished-sounding
out. Bring your own and finish yours.
|