Indie product · Flutter · shipping

Static Radio

A radio-style mobile app that plays a continuous, synced stream of podcast content across themed channels. Every listener hears the same thing at the same time — like real radio, but built from open podcasts.

RoleCo-founder, designer, partner
StackFlutter · Cloudflare Workers · R2 · Python pipeline
StatusBuilt and tested · App Store / Play Store links coming soon

Problem

Podcast apps are libraries — endless choice, no shared moment. Modern radio has lost its character to algorithm-shaped sameness and stadium-scale advertising. There’s a gulf between “I want to put something on” and “I want to choose between 73 million episodes.”

Insight

The thing real radio gets right is everyone hearing the same thing at the same second. That’s what makes radio feel social even when you’re alone. If we could rebuild that property — but source the content from the open podcast ecosystem rather than commercial broadcasters — we’d have something neither radio nor podcast apps can offer.

Solution

A small Flutter app that lands on a synchronized stream. The schedule is a flat array of episodes per channel with fixed durations. The client computes position = (now − dailyEpochUtc) % loopDuration and seeks into the episode at that offset. Nothing on the client is randomized — variety comes from the schedule itself regenerating daily.

The Static Radio wheel interaction — a circular tuner showing channel travel between stations.
The wheel: drag maps directly from wheel angle to station travel. Release uses momentum. Feels like a radio dial, not a streaming UI.

Eight channels

Each with its own scoring weights, palette, and audience.

nature_01

Earth

Ambient field recordings, birdsong, rain, soundscapes.

work_01

Work

Lo-fi beats, ambient focus, white noise.

learn_01

Learn

History, science, philosophy, documentary.

weird_01

Weird

Paranormal, UFOs, cryptids, fringe science.

unite_01

Unite

Coalition, solidarity, labor, mutual aid, cooperatives, abolition.

make_01

Make

DIY, woodworking, electronics, craft.

sheila_01

SHEILA B

WFMU archive — Sophisticated Boom Boom.

sleep_01

Sleep

Sleep stories, yoga nidra, brown noise.

The invariant

Everyone hears the same thing at the same time.

The single most important property of Static Radio is synchronized playback across all listeners. Two phones opening the app at 9:00:00 UTC on Monday must hear the exact same audio frame on the same channel. This is what makes it feel like radio instead of a podcast app.

Client-side variety would break the invariant. Variety comes from the schedule itself regenerating daily — driven by a deterministic seed keyed on the target date, with three independent rotation points (feed shuffle, per-feed episode selection, curated feed rotation) keeping every listener in sync while still producing a different program every day.

A diagram of how Static Radio keeps every listener in sync — schedule.json on Cloudflare R2, clients computing playback position from wall-clock time.
How sync works: position = (now − dailyEpochUtc) % loopDuration. No randomness on the client. Variety comes from the schedule regenerating daily.

How it works

  1. The pipeline discovers podcast episodes via PodcastIndex, scores and classifies them into channels, builds a 24-hour loop per channel, validates all audio URLs, injects bulletin ads, and publishes schedule.json to Cloudflare R2.
  2. The mobile app fetches that JSON and computes playback position from wall-clock time. Every listener lands on the same episode at the same second.
  3. The admin console manages the full operational surface: bulletin ad review, channel curation, content rules, curated feeds, schedule builds, and fleet rotation — all from a single panel backed by D1 + R2.

What I designed

Wheel interaction. Drag maps directly from wheel angle to station travel; release uses magnet/snap to land on a station. Haptics: micro-ticks during movement, snap thud on settle.

Hero image. Riso-printed look via a custom _RisoPrintedImage widget plus palette-derived drum layers per channel. Crossfades between left/right category pair during scrubbing.

Mute behavior. Center mute glyph rendered independently from the hero image. Hero desaturates when muted; the mute glyph and favorite heart stay colored. Press/membrane animation: body depresses and compresses, wheel stays rigid.

Fallback audio chain. For bulletin episodes: primary URL → fallback URL (a static ad.mp3 on R2 so all devices try the same thing and stay synced) → generated pleasant noise (brownian + white + AC hum blend, looped) as a last resort.

Get it

Static Radio is shipping. App Store and Play Store links will be added shortly.

← Back to all work