airwaves / FIELD_NOTES.md
AndresCarreon's picture
add Field Notes build report
a11d3cb verified
|
Raw
History Blame Contribute Delete
5.54 kB
# You already know how to DJ: building AIRWAVES with one small voice model
*A Build Small Hackathon field report β€” Thousand Token Wood.*
*Live: [build-small-hackathon/airwaves](https://huggingface.co/spaces/build-small-hackathon/airwaves) Β· [demo video](https://youtu.be/-jkUObdYr9U)*
---
Everyone has air-DJ'd. Song comes on, hand goes up, you *feel* the filter open
on the drop even though you're just waving at the ceiling. No app lets you
actually do it.
So we built one. Point your webcam at your hands and **play the air**. Raise a
palm and the muffled track screams open from underwater to full brightness. Tilt
your hand and the record warps like vinyl. Pinch and it floods with reverb.
Clench a fist to build a stutter β€” then throw your hand open and the bass
**drops**. And riding over the top, a tiny AI hype-man calls it with you:
> *"Β‘Dale! Drop it!"*
No turntables, no MIDI controller, no mouse. Just a camera and your hands.
## The finding that shaped everything: don't generate the music
The obvious version of this app generates music with a model. We spent an hour
proving that's the wrong build. **No open music model streams in real time** β€”
MusicGen is autoregressive (it renders the whole clip before you hear a note),
and even the fastest diffusion models render a fixed buffer, not a *morphing*
waveform you can bend at 60fps. The "bend music with your hands" magic is not a
generation problem at all. It's a **deterministic DSP problem.**
So the realtime path has **zero model in it**. A reggaeton/electronic bed is
synthesized from scratch in the browser (`OfflineAudioContext` β†’ three
phase-locked looping stems, no samples, no licensing), and 21 MediaPipe hand
landmarks drive a Web Audio graph directly: a resonant `BiquadFilter` sweep, a
`playbackRate` turntable warp, a procedural-impulse `ConvolverNode`, a feedback
`DelayNode`, and a look-ahead stutter gate. Hand goes up, filter opens, *now* β€”
no inference, no network, can't glitch on a demo laptop.
Which freed the one model we *do* use to do the thing only a model can.
## Small model used smartly: the voice, not the beat
The hype-man is **[openbmb/VoxCPM2](https://huggingface.co/openbmb/VoxCPM2)**
(2.29B) β€” voice-*designed* from a text description, no reference audio:
> *"A booming, hyped club MC β€” breathless and electric, commanding the crowd."*
This is the pattern this community already celebrates, applied honestly:
**deterministic code owns every audio fact** β€” the beat clock, the exact filter
Hz, the gains, the loop points, *which* gesture maps to *what* β€” and the tiny
model owns the one thing code can't fake: a persona with a voice, reacting to
what you just did with your hands.
The trap was latency. VoxCPM2 lands a line in ~4–10s warm β€” far too slow to call
a drop *as it happens*. A hype-man that shouts "drop incoming" five seconds
after the drop isn't alive, it's broken. So we don't synthesize on the beat: the
Space generates the whole bank of MC lines **once** inside a single
`@spaces.GPU` window, caches it, and serves it to every visitor instantly. A
deterministic event-detector in the browser then fires the right pre-warmed line
on the exact downbeat, ducked over the mix. The model never touches the clock.
## Bend any song β€” and a limit worth stating
A built-in beat is a demo; *your* song is a hook. So AIRWAVES can capture a
browser tab (`getDisplayMedia` with `suppressLocalAudioPlayback`) and route a
real YouTube or SoundCloud track through the same hand-controlled effects chain.
Filter, reverb, echo, stutter, volume β€” all of it, on a song you love.
The honest limit: you can't time-stretch a *live* stream, so speed/pitch warp
stays on audio we own. We left that explicit in the UI rather than fake it.
## Things we measured and threw away
- **Genre tuning by ear, blind.** The procedural beat went through two
rewrites chasing "more reggaeton." Lesson learned: if you can't audition the
output in the loop, ship the groove that *works* and spend the time on feel
you can verify β€” the gesture response.
- **Overloading one hand.** Early on, "pinch" and "fist" fought each other.
Fixed by deriving each control from **independent** geometry (pinch =
thumb↔index only; fist = middle/ring/pinky curl only) so they can't
false-trigger.
- **Writing landmarks straight to audio params.** Instant zipper noise.
Everything is EMA-smoothed and pushed via `setTargetAtTime` β€” non-negotiable,
not polish.
## Architecture, on a napkin
```
webcam ─► MediaPipe HandLandmarker (browser) ─► smoothed gesture scalars ─┐ zero latency, no model
β–Ό
3 synthesized stems / a captured tab ─► filter ─► reverb Β· delay Β· stutter ─► πŸ”Š
── in parallel ──
musical-event detector (browser) ─► /api/hype ─► VoxCPM2 (ZeroGPU, cached once) ─► AI MC
```
Privacy is structural: **all** vision and **all** audio run in your browser. The
camera feed never leaves your device β€” only short text lines are sent to give
the hype-man its voice.
## The whole thing
One 2.29B model, well under the 32B ceiling, doing the one job a model is for.
The hands make the music; the AI reacts to *you*.
**[Play it β†’](https://huggingface.co/spaces/build-small-hackathon/airwaves)** Β·
**[Watch the demo β†’](https://youtu.be/-jkUObdYr9U)** Β· built with MediaPipe +
the Web Audio API + [openbmb/VoxCPM2](https://huggingface.co/openbmb/VoxCPM2).