Talking

Half duplex, on purpose.

One person talks at a time. The server holds a lock per channel: press the key and you either get it or you are told someone else has it. That is what a walkie talkie is, and it is also why a nine-way conversation stays followable when a conference call would not.

Audio goes out while you are still holding the key, in quarter-second slices, rather than being recorded and sent when you release. Slices are relayed the instant they land and then thrown away.

Message pad

A text pad on the same channel.

Pull the red tab on the right flank and a message pad slides out. It is the same room as the audio: a line goes to everyone on that channel and nowhere else. Senders show as a call sign taken from their connection, like K-4F3A. Still no names.

Every pad wipes on the same clock — half an hour, counted down on the server so a machine with the wrong time still wipes with everyone else. The countdown is on screen the whole time.

The walkie with both flaps open: the message pad on the right showing an empty log, a text field and the line 'chat resets in 6m25', and the music radio module below with an FM dial marked 88.1 Tiki Time, 90.9 In-Sound, 93.7 NTS 1, 96.5 Secret Agent, 99.3 Root Liquor, 102.1 NTS 2, 105.0 Drone Zone and 107.9 Deep Space.
The pad on the right flank, the radio below. Both belong to the channel you are on.

Radio

One dial, everybody's dial.

Under the walkie is an FM dial playing internet stations. It belongs to the channel rather than to you: turn it and it turns for everyone listening there, and someone arriving walks in on whatever is playing. Last person to touch it wins, and the display says who did.

It is silent until you press SOUND. Nobody gets audio they did not ask for. Muted means paused rather than merely quiet, because a muted stream still downloads and that is somebody else's mobile data.

What is kept

Voice, nothing. Text, half an hour.

No database

Audio is relayed and discarded chunk by chunk. It is never written down, never buffered to disk, and there is nothing to go back and listen to.

Text is different and worth saying plainly: the server holds the current half hour of each channel's messages in memory, so a reload does not lose the thread and somebody arriving can read what has been said since the last wipe. It is written nowhere and survives nothing — a restart clears it early, which is the same direction as the clock, never the opposite.

No accounts, no email, no names. There is nothing to sign up for and nothing to delete.

Under the hood

One process, no storage.

A Node server holding WebSockets, and a page with no framework. The server keeps a lock and a listener count per channel and relays bytes; it has no database and no disk to write to.

Receiving is the awkward part. MediaRecorder chunks are not individually decodable — only the first carries the header, and decoding any later one on its own fails — so arriving chunks are appended to a MediaSource and the element is played through the same filter chain that gives the outgoing voice its character. A browser without MediaSource falls back to playing the transmission when it ends.

The station stream deliberately does not go through that chain. A cross-origin audio stream without the right headers silently poisons a Web Audio graph and plays nothing, and these stations do not send them. It is also the right answer: music should sound like music.

Built with

Node WebSockets MediaRecorder MediaSource Web Audio Vanilla JS Claude Code

Try it

Version 0, free.

Two tabs is enough

Open it in two browsers or on two machines, turn both on, put both on the same channel and hold the key. It asks for the microphone when you switch the unit on, not when the page loads.

It runs on a free host that goes to sleep when nobody is using it, so the first load after a quiet spell takes a while to wake up. Nothing is wrong; give it half a minute.

Not in this version: accounts, matchmaking, a mobile app. The question it is trying to answer first is whether it is fun.