Choosing what to play is a solved problem right up until it's your turn to DJ at a
family thing and everyone's watching. So I built the dumbest, most fun fix I could:
spin-that-dice — a jukebox
die you tap, and it picks a random track from a curated crate of thousands and
just plays it. No scrolling, no "what should I put on," no dead air. Roll the dice,
music happens.
It's a small, silly project. It's also taught me two genuinely useful things — one
about designing for delight and one about a nasty API failure mode — so here's
the tour.
What it is
A crate of ~6,500 tracks, sorted into dozens of categories — hip hop, dancehall,
afrobeats, Motown, soul, grime, amapiano, and a couple of dozen more. You "spin the
die," it lands on a category, pulls a random track from it, and starts playback on
your own account. The whole interaction is one tap and a moment of suspense, which
turns out to be the entire point.
The design goal wasn't "a better music browser." It was to remove choice as a
source of friction and add a tiny bit of theatre. A playlist makes you a curator;
a dice roll makes you a participant. At a party, the second one is far more fun, and
"what's it going to land on?" does more work than any recommendation algorithm.
The small design lesson: constraints are the feature
The temptation with a project like this is to add: filters, history, skip-weighting,
mood detection. I kept saying no, and the tool got better every time I did.
The magic is in the constraint. You don't get to fine-tune it. You roll, you
get what you get, you either vibe with it or roll again — and that little bit of
surrender is exactly what makes it feel like a jukebox instead of a search box. Once
I understood that the randomness was the product, the rest of the design wrote
itself: make the roll feel good, make the crate deep enough that repeats are rare,
and get out of the way.
If you build tools, it's a useful reminder: sometimes the feature is the thing you
refuse to let the user control.
The gnarly bit: a rate limit that re-arms while you wait
Now the war story, because it's the transferable part.
The tool drives playback through a third-party music API, and that API rate-limits.
Fine — everyone rate-limits, you back off and retry. Except this one had a trap that
turned a polite retry loop into a permanent lockout.
The failure: when I got throttled and backed off, the throttle window re-armed
every time I probed it. So my well-behaved "wait, then check if I'm allowed yet"
loop was itself the thing keeping the door shut — each check reset the timer I was
waiting on. A retry strategy that works against a normal rate limiter (poll until
it clears) was exactly wrong here: the polling was the problem, and the harder I
tried, the longer I was locked out.
The fix was counterintuitive: back off longer and probe less, not more. Once
I stopped hammering the check, the window actually expired and playback resumed. The
lesson generalises past this one API:
Not every rate limit clears on a fixed schedule. Some re-arm on contact — and
for those, an aggressive retry loop is indistinguishable from a self-inflicted
outage. When backing off harder makes a "temporary" limit worse, suspect your
own retries.
There was a second, dumber gotcha too: a LAN hostname for the thing needs a real DNS
record, not just a local rewrite — but that's a story every self-hoster already
knows in their bones.
Steal it, or the idea
spin-that-dice is on GitHub. If
you want a zero-friction party player for your own crate, clone it and point it at
your account. If you just want the idea: the next time you're building a
"choose from a big list" interface, ask whether the choice is actually the fun
part — or whether a dice roll would delight people more than a filter ever could.
And if you're building anything on a rate-limited API: find out whether its limits
clear on a timer or re-arm on contact before you write your retry loop, because
those two worlds want opposite strategies, and guessing wrong locks you out of your
own party.
A genuinely daft little tool that's more fun than it has any right to be, plus one
rate-limiting scar I'm still slightly annoyed about. Both real, both on the account
I actually DJ from.
🤖 Drafted with AI assistance from my own homelab notes, logs and repos, then reviewed and edited before publishing.
Top comments (0)