Skip to content

IFM Demos

Interactive demonstrations of the IFM protocol — three independent web apps for the three roles in the mesh, plus developer-facing products.

The Three Web Apps

Each app is a separate package in its own directory with its own package.json and bun dev. Run them together or standalone — every app boots the IFM web core (the Rust protocol core compiled to WASM) and joins the SAME mesh from the browser: the entry is always the rendezvous manifest JSON, exactly like production. To run against a LAN/local mesh instead, open any app with ?relay=ws://host:port.

The apps import the built SDK package (@ifm/sdkpackages/sdk/dist, linked live into each app's node_modules). After changing the SDK, rebuild it once — the demos pick up the fresh build on their next page load (bun install in an app refreshes the link if it ever goes stale):

bash
cd packages/sdk && bun run build   # rebuild dist after SDK changes
AppRoleURLDirectory
StationBroadcaster / Originhttp://localhost:5173/packages/sdk/examples/station/
RelayMesh Operator (passive observer dashboard)http://localhost:5174/packages/sdk/examples/relay/
ListenerEnd Userhttp://localhost:5175/packages/sdk/examples/listener/

The flow is always: Station broadcasts → Relay(s) forward → Listener hears, on the same frequency. Relays are station-agnostic infrastructure: a relay does not care who connects, it only relays every frequency and tracks everything running through it. Any station can use any relay, several stations can share one relay, and a station can use several — stations select their relays from the shared relay pool in the settings modal (or connect peers straight to the origin when none are selected). The Relay Station app is a passive observer dashboard: it watches the same mesh the desktop apps run on — stations on the discovery plane, connected peers, per-frequency traffic, and the shared relay pool (relays are brought up by their operators, never by the dashboard).

cd packages/sdk/examples/relay     && bun install && bun dev   # dashboard :5174
cd packages/sdk/examples/station   && bun install && bun dev   # broadcaster :5173
cd packages/sdk/examples/listener  && bun install && bun dev   # end user    :5175

Connecting Across Computers

Every app and the hub listen on all interfaces (0.0.0.0), so sessions on different machines can talk to each other — no code changes:

  1. Find the broadcasting machine's LAN IP: ipconfig getifaddr en0 (macOS).
  2. Start the stack there (bun scripts/start.mjs at the repo root) and allow the macOS firewall prompt.
  3. On the other computer open http://<ip>:5175/ (listener) or http://<ip>:5173/ (station). The apps auto-connect to the hub through the same-origin vite proxy, so the mesh is shared instantly.

To point a listener at a specific hub + station from anywhere, the listener (and station) accept deep-link parameters:

http://<ip>:5175/?relay=ws://<ip>:8790&freq=music.lofi
  • relay — the hub to join: ws://host:port, host:port, or a bare host (defaults to port 8790). wss:// is chosen automatically on HTTPS pages.
  • freq — auto-tune the listener to a specific station frequency on load.

The listener also has a Connection card (sidebar) that shows the live relay status and the endpoint it is connected through, and copies a shareable listen link — just ?freq=…, since the connection is automatic: whoever opens it tunes in and the receiver resolves the closest hop itself — a close peer's hub first, then the nearest pool relay by round-trip time, then the station's origin directly, shown in the player (the ?relay= deep link above is only needed to point at a specific hub). The receiver is idle until you tune: it dials nothing on load — the Frequencies on air list is fetched from the directory hub over plain HTTP (GET /discover, no socket), so running public frequencies appear while the receiver stays disconnected. Tuning a frequency is what dials — the station's closest hop (a close peer's hub, the nearest pool relay, or the station's origin). ?freq= auto-tunes on load (a selected frequency, so dialing is correct).

Prefer the closest hop. A station is just a browser tab with no listening socket; audio + chat always flow through a hub — the closest one: the hub a close peer is on first, then the nearest pool relay, then the station's own hub. The hub also caches chat history and each frequency's voice init segment, so a listener that tunes in mid-stream can decode the audio and join a conversation already in progress.

Every frequency has a frequency number. The Station's setup card lets you enter a real FM number (e.g. 91.700) directly, or enter a name (e.g. music.lofi) and an available number is auto-assigned on creation. Names are optional labels on top of the number — listeners discover by name and tune by number.


Station — Broadcaster Studio

Purpose: Professional broadcasting interface for originating live content into the IFM mesh.

Location: packages/sdk/examples/station/

Features

  • Real microphone streaminggetUserMedia + MediaRecorder (Opus/WebM), streamed as live voice packets on the studio frequency
  • Studio setup — frequency Number/Name toggle (validated; a name auto-assigns a free frequency number — never reusing a number you already created), access type (public/protected with key), optional name
  • Your stations — every station you open is saved locally (right of “Go on air.”) and reopenable/removable anytime; reopening a name keeps its number forever
  • Reload-safe studio — a reload keeps you on the same page: the on-air studio is restored automatically (re-tunes + re-announces), and the setup form is remembered too
  • Relay network view — the settings modal lists the live relay pool (station-agnostic, frequency-agnostic infrastructure — the shared pool the mesh gossips; relays are brought up by operators, never by apps). No relay is ever bound to a frequency or station: the station transmits through whichever pool relay the listener resolves as closest (a close peer first, then the nearest relay by round-trip time, then the station's origin directly)
  • Live studio — on-air indicator, stop-audio control, real-time listener/peer view
  • Chat — real-time text chat with everyone tuned to the frequency
  • File transfer — send files to listeners on the frequency

Quick Start

bash
cd packages/sdk/examples/station
bun install
bun dev
# Opens at http://localhost:5173/

Architecture


Relay — Mesh Operator Dashboard

Purpose: A live operational dashboard for the IFM mesh (a passive observer — the relay pool itself is infrastructure run by operators).

Location: packages/sdk/examples/relay/

Features

  • Web-core observer — the page boots the IFM web core as a passive observer node and joins the SAME mesh the desktop apps run on (entry = rendezvous manifest, exactly like production)
  • Live Dashboard (React app, port 5174) — shows in real time:
    • Stations on the discovery plane
    • Connected peers
    • Active frequencies with traffic
    • The shared relay pool (read-only)
    • Packets + bytes observed, uptime
    • Live traffic feed (type, frequency, sender)

Quick Start

bash
cd packages/sdk/examples/relay
bun install
bun dev
# Dashboard at http://localhost:5174/

Architecture


Listener — End-User App

Purpose: Consumer-facing app for discovering, listening to, and chatting on IFM frequencies.

Location: packages/sdk/examples/listener/

Features

  • Frequencies on air — live list of on-air stations, served by the directory hub over plain HTTP (no socket — the receiver stays idle until you tune); tune any of them, or type one
  • Idle until tuned — nothing is dialed on load (no reconnecting spinner, no dead relay); tuning a frequency resolves the closest hop — a close peer's hub, then the nearest pool relay by round-trip time, then the station's origin directly — and it keeps running even when no relay or peer is in range; frequencies without a station are heard through the hub
  • Protected frequency access — grant an access key (freq + key) so protected frequencies resolve their station and hop when you tune them; the key is hashed, never sent in clear
  • Listen page — live Opus/WebM audio playback (MediaSource) with LIVE indicator, mute, "via peer / relay / origin" hop readout
  • Chat — real-time text chat with the station
  • Files — receive files sent by the station

Reliability details

  • Mid-stream join — tuning a frequency that is already live works: the station re-broadcasts its voice init segment periodically, so a new listener's MediaSource can decode the live clusters that follow. The player then catches up to the live edge (a real “radio join” — you hear from now on, not from the station's start).
  • Stop → go live — restarting the broadcast begins a brand-new WebM stream; the listener detects the new init segment and rebuilds its player, so audio keeps playing.
  • Autoplay policy — real browsers block audio.play() from non-gesture contexts. If sound is blocked, the app shows a “🔊 Sound blocked — click to enable” control and retries playback on any click/tap/keypress.
  • Connection resilience — the mesh is direct-first with relay assist: the app listens to the station (or a listener peer) directly, and a relay is dialed only when the direct signal degrades (measured in JS → the core's path scorer flips best_path to the relay). Browser↔browser traffic flows over the WebRTC overlay; browser↔native flows over WebSocket/WebTransport to the mesh entry.

Quick Start

bash
cd packages/sdk/examples/listener
bun install
bun dev
# Opens at http://localhost:5175/

Architecture


Developer-Facing Products (Moved Out of Demos)

These are not demos — they are shipping developer tools:

ProductTypeLocationPurpose
Node UITerminal/TUI (Rust + ratatui)crates/core/examples/node_ui.rsOperator terminal for native node
CLICommand-line (Rust, on ifm-core)crates/cli/ifm for node management, frequency ops, identity, relay pool/binding
SDKLibrary (Rust + WASM + TypeScript)crates/sdk/, packages/sdk/Core library for building IFM applications

Node UI

Terminal interface for node operators — mesh topology, frequencies, packets, health, logs.

bash
cargo run --example node_ui -p ifm-core

CLI

bash
# Node management
ifm node start --config /etc/ifm/node.toml
ifm node status
ifm node logs --follow

# Frequency operations
ifm freq list
ifm freq tune music.lofi
ifm freq broadcast --file track.opus

# Identity & keys
ifm identity generate
ifm identity export --pubkey

SDK

toml
# Rust
[dependencies]
ifm-sdk = { path = "../crates/sdk" }

# TypeScript (npm)
@ifm/sdk@latest
@ifm/opus-wasm@latest

Project Structure (Web Apps)

packages/sdk/examples/
├── station/              # Broadcaster studio (port 5173)
│   ├── src/
│   │   ├── StationApp.tsx   # Setup + Studio (mic, chat, files, peers)
│   │   ├── Header.tsx       # Brand, relay status chip, cross-app links
│   │   └── style.css
│   ├── vite.config.ts
│   └── dev.mjs              # vite only (the mesh joins from the browser)
├── relay/                # Mesh operator dashboard (port 5174)
│   ├── src/
│   │   ├── RelayApp.tsx     # Live observer dashboard
│   │   └── style.css
│   ├── vite.config.ts
│   └── dev.mjs
└── listener/             # End-user app (port 5175)
    ├── src/
    │   ├── ListenerApp.tsx  # Station finder + player + chat + files
    │   ├── Header.tsx
    │   └── style.css
    ├── vite.config.ts
    └── dev.mjs

All three apps import the one @ifm/sdk web core — there is no per-app copy of webCore.ts or the wasm glue anymore.


Contributing

The three web apps are independent products with separate repos/lifecycles planned. For now in monorepo:

  1. Station — Add features for broadcasters (multi-source, scheduling, monetization hooks)
  2. Relay — Add operational features (alerting rules, historical replay, capacity planning)
  3. Listener — Add consumer features (discovery algorithms, social, offline, accessibility)

When modifying core protocol (crates/core, crates/sdk):

  1. Update Station if it exposes new broadcaster capabilities
  2. Update Relay if it exposes new operator controls or metrics
  3. Update Listener if it enables new listener features
  4. Update CLI if it needs new management commands
  5. Update Node UI if it needs new tabs/data for operators

Released under the MIT License.