LevelChatLevelChatDocs
Live streaming

Guides

Live streaming

Publish to one viewer or fifty thousand with the same room.

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:

TypeScript
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 (broadcast alias) — 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. Use client.joinLive().
  • hybrid (Coming soon) — start as a meeting, promote to live mid-session without dropping the call. The shape we're targeting:
TypeScript
// 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 fresh live room. 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 countTransportLatency
1 – 100direct WebRTC SFU80 ms
100 – 5 000cascade SFU mesh250 ms
5 000 – 50 000edge fanout + LL-HLS1.5 s
50 000+LL-HLS / CMAF4 s

You don't choose this — the SFU does. You can pin a minimum transport via the room config:

TypeScript
config: {
  distribution_floor: 'webrtc';
} // never fall back beyond direct WebRTC

WHIP from OBS

OBS Studio 30+ ships native WHIP. Point it at:

text
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

TypeScript
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.
Live streaming — broadcast to 25,000 viewers — LevelChat Docs