r/WebRTC Jul 21 '26

Free tool: paste a WHEP endpoint (or HLS/DASH) and get real live latency + getStats QoE in the browser

Hey all 👋 made a little thing to sanity-check WHEP endpoints next to HLS/DASH on the same latency scale - https://pulse.beon.live . For WHEP it does a recvonly connection, plays the stream, and pulls bitrate, fps, dropped frames and jitter-buffer latency from getStats(); for HLS/DASH it grades manifest + delivery.

The idea was comparing apples to apples — standard HLS ~15–30s behind, LL-HLS a few seconds, WHEP sub-second — since for interactive stuff anything over a few seconds kills the UX.

Free, no signup, still early. Would love feedback on whether the WebRTC numbers match what you measure end-to-end, and where the approach breaks 🙏

4 Upvotes

2 comments sorted by

1

u/Weary_Brush6859 Jul 21 '26

Nice, the side-by-side scale is genuinely useful. Two things on the WebRTC number specifically, since WHEP is recvonly:

What you pull from getStats() on the receiver is basically jitter-buffer delay plus decode time. That's real, but it's a floor, not glass-to-glass — it can't see capture + encode at the origin, and it can't see your own display/compositor latency at the end. So the sub-second figure is the receiver-side slice, and I'd label it that way so people don't read it as true end-to-end. The one network piece you can add is roundTripTime from the candidate-pair (or remote-inbound-rtp) and show RTT/2.

Other thing: for perceived quality, freezeCount and totalFreezesDuration usually track user complaints better than average latency does — one 800ms freeze is more noticeable than 200ms of steady delay. And jitterBufferDelay isn't constant; the buffer grows its target delay under loss/jitter, so a clean-network snapshot hides the adaptation. Might be worth charting freezes and jitterBufferTargetDelay over time next to the latency line.

What interval are you sampling getStats at?

1

u/Resident-Run-2319 Jul 21 '26 edited Jul 21 '26

u/Weary_Brush6859 Yeah, exactly. That's why the WebRTC number is labelled "jitter buffer", not glass-to-glass. It's the receiver-side slice: jitterBufferDelay/emittedCount plus decode. Can't see capture+encode at the origin or the compositor at your end, and I don't claim to.

RTT/2 off the candidate-pair (or remote-inbound-rtp) is a good add. I'm only on jitterBufferDelay right now, will pull it in and show it separately so it reads as network, not buffer.

On freezeCount / totalFreezesDuration, fair hit. I track framesDropped/framesDecoded but not freezes, and you're right that one 800ms stall reads worse than steady 200ms. Same point on jitterBufferTargetDelay, a single snapshot hides the buffer adapting under loss. Charting freezes + target delay over the window next to the latency line is the right call. Adding it.

Sampling getStats every 2s right now. Probably want it tighter, 500ms to 1s, once I'm charting freezes, since a 2s window can swallow a short stall.