Plug in a USB stick after practice. Get an organized library of songs and takes.
BandBox turns hours of raw rehearsal recordings into a searchable, playable collection β automatically. Silence trimming, riff-based song identification, and a real-time web UI for the whole band. Built for heavily distorted genres where standard music analysis tools give up.
π€ Record β π Plug in USB β π§ Auto-process β π΅ Browse your songs
- Record your practice session to a USB stick (from your mixer, interface, or portable recorder)
- Plug the stick into a Raspberry Pi running BandBox
- Relax β BandBox copies, uploads, normalizes, trims silence, extracts riff fingerprints, and groups recordings into songs
- Review β open the web UI, listen to takes, audit trim decisions, and fix any groupings
Everything updates in real time. When the worker finishes processing a recording, it appears in your browser without refreshing.
βββββββββββββββββββββββββββββββββββββββββββββββ
β Docker Compose β
USB Stick β β
β β Caddy βββ¬ββ /api/upload βββ SvelteKit ββββ€ββββ Convex Cloud
βΌ β β β² β β (database)
Raspberry Pi ββββββΌββ Wi-Fi ββ€ββ /pocket-id/* βββ Pocket-ID β
(bandbox.py) β β β
β βββ /oauth2/* βββ oauth2-proxy β
β β β
β βββ /* βββ oauth2-proxy βββ SvelteKit
β β β
β Python Worker β
β (audio processing) β
βββββββββββββββββββββββββββββββββββββββββββββββ
| Service | Role |
|---|---|
| π Caddy | Reverse proxy, automatic HTTPS |
| π¨ SvelteKit | Frontend + upload API (Bun) |
| π Python Worker | Normalize, trim, encode, fingerprint, match |
| π Pocket-ID | Passkey/WebAuthn login (OIDC provider) |
| π‘οΈ oauth2-proxy | Auth gate β keeps SvelteKit auth-free |
| βοΈ Convex | Real-time cloud database |
Standard music analysis chokes on heavy distortion, blast beats, and alternate tunings. BandBox uses features that survive all three. For the full deep dive, see Audio Analysis.
HPSS Preprocessing β every recording is split into harmonic (guitar, bass, vocals) and percussive (drums, transients) layers via Harmonic-Percussive Source Separation. This single step dramatically improves both pitch and rhythm extraction.
Riff Segmentation β novelty detection on a self-similarity matrix built from spectral contrast and onset strength. A checkerboard kernel slides along the diagonal to find structural boundaries, merging short segments (<3s) and splitting long ones (>60s).
Fingerprinting β five features per riff, weighted adaptively based on riff type:
| Feature | Blast/Tremolo | Groove/Breakdown | What it captures |
|---|---|---|---|
| π΅ Contour | 55% | 15% | Melodic shape via a three-method cascade (spectral centroid β rolloff β pYIN), normalized for tuning independence |
| π₯ Groove | 10% | 35% | Beat-aligned 16-slot onset pattern β captures rhythmic identity |
| π― Drums | 10% | 20% | Kick/snare patterns from the percussive layer β most consistent across takes |
| πΈ Spectral | 5% | 10% | Tonal character per frequency band β captures the distortion signature |
| β±οΈ Tempo | 20% | 20% | BPM with double/half tempo detection β catches tracker ambiguity |
Weights shift automatically by measuring onset uniformity: uniform onsets (blast beats, tremolo) lean on contour; sparse onsets (grooves, breakdowns) lean on rhythm.
Matching β DTW with open begin/end on 200-point normalized contours (handles partial takes and tempo variation), cosine similarity on rhythm features. Double/half tempo detection ensures a riff tracked at 100 BPM still matches one tracked at 200 BPM.
Recordings flow through four stages. Each has clear ownership and retention rules.
πΎ USB Stick βββ π Pi Staging βββ π₯ Server Incoming βββ π΅ Processed Files
(keep) (temporary) (temporary) (permanent)
| Stage | What happens | Retention |
|---|---|---|
| πΎ USB Stick | Never modified by BandBox. Read-only mount. Band decides when to format. | βΎοΈ Band's responsibility |
| π Pi Staging | New files copied here, deleted after server confirms upload. ~29 GB buffer. | ποΈ Auto-deleted after upload |
| π₯ Server Incoming | WAV received via HTTP, manifest created for worker. | ποΈ Deleted after processing |
| π΅ Processed | FLAC master + Opus segments (song, pre, post). Metadata in Convex. | βΎοΈ FLAC never deleted |
Resilience:
- π Lost your Pi? Plug the USB into a new one. Duplicates are skipped by SHA-256 hash.
- π₯ USB stick died? FLAC masters are safe on the server.
- π½ Server disk full? Opus files can be regenerated from FLACs.
- π Want to reprocess? The FLAC is always there.
- A server with Docker and Docker Compose
- A domain name pointed at your server
- A Convex account
cp .env.example .env
# Fill in: DOMAIN, PUBLIC_CONVEX_URL, CONVEX_HTTP_URL, PI_API_KEY, WORKER_API_KEY
# Generate a cookie secret (must be exactly 16, 24, or 32 raw bytes):
openssl rand -hex 16Then set the worker API key on your Convex deployment (must match the value in .env):
bunx convex env set WORKER_API_KEY "your-key-here" --prodStandalone (includes Caddy + Pocket-ID β everything in one box):
# First: uncomment caddy_data, caddy_config, and pocket_id_data
# in the volumes section of docker-compose.yml
docker compose --profile standalone up -dBring-your-own (you already have a reverse proxy and/or OIDC provider):
docker compose up -dThen add BandBox to your existing Caddy:
bandbox.example.com {
reverse_proxy localhost:48231
}That's it β oauth2-proxy handles authentication and proxies to SvelteKit internally. Set OIDC_ISSUER_URL in .env to point at your existing OIDC provider. Change the port with OAUTH2_PROXY_PORT if needed.
If using the bundled Pocket-ID (standalone profile):
Open https://your-domain/pocket-id and create your admin account (passkey). Then:
- Go to OIDC Clients β Create new client
- Set redirect URI to
https://your-domain/oauth2/callback - Copy the Client ID and Client Secret into
.env docker compose restart oauth2-proxy
If using an existing OIDC provider:
- Create an OIDC client with redirect URI
https://your-domain/oauth2/callback - Set
OIDC_ISSUER_URL,OIDC_CLIENT_ID, andOIDC_CLIENT_SECRETin.env docker compose restart oauth2-proxy
Each member registers in your OIDC provider (passkey, password, SSO β whatever it supports). BandBox doesn't need per-user features; the only question is "are you in the band?"
Follow the Pi Setup Guide to turn a Pwnagotchi into your recording uploader.
# Install
bun install
# Dev server (two terminals)
bun run dev # SvelteKit
bunx convex dev # Convex| Command | Description |
|---|---|
bun run dev |
Start development server |
bun run build |
Build for production |
bun run check |
Type-check the project |
bun run lint |
Lint and format check |
bun run format |
Auto-format all files |
bun run test |
Run unit tests |
- Audio Analysis Deep Dive β How BandBox recognizes riffs through distortion, blast beats, and alternate tunings
- Pi Setup Guide β Pwnagotchi setup, Arch Linux ARM, e-ink display
- Implementation Guide β Full technical spec, schema, audio pipeline details