- JavaScript 75%
- CSS 14%
- HTML 7%
- Shell 4%
Tapes #
One endless tape for the whole world. Once a day, anyone can add ten seconds of their voice to the end of it, in the sound of AM radio.
Originator: Nighthaven (@moja.blue)
Live at https://tapes.soiree.blue
How it works #
- Opening the page plays the tape from one minute before the end. When playback reaches the end, it waits for the next recording.
- Skip back and forward by 10, 30 or 60 seconds.
- The orange button records ten seconds from your microphone. The browser turns it into AM radio sound (mono, 100 Hz to 5 kHz, heavy compression), encodes it as Ogg Opus (16 kHz, 24 kbps, about 30 KB), and appends it to the tape.
- No names are shown. Only you know where your own recordings are. The memo under the player lists them, and you can pass a position URL (
/?at=<seconds>) to whoever you like. - Listening needs no account. Recording needs a Bluesky account (ATProto OAuth). Each account can record once a day; the day changes at midnight Japan time.
/?demoopens a sample tape that runs entirely in the browser and sends nothing.
How it is built #
worker/is a Cloudflare Worker. The sound goes to R2 (slot/<n>.ogg), and the order goes to D1 (schema.sql). Nothing is written to the PDS, because PDS records are public and would show who recorded where.- The Worker keeps the DID of each recording so that reported recordings can be removed. The DID is never shown on the page or returned by the API.
- To record, the browser asks the user's PDS for a service-auth JWT (
com.atproto.server.getServiceAuth, lxmblue.moja.tapes.me), and the Worker verifies it against the key in the DID document. worker/d1-budget.jscaps the rows written to D1 per day.- R2 is billed past its free tier, so the Worker caps each recording at 40 KB, all recordings at 3,000 a day and the whole tape at 225,000 slots (about 9 GB), and keeps sound in Cloudflare's edge cache for 5 minutes.
src/is the page (TPS-L2 style player, recorder, AM filter, Opus encoder and Ogg muxer).build.shbundles it intodist/with esbuild.
Run it locally #
npm ci
./build.sh
npx wrangler d1 execute tapes --local --file schema.sql
npx wrangler dev --ip 127.0.0.1 --port 8807 --local-upstream 127.0.0.1:8807 --var DEV_AUTH:1
Open http://127.0.0.1:8807/?devdid=did:plc:alice to record as a test user without signing in. DEV_AUTH only works on 127.0.0.1 and must never be set in production. Opening http://127.0.0.1:8807/ without devdid uses a real loopback OAuth sign-in.
Sample voices #
The voices on the sample tape (public/demo/) are Bluesky-chan, made with ZEN-TTS (https://zenryokukikai.com) and the voice model from kojira/zen-tts-models. The model's terms ask projects that use it to publish their source under an OSI license, which is why this repository is public. ZEN-TTS and the model are not included here. bsky/make-bsky.sh regenerates the voices; set ZEN_TTS_DIR to a folder that holds tts/, voices/ and radio.js.
License #
The code is under the MIT License. The sample voices follow the terms of the voice model.