Live Radio Streaming
Embed any ShoutCast or Icecast station on your site with a single iframe, or broadcast live from your browser — mic, system audio, or both. No relay scripts, no MediaSource, no external software needed.
Running Drops. This tutorial uses Drops, a compact language that replaces curl commands. Run them via POST /exec or paste into the live editor.
How it works: ShoutCast and Icecast streams are plain HTTP audio. An <audio> element plays them directly — the browser handles all buffering natively. InfiniteOcean adds an optional metadata layer for track titles via SSE, plus a browser-based broadcast mode that uses IO blobs for audio delivery.
DJ's ShoutCast/Icecast stream URL
|
v
<audio src="stream-url"> browser plays natively
+
IO metadata (optional)
|- fn/radio-meta reads ICY metadata, writes now-playing drops
'- GET /stream/* SSE delivers track titles to player
Browser broadcast (no stream server needed)
|
v
MediaRecorder (2s chunks) → POST /blob → segment drops → SSE → AudioContext playback
1 Get Your Stream URL
You need a ShoutCast or Icecast stream URL. This is the direct URL to the audio stream — the same one you'd paste into VLC or Winamp. Examples:
https://ice1.somafm.com/groovesalad-128-mp3— SomaFM Groove Saladhttps://your-server:8000/live— your own Icecast mounthttps://stream.example.com:8080/stream— any ShoutCast server
If you're DJing with Virtual DJ, BUTT, or Mixxx, they can broadcast to a ShoutCast/Icecast server. The server gives you a stream URL your listeners connect to.
HTTPS required. The stream URL must be https://. Browsers block HTTP audio on HTTPS pages (mixed content). If your ShoutCast/Icecast server only supports HTTP, put a reverse proxy like Caddy in front of it to add TLS — or skip the stream server entirely and broadcast from your browser (step 5).
Don't have a stream server? You can broadcast directly from your browser (step 5) — no Icecast or ShoutCast server needed. Or use free options: ShoutCast (DNAS), Icecast (open source), or hosted services like Radio.co. For testing, use any SomaFM URL above.
2 Embed the Player
One line of HTML. Paste your stream URL as the src parameter:
<iframe
src="https://view.infiniteocean.io/radio-player/?src=https://ice1.somafm.com/groovesalad-128-mp3"
width="340" height="180"
frameborder="0"
allow="autoplay"
></iframe>
That's it. Click play and audio starts instantly.
The player uses a native <audio> element. The browser handles buffering, reconnection, and codec decoding automatically.
Player parameters:
src— Stream URL. Use this for Icecast/ShoutCast streams.route— IO route for live metadata or browser broadcast playback.name— Station name to display (defaults to hostname from URL).key— Your Ocean Key (needed if route is private).api— API base URL (defaults tohttps://api.infiniteocean.io).
Two playback modes: When src is set, the player uses a native <audio> element for Icecast/ShoutCast. When only route is set (no src), it switches to IO blob playback mode for browser broadcasts — fetching audio chunks via SSE and playing them with AudioContext.
3 Add Track Titles (optional)
The player works great without this step. But if you want live "Now Playing" titles, InfiniteOcean can read them from your stream's ICY metadata and push them to the player in real-time.
Start the metadata monitor:
-- Start metadata tracking (runs for 5 minutes)
run fn/radio-meta { source: "https://ice1.somafm.com/groovesalad-128-mp3", route: "radio/live", duration: 300 } async
Returns: {"job_id": "abc123"} — the metadata monitor runs in the background, writing now-playing and status drops to your route.
Now update your embed to include the route:
<iframe
src="https://view.infiniteocean.io/radio-player/?src=https://your-stream:8000/live&route=radio/live&key=YOUR_KEY"
width="340" height="180"
frameborder="0"
allow="autoplay"
></iframe>
The player subscribes to SSE on that route and updates the track title in real-time.
For continuous 24/7 metadata tracking, schedule it with cron:
-- Restart metadata monitor every 5 minutes
cron "*/5 * * * *" radio-meta: run fn/radio-meta { source: "https://your-stream:8000/live", route: "radio/live", duration: 300 }
4 Verify It Works
Check that metadata is appearing:
-- Check station status
read radio/live @status
-- Check now-playing
read radio/live @now-playing
HTTP only. SSE streams use a persistent HTTP connection — use curl -N or EventSource in JavaScript.
# Watch live metadata via SSE
curl -N "https://api.infiniteocean.io/stream/radio/live?_key=YOUR_KEY"
Status: {"live": true, "bitrate": 128, "station": "Groove Salad"}
Now playing: {"title": "Tycho - A Walk", "timestamp": 1709438400}
5 Broadcast from Your Browser
Don't have a ShoutCast or Icecast server? You can broadcast directly from your browser — just your microphone, system audio, or both. No external software needed.
Open the broadcast page:
https://view.infiniteocean.io/radio-broadcast/
Sign in with your email (magic link), and you're ready to go. A personal route is assigned automatically based on your account — or set a custom one in the route field.
The broadcast page lets you:
- Pick your source — microphone, system audio (desktop/tab audio), or both mixed together
- See a live level meter — confirms audio is being captured
- Set a "Now Playing" title — updates the player in real-time
- Monitor stats — segments sent, duration, codec, total data
- Copy the player link — share it with listeners or embed on your site
How it works: The browser captures audio with MediaRecorder (2-second standalone chunks, Opus codec at 64kbps). Each chunk is uploaded as an IO blob, then a segment drop is written to your route. The player picks up these segment drops via SSE, fetches the audio blobs, and decodes them with AudioContext for gapless playback.
Share the player link with your listeners — note there's no src parameter, just the route:
<iframe
src="https://view.infiniteocean.io/radio-player/?route=radio/my-show&name=My%20Station"
width="340" height="180"
frameborder="0"
allow="autoplay"
></iframe>
When only route is set (no src), the player automatically uses IO blob playback. Listeners hear your broadcast within ~4 seconds.
6 Customize
Ideas to extend your radio setup:
- Multiple stations — embed multiple players with different
srcURLs and routes (stations/jazz,stations/rock) - Listener counter — use atomic counters to track listeners:
{"route": "radio/stats", "key": "today", "payload": {"+listeners": 1}} - Track history — query past tracks with SQL:
SELECT payload FROM "radio/live" WHERE key = 'now-playing' ORDER BY created_at DESC LIMIT 20 - Custom player — the player widget is just HTML. Fork it by reading the entity, modifying, and writing to your own route.
- Access control — make your metadata route private with route-level ACLs
- Record streams — for archiving, use relay.py to capture audio as blobs
Cost: The player and SSE connections are completely free. Only the metadata function uses compute (a few io per run). Browser broadcasting costs 1 io per 100 KB of audio uploaded (roughly 4 io per minute at 64kbps).