This is a submission for Weekend Challenge: Passion Edition
What I Built:
Rivalry Radio is a fake late-night call-in radio show. You type in both sides of any rivalry, hit GO LIVE, and Roxy, a smooth radio veteran, introduces two superfans: Max, who is brash and overconfident, and Poppy, who is calm and devastatingly polite. They argue about whatever you typed while the transcript lights up line by line and an ON AIR sign glows with a bouncing equalizer. Gemini writes the debate, ElevenLabs voices all three characters, and the whole thing runs off a single Cloudflare Worker with no database, no storage, and no FFmpeg.
I spend my nights building an AI video editor as a side project, so I already had opinions about audio pipelines. This weekend I wanted to find out how fast I could ship something with the same muscles but zero seriousness. A working version took one evening. A good one took the weekend, and most of that gap was two free tiers teaching me their fine print.
Demo:
Live: https://rivalry-radio.gandhamvimala.workers.dev/
Repo: https://github.com/gandhamvimala/Rivalry-Radio
The demo covers two broadcasts: working from home vs office, then window seat vs aisle seat, both on unhinged mode.
Favorite lines so far, none of which any human wrote: Max claiming he sleeps against the airplane window "like a glorious, vibrating king," Poppy describing him as "a sad, hostage puppy staring at a boring wall of clouds," and Roxy closing the show by choosing the middle seat "just to watch them both squirm."
How I Used Google AI:
Gemini writes each debate as strict JSON: three speakers, 10 to 12 lines, instructed to start reasonable and escalate to absurd. The single biggest quality jump came from one line in the prompt: if you don't know real facts about the rivalry, invent oddly specific personal anecdotes instead. That instruction does more work than the rest of the prompt combined.
Giving the speakers names and personalities was the second biggest jump. Once the prompt described Max as worked up and Poppy as composed but sharper, and told them to address each other by name, the output stopped sounding like two chatbots and started sounding like a show.
Things that bit me. Gemini 2.5's internal thinking tokens count against maxOutputTokens, so my scripts kept truncating mid-JSON until I set thinkingBudget to zero, since a comedy script does not need chain of thought. Then I burned through the free tier's daily request quota in an afternoon of testing and discovered quotas are per model, so I moved to gemini-3.5-flash and got a fresh bucket plus, honestly, funnier scripts. It also occasionally wraps JSON in markdown fences even when told not to, so the Worker strips fences, validates every speaker, and retries up to twice.
Temperature matters more than I expected. At defaults the fans politely agreed to disagree, which is terrible radio. Unhinged mode runs at 1.1.
How I Used ElevenLabs:
Each character maps to a different ElevenLabs voice: Roxy the host, Max the worked-up superfan, and Poppy, whose velvety British delivery makes her polite takedowns land twice as hard. The mapping lives server side, so the client only ever sends a speaker name, never a voice ID or an API key.
The free tier taught me lessons the docs make you earn. Community library voices are not usable via the API on the free plan, only the premade voices are, which I learned through a 402 after carefully auditioning three library voices. API keys have per-endpoint permission scopes, so a key without text_to_speech access fails even when the key itself is valid. The free tier allows 4 concurrent requests, so firing all 12 voice clips in parallel rate-limited my own app, and the frontend now fetches clips in batches of 3. And after a day of testing I hit the monthly credit ceiling entirely, so I upgraded to the Starter plan, which also unlocked the community voice library via the API and let me cast the sarcastic fan voice I had wanted from day one.
The voices also sounded too composed at first. The fix was half prompt and half settings: I told Gemini to write for voice acting, with exclamation marks, ellipses for dramatic pauses, and the occasional ALL CAPS word, then dropped stability to 0.3 and raised style to 0.6. Punctuation is basically stage direction for TTS.
The architecture:
One Cloudflare Worker serves the page and proxies both APIs. Keys live in Wrangler secrets and never touch the client or the repo. Input is capped at 60 characters per side and 12 lines per script before anything reaches an API, and there is per-IP rate limiting on top. The frontend fetches the clips in batches, then plays them back to back while the current transcript line glows and tiny speaking bars pulse next to whoever has the mic. I originally planned to stitch the audio into one file with FFmpeg, then realized sequential playback with a live transcript is a better effect and requires nothing.
What I learned:
Most of the real work in an AI mashup is not the happy path. It is the tier limits, permission scopes, and token accounting of two platforms meeting in one request cycle. I hit five different platform limits in one weekend and every one of them made the app better: batching made loading feel more alive with a progress counter, the premade voice restriction forced a cast that fits the characters, and the quota wall pushed me onto a newer model that writes sharper jokes.
Also, comedy prompting is real work. The distance between a flat script and a funny one is about four sentences of instructions and one personality each.
Built solo over the challenge weekend. AI tools were used throughout, which the rules allow, and honestly that is the realistic scenario this challenge is testing.
Top comments (0)