flim.sh

Broadcast your DAW to a URL. Icecast in, WebRTC out.  home

flim.sh is a glom.sh, but for live audio. Point your DAW's broadcast plugin at a flim.sh mount, share the link, and anyone who opens it hears your set live in the browser — no app, no install.

your DAW  --icecast (MP3/OGG)-->  flim.sh  --webrtc-->  listeners
                                   transcode              (just a
                                   to Opus                 browser)

How it works

Three stages, one per hop above.

1. Ingest — the Icecast source protocol

Broadcast tools (BUTT, Mixxx, DAW plugins) already know how to push a continuous MP3 or OGG stream to an Icecast mount point using HTTP Basic auth. flim.sh speaks that protocol, so any of them can connect to a mount named after your slug — e.g. /midnight-set — with the username source and your stream's source password.

2. Transcode — MP3/OGG to Opus

Browsers don't play Icecast streams over WebRTC directly, so flim.sh re-encodes your incoming audio to Opus (48 kHz, the codec WebRTC speaks) in real time with ffmpeg.

3. Egress — WebRTC via WHEP

Listeners connect with WHEP (WebRTC-HTTP Egress Protocol): the browser sends one SDP offer, the server returns one answer, and audio flows. A single shared Opus track is added to every listener's connection, so one encode fans out to everyone — and that track outlives any single source connection, so you can drop and reconnect your DAW without kicking listeners off. Latency is typically well under a second.

Quickstart

With flim.sh running (npm start — HTTP on 8000, ingest on 8001):

1. Claim a stream

Mints a slug, an api_key (keep it — that's how you manage the stream later), and source credentials for your DAW.

# with the CLI (saves your api_key locally)
node bin/flim.js create --title "Midnight set"

# ...or with curl
curl -X POST https://flim.sh/api/streams \
  -H 'content-type: application/json' \
  -d '{"title":"Midnight set"}'

2. Broadcast to it

Fastest test — a tone straight from the CLI:

node bin/flim.js stream <slug> --tone
# or a file:  node bin/flim.js stream <slug> --file set.mp3

For a real set, point your DAW or BUTT at the source credentials from step 1.

3. Listen

Open https://flim.sh/<slug> and hit Tune in. Share that URL with anyone. Run node bin/flim.js ls to see which streams are live and how many people are listening.

Concepts

TermWhat it is
slugPublic name of your stream, e.g. neon-drift-7f2a. Listen URL is https://flim.sh/<slug>; mount point is /<slug>.
api_keyOwner key, returned when you claim a stream. Pass as Authorization: Bearer <key> to list or delete. Reuse it to group streams under one account.
sourcePasswordIcecast source password your DAW/BUTT uses to push audio, with username source.
mount pointThe Icecast path your broadcaster connects to — always /<slug>.
WHEPThe WebRTC signalling standard listeners use. Handled by the player page; you never touch it.

Keep api_key and sourcePassword secret — anyone with your source password can broadcast to your slug.

Broadcast your DAW

Every method ends the same way: an Icecast source pushing MP3 (or OGG) to your flim.sh mount. You need these values from flim create (or the API response's ingest block):

FieldValue
Server / hostflim.sh
Port8001
Mount/<your-slug>
Usernamesource
Passwordyour sourcePassword
FormatMP3 or OGG, stereo

Secure your source password: if the server exposes a TLS ingest (port 8443 by default), enable your tool's SSL/TLS option and use that port so your password is encrypted in transit. The CLI uses flim stream <slug> --tls.

Some tools (Mixxx, VirtualDJ) speak Icecast natively — point them straight at flim.sh. Production DAWs (Ableton, Logic, FL Studio) don't broadcast on their own, so you route their audio through a virtual audio cable into BUTT, which does the streaming.

BUTT — the universal encoder (any DAW)

BUTT ("Broadcast Using This Tool") is a free encoder for macOS, Windows and Linux. It streams whatever audio input you give it — the reliable path for any DAW.

  1. Open BUTT, click Settings. On the Main tab, find Server and click ADD.
  2. Set Type to Icecast and fill in:
    Address            : flim.sh
    Port               : 8001
    Password           : <your sourcePassword>
    Icecast mountpoint : /<your-slug>
    Icecast user       : source
  3. On the Audio tab, pick your input device and set the codec to MP3 or OGG, stereo.
  4. Close Settings and click the broadcast (play) button — the status bar turns green when connected.
  5. Open https://flim.sh/<your-slug> and hit Tune in.

The username must be exactly source — a 401 almost always means a wrong password or username.

Mixxx — native Icecast (best for DJing)

Mixxx is free DJ software with Icecast broadcasting built in — no virtual cable needed.

  1. Open Preferences → Live Broadcasting.
  2. Set Type to Icecast 2 and fill in:
    Host     : flim.sh
    Port     : 8001
    Mount    : /<your-slug>
    Login    : source
    Password : <your sourcePassword>
  3. Set Format to MP3, Channels to Stereo, bitrate 192–256 kbps.
  4. Click OK, then enable broadcasting from the toolbar. Leave the public-directory options off.
  5. Open https://flim.sh/<your-slug> and hit Tune in.

Ableton / Logic / FL Studio — virtual cable → BUTT

These DAWs don't broadcast on their own. Send the master output into a virtual audio device, then have BUTT capture that device and stream it: DAW master → virtual cable → BUTT → flim.sh.

  1. Install a virtual audio device: BlackHole (macOS) or VB-Audio Cable (Windows).
  2. Set your DAW's master/main output to the virtual device (Ableton: Preferences → Audio → Output Device; Logic: Settings → Audio → Output Device; FL Studio: Settings → Audio → Device). On macOS, build a Multi-Output Device so you can still hear yourself.
  3. In BUTT, set the audio input device (Settings → Audio) to the same virtual device, then configure the server exactly as in the BUTT section above.
  4. Start broadcasting in BUTT, then open https://flim.sh/<your-slug>.

VirtualDJ — native broadcast

  1. Open the Broadcast tab and choose Icecast as the server.
  2. Enter host flim.sh, port 8001, mount /<your-slug>, user source, and your source password.
  3. Pick MP3 (stereo) and start broadcasting; open https://flim.sh/<your-slug> to listen.

flim CLI — file, URL or tone

The bundled CLI speaks the Icecast source protocol itself, so it's both a test source and a way to broadcast a file without other software.

node bin/flim.js stream <slug> --tone
node bin/flim.js stream <slug> --file ~/sets/live.mp3
node bin/flim.js stream <slug> --url https://example.com/feed.mp3

If you ran flim create, the source password is remembered; otherwise pass --password.

CLI reference

CommandDoes
flim create [--title "…"]Claim a slug; prints listen URL + source creds and saves your api_key to ~/.config/flim/config.json.
flim lsList your streams with live status and listener count.
flim rm <slug>Stop and delete a stream.
flim stream <slug> [--tone|--file f|--url u]Broadcast over the Icecast source protocol.

Global flags: --server <url> (or FLIM_SERVER); --password, --host, --port for stream.

HTTP API

Provisioning is a plain REST API, so a DJ's own agent can claim a stream and hand the DAW its credentials with no clicking. Auth, when needed, is Authorization: Bearer <api_key>.

Method & pathAuthDoes
POST /api/streamsoptionalClaim a slug. Returns slug, apiKey, and an ingest block. Pass an existing key as Bearer to add to that account.
GET /api/streamsBearerList your streams with live + listeners.
DELETE /api/streams/:slugBearerStop and delete a stream.
POST /whep/:slugnoneWHEP: SDP offer in, SDP answer out.
DELETE /whep/:slug/:idnoneEnd a listener session.
GET /:slugnoneThe listener player page.
curl -X POST https://flim.sh/api/streams \
  -H 'content-type: application/json' \
  -d '{"title":"Midnight set"}'
# -> { "slug": "...", "apiKey": "...", "listenUrl": "...",
#      "ingest": { "host": "...", "port": 8001, "mount": "/...",
#                  "username": "source", "password": "..." } }

Self-hosting

flim.sh is a small Node app — no database (streams persist to a JSON file), no system ffmpeg (a static build is bundled).

npm install
npm start   # HTTP on 8000, Icecast ingest on 8001
Env varDefaultMeaning
FLIM_HTTP_PORT8000API + WHEP + player + docs
FLIM_INGEST_PORT8001Icecast source ingest
FLIM_PUBLIC_ORIGINe.g. https://flim.sh; else derived from the request
FLIM_OPUS_BITRATE128kEgress Opus bitrate
FLIM_ICE_SERVERSJSON array of STUN/TURN servers, injected into both the server and the player. Needed to reach listeners across the internet.
FLIM_ICE_TRANSPORT_POLICYallSet to relay to force media through TURN (recommended behind ngrok/home NAT).
FLIM_ANNOUNCED_IPThe VM's public IP, advertised as a WebRTC host candidate (required on 1:1-NAT clouds like Oracle/GCP/AWS).
FLIM_ICE_PORT_RANGEmin-max UDP range for WebRTC media, so it can be firewalled (e.g. 40000-40100).
FLIM_HTTP_HOST0.0.0.0Bind address for HTTP. Set 127.0.0.1 when a TLS proxy (Caddy) fronts it.
FLIM_INGEST_PUBLIChost:port of a separately-tunnelled ingest (e.g. an ngrok TCP endpoint), reported to remote DJs instead of the local address.

Deploying for real? Run it on a public-IP VM — listeners then connect directly over UDP and you can skip TURN entirely. There's a full walkthrough (systemd + Caddy TLS + firewall ports) in deploy/README.md, with a one-shot deploy/setup.sh.

Testing over the internet (ngrok + TURN)

ngrok tunnels TCP/HTTP but not WebRTC's UDP media, so it can't carry the audio by itself — you'll get "Live now" and silence. The fix is a free TURN server to relay the media (both sides send outbound UDP to it, which NAT allows). ngrok then only carries the player page and WHEP signalling.

  1. Start flim with a STUN server so listeners can hole-punch directly (works on most home networks, though not symmetric NAT):
    export FLIM_ICE_SERVERS='[{"urls":"stun:stun.l.google.com:19302"}]'
    npm start   # or ./scripts/serve-public.sh
    For guaranteed connectivity on any network, add a real TURN server and force relay. Get a free key from a provider like Metered (the old public Open Relay endpoints are dead), then:
    export FLIM_ICE_SERVERS='[
      {"urls":"stun:stun.l.google.com:19302"},
      {"urls":"turn:YOUR.relay.metered.ca:443","username":"KEY","credential":"SECRET"}
    ]'
    export FLIM_ICE_TRANSPORT_POLICY=relay   # only with WORKING turn, else WHEP hangs
    npm start
  2. Expose the HTTP port: ngrok http 8000 → gives an https://…ngrok…app URL.
  3. Broadcast (simulate a DJ locally, or expose ngrok tcp 8001 for a remote DJ):
    node bin/flim.js create --title "Test"
    node bin/flim.js stream <slug> --file set.mp3
  4. Send a friend https://…ngrok…app/<slug>. On ngrok's free tier they'll click through a one-time warning page, then hit Tune in.

For a cleaner setup without TURN, run flim on a cheap/free cloud VM with a public IP and set FLIM_ANNOUNCED_IP to that IP (open the WebRTC UDP ports in the firewall).

Troubleshooting

Broadcaster won't connect (401 / 404)

401 — wrong password, or username isn't exactly source. 404 — the mount must be /<your-slug>, slash included. Also check you're pointing at the ingest port (8001), not the HTTP port.

Page says "Live now" but I hear nothing

Make sure the DAW is actually feeding the device BUTT captures (watch BUTT's level meter), and that the browser tab isn't muted.

Listeners on other networks can't connect (or hear silence)

flim's ICE candidates are private when it runs behind NAT, so remote listeners can't reach the media directly. Configure a TURN server via FLIM_ICE_SERVERS — see Testing over the internet.

Can I reconnect my DAW without dropping listeners?

Yes — the shared broadcast track persists across source reconnects.