Cutting Live Broadcast Latency From 7 Seconds to 2 With Fishjam
by Maciej Rys • Jul 20, 2026 • 7 min read
ChatBCC is an invite-only community app. Actors, reality stars, and athletes host private group chats and talk directly with their fans. It was built around chat and it did that well. The hosts kept asking for one thing it didn’t do: a way to go live together, talk to their community in real time, react to an episode the moment it drops.
To make sure they were choosing the right architecture for their needs, the ChatBCC team wanted to compare the Fishjam stack with a leading alternative before implementation. Together, we evaluated different approaches to find the best fit for their product.
After thorough benchmarking, Fishjam proved to be a better-suited option. Using it, end-to-end latency went down from over 7 seconds in the first builds to around 2, and the cost came out roughly 40% lower than the alternatives we measured. Most of that traces to one decision: doing ingest, composition, and broadcast using a single platform rather than stitching together an SFU, a separate compositor, and a third-party delivery service.
Comparing two approaches, then refining the architecture
Together, we decided to build and compare two candidate architectures under real conditions: LiveKit + MUX and Fishjam + MUX. Both used the same shape: WebRTC between the hosts running the session, and LL-HLS through MUX to fan the stream out to viewers.
Two weeks later both were running. We measured the glass-to-glass latency between the host and the viewers.. Both came in around 7 seconds.
The tie was the useful part. Host-to-host was near real time over WebRTC, so the 7 seconds wasn’t coming from the media stack. It was the LL-HLS delivery path on the viewer side, and most of that latency is buffering by design. LL-HLS chops the stream into partial segments, the packager has to produce them, and the player holds several before it starts playing so playback stays smooth. That buffer is the whole point of HLS, and it’s roughly the same size whoever sits upstream of it.
That pointed at the fix. The delivery path was the bottleneck, not the media stack, and removing it was something we could actually do, because Fishjam can carry the broadcast over WebRTC end to end with no HLS layer at all. We recommended dropping MUX and delivering to viewers over WebRTC the whole way, host-to-viewer. Each frame goes out as it’s encoded, with only a small jitter buffer and no segment buffer to wait on, which brought the end-to-end latency to around 2 seconds.
At this point you might ask: why not just drop MUX and run the whole thing on LiveKit?
While you can stitch together a functional pipeline that composites and re-publishes the stream over WebRTC, that’s a bunch of software you’d have to build and maintain yourself. In Fishjam it’s all native — the client doesn’t have to own any code. And there’s the question of CPU vs GPU performance that we are going to dive deeper into in a moment.
What’s inside the cost
For a reference session (60 minutes, 3 hosts, 500 viewers, 720p at 2 Mb/s):
- Fishjam end to end: ~$20
- Fishjam + MUX: ~$33.50
- LiveKit + MUX: ~$34
The Fishjam-only path is the cheapest because it drops the third-party delivery layer entirely. The gap between Fishjam alone and Fishjam plus MUX is essentially the cost of routing delivery through MUX.
One note on scale. These cost figures are for a session of a few hundred viewers, which is where ChatBCC runs today. The broadcaster tier scales horizontally to a few thousand by adding nodes.
Compositing on the GPU
Turning three or four host cameras into one broadcast stream means compositing them: laying the feeds out, drawing speaker outlines, names, mic and camera state, mixing the audio. You can do that on each viewer’s device, but then the weakest phone in the audience sets the ceiling on the layout, and every device is decoding several streams on a battery. So the composition runs server-side, and each viewer receives one finished stream.
Fishjam does that with Smelter, our compositor: highly optimized Rust software for mixing video in real time on the GPU, with its own rendering. Fishjam runs it as managed infrastructure. A common way to composite server-side is to run the layout in a headless browser, because browsers are good at layout. The browser plays the incoming streams, renders them into a page, and that page is captured frame by frame and encoded. Capturing what the browser renders means reading it back from the GPU, so the frames get copied between GPU and CPU before encoding. Smelter is built to keep the whole thing on the GPU: it renders the layout and overlays with wgpu, and it can decode and encode through Vulkan Video, so the raw frames don’t have to round-trip to the CPU between decode, composite, and encode.
Two things follow from that here. The copies you don’t make are latency you don’t spend, which is part of how a server-side composite still fits inside a 2-second budget. And a GPU compositor packs far more concurrent sessions onto a machine than a fleet of headless browsers does, which is part of the cost gap.
For the ChatBCC team this meant configuring the layouts in backend code and letting Fishjam run the rest. No compositor to operate, no media backend to maintain.
Reaching the viewers
The hard part of broadcasting to a crowd over WebRTC is fan-out, and it’s a different problem from the conference between the hosts. So Fishjam keeps the two separate. The hosts run as a normal video conference: each publishes to a room over the SFU, so they see and hear each other in real time.Fishjam then publishes those host tracks to Smelter over WHIP, composites them into the single broadcast stream described above, and publishes it back to Fishjam over WHIP again. From there it goes out to viewers, over WHEP, served by a tier of broadcaster nodes that scale up as the audience grows. Viewers are subscribe-only, so the node count tracks the viewer count, and the SFU running the host conference never has to fan out to the whole crowd.
It’s WHIP and WHEP the whole way, including Smelter’s link to Fishjam. Those are standard protocols for publishing and receiving WebRTC streams, so none of this is a proprietary black box: a viewer could use any WHEP player, and a stream could be published into the same path from something like OBS. The same composite also produces the session recording, so there’s no second pipeline to capture it.
The conference, the composite, and the fan-out are one system we build and run, rather than an SFU, a compositor, and a delivery service stitched together from three vendors.
Results
- ~2s end-to-end broadcast latency on Fishjam, down from over 7s
- ~40% lower broadcast cost per reference session
- ~40,000 minutes streamed over the past 60 days
- 3 comparative proofs of concept in 2 weeks, so the architecture decision was made on measured numbers
ChatBCC successfully expanded from chat to integrated live video while keeping operational complexity low, shipping without having to build and operate their own media backend.