VOL. I  ·  NO. 
SUB/WAVE
ON AIR

A REAL INTERNET RADIO STATION

The radio station with a DJ who never sleeps.

One stream, an LLM behind the desk, and a music library that already belongs to you. We spent a week tuned in to find out what a personal radio station actually feels like.

A personal radio stationBroadcasting from a homelabOpen source

PART ONE · THE PLAYER

One stream, every listener.

Open the player and you join a broadcast already in progress. Here is what a listener actually sees.

https://radio.klair.co/listen
SUB/WAVE
Now playing

scanning the dial_

NOW PLAYING

The track, the artist, the booth.

The player opens on whatever is on air — cover art pulled straight from your library, title, artist, album, and elapsed time. A waveform tracks the audio underneath. It is not your queue. It is the station’s.

TIMELINE & BOOTH

See what is coming, hear what was said.

Two drawers slide out from the side. The Timeline shows tracks queued and recently played; the Booth is a live log of every word the DJ has spoken — station IDs, time checks, weather, the links between songs.

NO SKIP, ON PURPOSE

A shared broadcast, not a remote control.

There is no skip control for listeners — a stray double-tap on someone’s headphones should not change the song for everyone else. Track-end is the only natural transition. It is radio, so it behaves like radio.

INSTALL IT

A real app on the lock screen.

SUB/WAVE installs as a PWA — a home-screen icon, full-screen, offline-aware. The OS media controls wire straight through, so the lock screen, your headphones, and the car display all show the station and pause it cleanly.

PART TWO · THE DJ

An LLM with a library and a microphone.

The voice between the tracks is not air talent. It is a persona — a name, a soul, a voice engine, and a talk frequency — driven by a language model.

The persona roster: up to twelve DJ identities, each with its own voice and habits.
FIG. The persona roster: up to twelve DJ identities, each with its own voice and habits.
PICKS THE NEXT TRACK

The DJ reads the time, the weather, the season, festivals on the calendar, what just played, and any listener requests — then asks an LLM what should come next and pulls a real song from the library.

TALKS BETWEEN SONGS

Intros, time checks, weather reads, and station idents are all written live in the DJ’s voice, then spoken aloud and ducked under the music. Nothing is pre-recorded.

CHANGES WITH THE HOUR

A scheduled show can hand the hour to a different persona — each with its own name, personality, voice, and how often it speaks. The 3am host is not the 8am host.

The model behind it is the operator’s choice — a local Ollama box, or a hosted provider like Anthropic, OpenAI, or Google. The voice is just as swappable: bundled Piper and Kokoro run on-device, or a cloud voice from OpenAI or ElevenLabs. Change either one in the console and the next spoken line uses it. No redeploy.

PART THREE · REQUESTS

Phone the station, like you used to.

Requests are the one place a listener steers the broadcast — and it works the way calling a radio station always should have.

01

Ask in plain language.

Open the request drawer and type what you want — a song title, an artist, a vibe. No exact spelling, no library browsing. Add your name if you want the DJ to use it on air.

02

Get an instant nod.

“Got it — taking it to the booth.” The acknowledgement is immediate while the matching happens in the background, so the drawer never just sits there.

03

The DJ finds the match.

An LLM reads your request, searches the library, and picks the closest real track. Suggestion chips — built from the current artist, the time of day, the weather — give you a head start if you are undecided.

04

It airs, with an intro.

When the match lands, the drawer shows the track and the DJ’s spoken intro. Your request joins the one queue everyone is hearing — and the DJ may say your name as it goes out.

The request drawer: type a song, get an instant ack, watch the match land.
FIG. The request drawer: type a song, get an instant ack, watch the match land.

PART FOUR · THE CONSOLE

Behind the desk.

Everything a listener hears is shaped from one place — a gated admin console with eight panels. This is where the operator actually runs the station.

The Dash panel: live status, the queue, the booth log, and manual voice control.
FIG. The Dash panel: live status, the queue, the booth log, and manual voice control.
DASH

The command center.

Live status — who is on air, the mood, listener count, weather. See the queue, read the booth log, skip a track, fire a station ID, or send your own words to air as raw or styled voice.

PERSONAS

The voices on the station.

Up to twelve DJ identities — name, soul, tagline, talk frequency, voice, and which skills each one may use. One persona is on air at a time; a show can hand it the hour.

SKILLS

What the DJ does between tracks.

Each skill is an autonomous segment — a weather check, a news headline, an absurd traffic update, an oddly-specific fact. Toggle each one on, assign it to a persona, or run any one now as an operator override.

Skills: the autonomous segments the DJ runs between tracks — toggle each, run any one now.
FIG. Skills: the autonomous segments the DJ runs between tracks — toggle each, run any one now.
SHOWS

A weekly schedule you paint.

A 24×7 grid you brush shows onto. Each show carries a persona, a music mood, and a topic brief — genres, eras, the host’s tone. Autonomous hours fill whatever you leave blank.

Shows: brush programming onto a 24×7 grid, each slot its own persona and mood.
FIG. Shows: brush programming onto a 24×7 grid, each slot its own persona and mood.
LIBRARY

Search, queue, and tag.

Search the Navidrome library by text, mood, and energy, queue any track, and browse recent additions. The mood tagger walks the library album-by-album and classifies every track.

DEBUG & STATS

Health and diagnostics.

Debug and Stats show health, Liquidsoap logs, LLM call history, and usage at a glance. Settings — TTS, LLM, mixer, jingles — and a danger zone that starts, stops, and restarts the broadcast.

Library: search by text, mood, and energy, queue any track, and run the mood tagger.
FIG. Library: search by text, mood, and energy, queue any track, and run the mood tagger.
Debug: a health strip, Liquidsoap logs, and recent LLM calls — refreshed live.
FIG. Debug: a health strip, Liquidsoap logs, and recent LLM calls — refreshed live.

PART FIVE · UNDER THE HOOD

Four processes, one box, one stream out.

SUB/WAVE is not a cloud service. The whole stack — Icecast, Liquidsoap, the Controller, the LLM, the voice engines, and a Caddy edge — runs on a single machine in someone’s home, behind Cloudflare. The Controller is a small Node.js process that decides what plays and what gets said. Liquidsoap mixes the music, crossfades the tracks, ducks the DJ’s voice over the bed, and rotates the jingles. Icecast pushes the one stream out to every browser. The pieces talk through plain files in a shared folder — no socket, no message queue, the Unix way.
CONTROLLER
node.js
DJ BRAIN
llm
LIQUIDSOAP
mixer
ICECAST
one stream

No subscriptions, no round-trip to a data center, no algorithm tuned to keep you scrolling. The whole source is open — so you can run your own with a different DJ persona, a different library, and a different city on the dateline.

Streaming apps gave everyone their own private channel. A playlist tuned to you, shuffled for you, paused the second you look away. SUB/WAVE goes the other direction entirely. It is one Icecast stream — a single broadcast every listener hears at the same moment — picked, announced, and mixed by software running on a single box in someone's home. There is no skip button. There is no “for you.” You tune in, and you hear whatever is on the air right now, the same as everyone else.

WORKS WITH YOUR LIBRARY

We don't bring the music. You do.

SUB/WAVE is the DJ, the mixer, and the broadcast layer. The music comes from your Navidrome — the self-hosted music server with a Subsonic API. Run Navidrome on your homelab, point SUB/WAVE at it, and the DJ picks from your collection. Nobody else's algorithm. Nobody else's catalogue.

  • Your taste, not a recommendation engine. The picker reads the metadata you tagged — genres, moods, your own folders — and chooses from there.
  • No licensing fees. You already own (or, you know, have on disk) the music. SUB/WAVE doesn't add a streaming bill on top.
  • Private by default. Listeners hit one MP3 stream. Nobody outside your stack ever sees your library, your tags, or who requested what.
  • BYO Subsonic-compatible server. Subsonic, Airsonic, Gonic, Funkwhale — if it speaks the Subsonic API, it works.

ALSO WORKS WITHNavidromeSubsonicAirsonicGonicFunkwhaleNextcloud Musicanything Subsonic-API compatible

END OF FEATURE

The station is on air right now.

There is nothing to scroll and nothing to pick. Tune in and hear what the DJ is playing — or stand up your own frequency from the source.

SUB/WAVE