A user reports that the video went blurry for thirty seconds. You check the monitoring: no packet loss, no errors, connection healthy throughout.
Both observations are correct. The picture did degrade, and nothing was lost — because the degradation is what prevented the loss.
WebRTC does not wait for packets to die before reacting. It watches for the queue that is about to kill them, and gives up quality pre-emptively to avoid it. Understanding that inverts how you read every quality complaint you will ever receive.
The downgrade is the mechanism, not the failure
The instinct when quality drops is that something broke. In a real-time system the opposite is closer to true: the alternative to a downgrade is not better video, it is a call that stalls, stutters and eventually fails.
Video is elastic in a way that most network traffic is not. A file transfer has a fixed size and can only go faster or slower. A video stream can be made smaller — fewer pixels, fewer frames, coarser quantisation — and still be a video. That elasticity is what a congestion controller spends, and spending it is the correct behaviour.
This is one stage in a longer chain, and it is the stage users actually perceive. Everything else in the pipeline either works or does not; this one degrades visibly, on purpose, and gets reported as a bug for exactly that reason.
How WebRTC bandwidth estimation actually works
The algorithm in browsers is Google Congestion Control, specified in IETF draft-ietf-rmcat-gcc. It runs two controllers in parallel and takes the lower of their two estimates as the sending rate.
The delay-based controller
This is the interesting one, and the reason the system reacts early.
Packets are sent in groups. For each group, the controller compares how far apart packets were sent with how far apart they arrived. If a group arrives more spread out than it was sent, something in the path is holding them — a queue is filling.
The specification models this as d(i) = m(i) + v(i), where m(i) is the underlying delay trend and v(i) is ordinary jitter. A Kalman filter separates the two, because the whole difficulty is distinguishing a genuine trend from noise. When the filtered estimate exceeds an adaptive threshold, the detector signals over-use and the rate comes down.
That threshold starts at 12.5 ms and adapts within a range of 6 to 600 ms. The draft is explicit that too small a threshold makes the detector "overly sensitive" — so the algorithm deliberately tolerates some delay growth before acting, to avoid reacting to noise.
The loss-based controller
Simpler, and it operates on three bands:
The hold band explains something engineers notice and find counterintuitive: a call can sit at 8% packet loss and the sending rate will not move. That is deliberate. Some loss is normal on real networks and reacting to it would make the system jittery, so GCC treats the 2–10% range as noise and only retreats above it.
The final sending rate is the minimum of the two controllers. In practice the delay-based one usually wins, because queues fill before they overflow — which is the entire point.
Why reacting to delay beats reacting to loss
A network under pressure does not go straight from fine to dropping packets. It queues them first. Buffers in routers, switches and access equipment fill up, packets take longer to traverse, and only when those buffers are full does anything actually get discarded.
Loss-based congestion control waits for that final stage. Delay-based control watches the buffers filling and reacts while there is still time to do something about it gracefully.
The difference is the whole user experience. Reacting at loss means the user sees frozen frames, artefacts and stutter, because the damage has already happened. Reacting at delay means the user sees a resolution drop, which is mild and recoverable.
This is also why bandwidth estimation is mandatory rather than optional on real-time media. TCP's approach — push until something breaks, then halve — is fine for a download where a retransmission fixes everything. For live video a retransmitted frame from 300 ms ago has no value, so the only workable strategy is to never get into the situation in the first place.
Where the estimate is calculated: REMB and transport-cc
Two generations of mechanism, and you will encounter both.
The older approach, REMB, had the receiver calculate an estimate from what it observed and send that figure back to the sender as a maximum bitrate. It worked, but it put the logic on the receiving end, where different implementations reached different conclusions and where the sender could not see the raw data.
The current approach, transport-wide congestion control, inverts this. The receiver simply reports arrival times for every packet, and the sender does the estimation. That is strictly better: the sender has the departure timestamps already, so pairing them with the arrival feedback gives a complete picture, and improvements to the algorithm ship with the sending implementation rather than depending on what the far end is running.
Practically, this means bandwidth estimation is a sender-side concern in modern deployments. For a media server that is significant, because the server is a sender to every subscriber — it runs an independent estimate per subscriber, and decides per subscriber what to forward.
What a downgrade actually looks like
The estimate falls. Something has to give, and there are three levers, usually applied in this order.
Resolution drops first in most configurations, because pixel count is where bitrate lives. With simulcast the server does not even ask the publisher to change anything — it can simply drop a simulcast layer instead of the resolution, forwarding 360p where it was forwarding 720p, per subscriber and without affecting anyone else in the room.
Frame rate drops second, or first for screen content where sharpness matters more than motion. Temporal layers make this cheap, since dropping alternate frames needs no re-encode.
Quantisation coarsens last — the same resolution and frame rate with more compression artefacts. This is the least pleasant of the three and usually a sign the other two have been exhausted.
Worth knowing that latency moves at the same time. When a network becomes variable enough to trigger a downgrade, the jitter buffer deepens at the same time to absorb the variance — so the user is getting a smaller picture and a slightly later one, from the same underlying cause.
None of this is explained to the user by default, which is the actual product problem. The infrastructure behaves correctly and the interface says nothing, so the user concludes the application is broken. A single line of UI — "reduced quality, weak connection" — converts a support ticket into an understood event, and almost nobody ships it.
Where WebRTC bandwidth estimation gets it wrong
The algorithm is good and it is not infallible. Three situations where it misjudges, all of which produce real complaints.
It loses to TCP on a shared link
This is the big one and it is structural. A delay-based controller backs off at the first sign of queuing. TCP does not — it keeps increasing until packets actually drop, which is precisely the point a delay-based algorithm was designed to avoid reaching.
So when someone starts a large download on the same connection as their video call, the download keeps pushing while the call politely retreats. The video degrades, the download proceeds at full speed, and the user reasonably concludes the video application is the weak one. It is the opposite: the video is the only participant behaving well.
There is no clean fix inside WebRTC for this. It is a consequence of being a good citizen on a network where the other traffic is not.
It under-estimates on high-jitter networks
The delay-based controller has to separate a genuine trend from noise, and on mobile networks, where jitter is structural, there is a great deal of noise. Radio scheduling, handovers and carrier queuing produce delay variation that is not congestion, and a controller tuned to be responsive will read some of it as congestion and back off unnecessarily.
The adaptive threshold exists precisely to manage this tension, which is why it is permitted to grow as far as 600 ms. But the tradeoff is real: a controller tolerant enough for a mobile network is slower to react on a fixed one.
A relayed path changes the picture
When a relayed path adds a hop and changes the timing, the estimator is measuring a route it was not measuring before — different queuing behaviour, an extra buffer, often a longer path. The estimate has to reconverge, and during that period the rate is wrong in one direction or the other.
This is worth knowing because it partially explains why relayed sessions feel worse beyond the pure latency cost. Some of it is the extra hop; some of it is a control loop working with less reliable signal.
What to measure
Most teams monitor bitrate and packet loss, and both are poor early-warning signals — bitrate falling is the system working, and loss appears only after the delay-based controller has already failed to prevent it.
The metric with actual predictive value is the rate at which the server is issuing quality reductions. Layer-downgrade rate is the alert worth having, because it rises before users complain and falls when conditions improve, which is exactly the shape an early-warning metric should have.
Two more worth collecting: the distribution of estimated bandwidth across your user base, which tells you whether your default ladder matches reality, and the proportion of sessions that spend significant time below your lowest layer, which tells you whether your floor is set too high for the networks your users are actually on.
Build, Buy, or Deploy
Three routes, with the honest boundary on each.
Build on open source
mediasoup, Janus, Pion and LiveKit all implement transport-cc and expose per-consumer estimates, which is what makes any of this observable. Building means you can tune layer-switching policy — how eagerly to drop, how cautiously to recover, whether to prefer resolution or frame rate for your content type. Those defaults are generic and your content probably is not, so the tuning is real value. The cost is that congestion control is subtle and a well-intentioned change can make things worse in ways that only appear under load.
Buy a cloud video API
Per-minute providers handle estimation and layer selection entirely, and their defaults are usually well tuned because they see an enormous range of networks. The limits are that you generally cannot adjust the policy for unusual content, and you often cannot see per-subscriber estimates — which is the one number that would tell you why a specific user had a bad call.
Deploy a commercial platform
The middle path runs a commercial platform on infrastructure you choose. Samvyo is one such option: based on SFU architecture, shipping embeddable SDKs, with the media path, TURN and recording kept on infrastructure you control. Path length is the relevant lever here — a shorter, more predictable route between your users and the media server gives the estimator a cleaner signal to work with, and choosing where that server sits is choosing how well the control loop performs. It is resilient by design rather than something you assemble.
Where it does not fit: for two-party calls the browser handles all of this end to end and there is nothing for a platform to improve. And if you need to modify the congestion control algorithm itself — unusual, but it happens in specialised low-latency work — that requires building against the libraries directly.
The Bottom Line
WebRTC reduces quality before anything is lost because it watches queues filling rather than packets dying. A Kalman-filtered delay estimate crossing a 12.5 ms threshold is enough to trigger a downgrade, long before the loss-based controller's 10% threshold would. The blurry thirty seconds your user reported is the system succeeding.
Two things follow. Alert on quality-reduction rate rather than on bitrate or loss, because the first is predictive and the other two are lagging. And tell the user what is happening — the infrastructure is already doing the right thing, and a single line of interface is the difference between a support ticket and an understood event.
What's Next
Bandwidth estimation decides which layer gets forwarded; drop a simulcast layer instead of the resolution covers how those layers are produced and what they cost the publisher. For where this sits among the other seven stages, one stage in a longer chain walks the whole pipeline and what fails at each link.
Frequently Asked Questions
What is bandwidth estimation in WebRTC?
A continuous measurement of how much capacity is available on the path, used to set the sending rate. Browsers use Google Congestion Control, which runs two controllers in parallel — one watching delay trends, one watching packet loss — and takes the lower of their estimates. When capacity looks constrained it reduces quality rather than risking packet loss.
Why does my video call get blurry when the network looks fine?
Because the system is reacting to queuing delay, not packet loss. When packets start arriving more spread out than they were sent, a queue is filling somewhere in the path, and WebRTC reduces quality before anything is actually dropped. Monitoring that only tracks loss and errors will show nothing while this happens.
At what packet loss does WebRTC reduce quality?
Above 10%, per the loss-based controller, which then multiplies the estimate by (1 − 0.5p). Below 2% it probes upward by 5%, and between 2% and 10% it holds steady — so a call can sit at 8% loss with no rate change. In practice the delay-based controller usually acts well before any of this, since its threshold starts at 12.5 ms of excess delay.
Why does a file download ruin my video call?
Because they use opposite strategies. WebRTC backs off at the first sign of queuing; TCP keeps increasing until packets actually drop. On a shared link the download therefore wins, the video retreats, and the user blames the video application — which is the only one behaving correctly.
What is the difference between REMB and transport-cc?
Where the estimate is calculated. With REMB the receiver computed a maximum bitrate and reported it back. With transport-wide congestion control the receiver just reports per-packet arrival times and the sender does the estimation, which gives a complete picture since the sender already holds the departure timestamps. Transport-cc is the modern default.
Should I alert on bitrate drops?
No — a falling bitrate is the system working as designed, so alerting on it produces noise. Alert instead on the rate at which quality reductions are being issued, which rises before users complain and falls when conditions recover. Packet loss is also a lagging signal, since it only appears after the delay-based controller has failed to prevent it.
Can I stop WebRTC from reducing quality?
You can set a minimum bitrate, and you generally should not. Forcing a rate above what the path supports does not produce better video — it produces packet loss, frozen frames and eventually a failed connection. The better response is to make degradation visible in the interface so users understand what they are seeing.