Live streaming in LevelChat is a room with type: "live" (the legacy alias "broadcast" still
normalizes here at ingress for backwards-compat). The publisher experience is identical to a
meeting; the viewer side scales horizontally via cascade SFU and falls back to LL-HLS / CMAF at
the long tail.
Start a live broadcast
Mint a viewer or broadcaster token on your server, then call client.joinLive() — same SDK,
same token endpoint as a meeting, a live topology instead:
import { LevelChat } from '@levelchat/web';
const client = new LevelChat();
// Broadcaster — publishes camera/mic/screen. `role` is required.
const stream = await client.joinLive({ token: broadcasterToken, role: 'broadcaster' });
await stream.publishCamera();
// Viewer — subscribe-only, scales to tens of thousands.
const viewer = await client.joinLive({ token: viewerToken, role: 'viewer' });The viewer side scales horizontally — see the distribution tiers below.
When to use live vs. hybrid
live(broadcastalias) — fixed number of publishers (1–10) and a viewer count that can grow into the thousands. Viewers can't publish without a new broadcaster token. Useclient.joinLive().- hybrid (Coming soon) — start as a
meeting, promote tolivemid-session without dropping the call. The shape we're targeting:
// COMING SOON — not yet shipped. Ships behind the W9.4 live-call-migration milestone.
// Do NOT paste this into production today — `room.promote()` is not exported by
// `@levelchat/[email protected]` and will throw `TypeError: room.promote is not a function`.
await room.promote('live', {
capacity: 50_000,
region: 'eu',
distribution: 'av1-svc', // or "vp9-svc" for broader hardware support
});In-session promotion is not yet available. Re-homing a live room from a meeting topology to a 50k-viewer broadcast fanout without dropping participants ships behind the W9.4 milestone. The supported path today is: end the meeting, then have viewers
joinLive()against a freshliveroom. Talk to us at [email protected] if hybrid promotion is on your critical path — we'll share the milestone timeline.
Distribution tiers
LevelChat picks the right transport for each viewer based on count and latency target:
| Viewer count | Transport | Latency |
|---|---|---|
| 1 – 100 | direct WebRTC SFU | 80 ms |
| 100 – 5 000 | cascade SFU mesh | 250 ms |
| 5 000 – 50 000 | edge fanout + LL-HLS | 1.5 s |
| 50 000+ | LL-HLS / CMAF | 4 s |
You don't choose this — the SFU does. You can pin a minimum transport via the room config:
config: {
distribution_floor: 'webrtc';
} // never fall back beyond direct WebRTCWHIP from OBS
OBS Studio 30+ ships native WHIP. Point it at:
Server: https://api.levelchat.io/v1/whip/<roomId>
Bearer: <a publisher room JWT>…and you're publishing in seconds, no SDK required.
Recording while streaming
await room.record({
compose: 'speaker', // tracks | grid | speaker
format: { output: 'mp4_av1', alsoGenerateHls: true },
webhook: 'https://your-app.example/lc-rec',
});The recording runs out-of-band on a recorder-svc worker — it does not add latency to the
live distribution.
Quality + cost
- AV1-SVC publish gives you 30–40% better viewer-side bitrate at the same quality vs. VP9.
- LevelChat bills viewers as
viewer_hours, not participant-minutes — see Pricing for the breakdown.