Problem
Desktop encoders are operational debt
Traditional internet radio expects operators to run BUTT, Nicecast, or OBS alongside a stream URL and mount point. That is fine for engineers — fragile for church volunteers on unfamiliar laptops.
The product bet: move encoder complexity to a server-side bridge the operator never sees. They open a web page, pick microphone or tab audio, paste a token, click start. Video streaming in this same monorepo deliberately avoided a bridge; audio accepts it because the UX win is larger.
My role and product bet
Move complexity away from the operator, not out of the system
I owned the operator experience, listener surface, streaming-origin architecture, WebSocket bridge, Icecast/nginx provisioning scripts, health APIs, and the runbook. The technical decision was not “browser audio is cool.” The product decision was that the person starting a devotional broadcast should not debug mount points, source passwords, encoder formats, or local audio routing.
This is where the audio case study deliberately differs from the video case study. For video, the simplest operator path was OBS directly into nginx-rtmp, so I removed the bridge. For audio, requiring BUTT or OBS would create unnecessary friction, so I accepted a small server-side bridge. Same philosophy, opposite architectural choice: put complexity where the user's mental model is least harmed.
Operator goal
Open a browser, choose microphone or tab audio, paste a token, watch the meter, and start broadcast.
System goal
Normalize inconsistent browser audio containers into MP3 128k stereo for the broadest listener compatibility.
Security goal
Fail closed by default, keep Icecast off the public internet, and prevent one operator from accidentally displacing another.
Truth goal
Show Sem sinal when no source is connected. This tool is live-ingest infrastructure, not an automated playlist pretending to be 24/7 programming.
Architecture
Browser → bridge → Icecast → listeners
FIG 1 — Browser sends WebM chunks; the VM normalizes to MP3 128k. Operators never install BUTT.
Token auth fails closed. Second connection returns 409. Icecast binds localhost only.
Operator browser
MediaRecorder (WebM/Opus chunks every 1s)
→ wss://stream…/broadcast?token=…
→ nginx TLS (access_log off on ingest — token in query)
→ Node bridge :8090
→ ffmpeg stdin → MP3 128k → icecast://127.0.0.1:8000/live
→ https://stream…/live (public listeners)
Vercel app (audio.radioalvoradatv.com.br)
/api/now-playing → Icecast status-json.xsl
/api/health → ok when source connectedTechnical deep dive
Operator path
MediaRecorder MIME negotiation
Tries audio/webm;codecs=opus, then webm, ogg, mp4 — first supported wins. Chunks emit every 1000ms as ArrayBuffer over WebSocket. Tab/system audio uses getDisplayMedia (Chromium); Safari falls back to microphone-only with explicit UI copy.
Level meter
Web Audio AnalyserNode on the local preview stream — operators see signal before going live, reducing silent broadcasts.
Fail-closed token auth
If BROADCAST_TOKEN is unset on the VM, all connections reject — no accidental open ingest. Comparison uses timingSafeEqual. Second simultaneous connection gets HTTP 409 (one operator at a time).
Implementation detail
Why ffmpeg sits in the middle
Browser audio is not one format. Depending on the browser, MediaRecorder may output WebM/Opus, Ogg/Opus, or MP4. Icecast can serve many formats, but the safest listener target for ordinary HTML audio players is still MP3. The bridge therefore treats browser chunks as an ingest format, not a distribution format.
ffmpeg reads the WebSocket binary stream through stdin, rate-limits input with -re, converts to libmp3lame, and pushes to the Icecast source mount. In plain language: the browser sends whatever it can record reliably; the server turns it into the thing radios and browsers understand.
Security
Icecast never faces the public internet
Icecast binds 127.0.0.1:8000 only. nginx terminates TLS on 443 and proxies /live for listeners and /broadcast for ingest. Port 8000 is blocked in Oracle security lists. Admin UI returns 403 at nginx.
Bridge runs as unprivileged radio-bridge user with systemd sandboxing (ProtectSystem=strict, memory cap 256M). Long proxy timeouts (3600s) keep live sessions alive.
Reliability model
What happens when things go wrong
The bridge has a single active broadcast session by design. A second operator receives HTTP 409 instead of replacing the current program. If ffmpeg exits, the WebSocket closes with an encoder error so the UI can stop pretending audio is still going out. If the bridge process crashes, systemd restarts it.
The health model is intentionally simple: Icecast is the source of truth. If status-json.xslreports a source on /live, the API returns ok: true. If no source exists, the app is not “down”; it is off-air. That distinction matters because HTTP failure would describe infrastructure, while Sem sinal describes broadcast state.
Health & off-air
Honest live semantics

Off-air is honest — Sem sinal, not a fake loop. Infrastructure runs 24/7; audio exists only when someone broadcasts.
Same status-json.xsl semantics power /api/health on Vercel and the operator panel badge.
/api/health always returns HTTP 200 — the ok boolean reflects whether Icecast reports an active source on /live. Off-air UI shows Sem sinal, not a fake loop (unlike video's YouTube fallback — a deliberate product difference).
Infrastructure is 24/7; content is live-only. The VM and bridge run continuously, but audio exists only when someone is broadcasting. That honesty matches volunteer-operated radio.
Outcome
What shipped — and what it proves
This project proves I can design an operationally safer broadcast workflow and then implement the infrastructure needed to make that workflow real. The value is not the novelty of WebSockets; it is the way browser capture, ffmpeg, Icecast, nginx, Vercel APIs, and operator copy combine into one coherent experience.
If you're hiring for a remote Design Engineer, Frontend Developer, Full Stack Developer, or Product Engineer role, this is the kind of work I do: design the workflow, ship the UI, and own the operational details that keep it reliable.
- One-script VM bootstrap: Icecast + nginx + certbot + bridge systemd unit
- Operator panel with device picker, token field, meter, and public status poll (10s)
- Listener page with same now-playing API as operator health semantics
- Documented external-encoder path (BUTT/OBS) for advanced operators who prefer it
- Security posture: fail-closed token, localhost Icecast, nginx admin block, single-session ingest
Searchable expertise
Keywords this work should rank for
The work is intentionally described with the phrases hiring teams and founders actually search for: browser audio broadcasting, WebSocket audio streaming, Icecast operator UI, self-hosted radio infrastructure, ffmpeg audio bridge, live audio product design, Oracle Always Free streaming, and design engineering for broadcast tools.
