How to read chrome://webrtc-internals
A field guide to chrome://webrtc-internals: how to open it, what every section shows, which metrics explain a bad call, and how to save a dump file.
Posted by
Related reading
What WebRTC observability actually requires
WebRTC observability gets used as a synonym for dashboards and for getStats() access. Here is the requirements list: the six things a system has to capture before it can tell you why a call went bad.
Voice AI evals stop at the network - just when it became more important
Voice AI evals score the STT-LLM-TTS pipeline. Two transport failures leave no mark in a transcript, and full duplex removed the slack that hid them.
Your session data shouldn't leave your VPC by default
rtcStats collects WebRTC session data inside your infrastructure with rtcstats-server. Forwarding to rtcstats.com is off by default. You choose what leaves your VPC.

A call went bad and someone told you to open chrome://webrtc-internals.
You typed it into the address bar, got a page full of collapsible sections and a few hundred jittering line graphs, and closed the tab again. That reaction is fair. It ships with no labels, no documentation, and no opinion about which of its roughly 200 stats per second actually matter. So here is what the tool is, how to open it before you need it, what each section is showing you, which handful of metrics explain most bad calls, and how to save a dump file you can hand to somebody else. If you are debugging your own browser tab right now, webrtc-internals is all you need, and the rest of this page is how to get the most out of it before you go looking for anything else.
What is chrome://webrtc-internals
webrtc-internals is a diagnostic page built into Chrome and Edge. It records the WebRTC API calls your pages make from the moment you open it, polls getStats() on every active RTCPeerConnection about once per second, and draws the result as event logs and live graphs. Nothing is installed and nothing is sent anywhere. The page reads the WebRTC engine inside the browser you are already using, and everything it collects stays in that tab. It is the same data your own application can read through getStats(), without the work of writing the collection code. Because it is wired into the engine rather than into your app, it reports what the browser actually did and not what your application believes it did. That is why it is still the first stop for a WebRTC bug.
How do you open webrtc-internals
The four Create sections on an idle chrome://webrtc-internals page in Chrome
Type chrome://webrtc-internals into a new tab in Chrome, or edge://webrtc-internals in Edge, and press enter. There is no menu item, no flag, and no extension to install. On an idle browser you get four collapsed sections and nothing else: Create a WebRTC-Internals dump, Create diagnostic audio recordings, Create diagnostic packet recordings, and Create DataChannel message recordings. That is the correct result, and it means no tab in this browser profile has an active WebRTC session yet. The one rule that catches everybody is that webrtc-internals only records from the moment you open it. It does not reach backwards. If the call has already ended, there is nothing to see and no way to recover it. So the order is always the same: open webrtc-internals first, reproduce the problem, then read. Keep the tab open for the whole call, in the same browser profile.
What does webrtc-internals show during a call
Once a session starts, the page grows one collapsible section per RTCPeerConnection, labeled with the origin of the page that created it and the rtcConfiguration it was constructed with, ICE servers included. The page also carries a GetUserMedia Requests section listing every camera and microphone request the page made and the exact constraints it asked for, which is the first place to look when the wrong device got picked. Inside a peer connection section you get two things. The event log is a timestamped table of API calls and state changes in order: createOffer, setLocalDescription, addIceCandidate, iceconnectionstatechange, signalingstatechange, and the rest. Below it are the stats graphs. The event log answers what your application did. The graphs answer what the network and the media did about it. Most debugging is correlating a moment in one against the other.
How do you read the webrtc-internals stats graphs
The graphs are grouped by getStats() report, and the group name tells you which direction you are looking at. inbound-rtp is media arriving at this browser, outbound-rtp is media leaving it, remote-inbound-rtp is what the far end reports about what it received from you, and candidate-pair is the network path the two ends settled on. Names in square brackets, like [bytesReceived_in_bits/s], are rates that Chrome derives from the raw counters, and those are usually the ones you want: a counter that only ever climbs tells you much less than the rate at which it climbs. Read the shape before you read the value. A flat line that steps down, a sawtooth, or a gap in the series is a moment worth taking back to the event log timestamps above it, because a graph on its own tells you what happened but never why.
Which webrtc-internals metrics explain a bad call
Most WebRTC complaints come down to one of five signals, and once you know where they live you can check all five in about a minute. packetsLost and the loss rate derived from it tell you whether the network dropped media. jitter, and the rate of change in jitterBufferDelay rather than that counter's raw total, tell you whether media arrived unevenly, which is what a listener hears as choppy audio. framesPerSecond with frameWidth and frameHeight on inbound-rtp shows video quality collapsing, and freezeCount with totalFreezesDuration puts a number on the freezes the user actually saw. qualityLimitationReason on outbound-rtp says in one word why your encoder backed off: bandwidth, cpu, other, or none. And currentRoundTripTime with availableOutgoingBitrate on the selected candidate-pair tells you what that network path was capable of at the time. Check them in that order and stop at the first one that moved.
One caveat on vantage point. You are reading one side of the call, so the sending-side stats you see belong to this browser, and the far end's encoder decisions are only visible in the far end's own webrtc-internals.
| What the user reported | Where to look | What it means |
|---|---|---|
| Choppy or robotic audio | jitter and jitterBufferDelay on inbound-rtp | Media arrived unevenly and the jitter buffer stretched to absorb it |
| Video froze | freezeCount, totalFreezesDuration, framesPerSecond on inbound-rtp | Decoder starved, usually downstream of loss or a bandwidth drop |
| Their video went blurry | frameWidth, frameHeight on inbound-rtp, then ask the sender for qualityLimitationReason | The far end's encoder traded resolution for something: bandwidth or CPU |
| My video went blurry | qualityLimitationReason on outbound-rtp | Your own encoder backed off. The reason is the answer |
| Call never connected | Event log iceconnectionstatechange, plus candidate-pair | ICE never selected a working pair. Read the candidates, not the media |
| Everything was late | currentRoundTripTime on the selected candidate-pair | Path latency. Check which candidate pair actually won |
How do you save a webrtc-internals dump file
At the top of the page, expand Create a WebRTC-Internals dump and click the download button for the PeerConnection updates and stats data. Chrome writes everything the page has collected to a JSON file. That file holds the event log and the full stats history for every peer connection in the session, plus the browser's user agent string, which is what makes it the standard artifact to attach to a bug report. The sibling sections capture diagnostic audio, packets, and DataChannel messages, which are separate and much heavier recordings for narrower problems. Two things to know before you rely on the dump. It only contains what the tab already captured, so the open-it-first rule applies here as well. And it is raw JSON for a real call, not something you read in a text editor.
If getting that file out of a user is the hard part, you can capture it in the browser instead, and strip identifying data out of a dump before it leaves your organization.
Does webrtc-internals work in Firefox or Safari
No, and this catches people who support more than one browser. webrtc-internals is a Chromium feature, so you get it in Chrome and Edge and nowhere else. Firefox has about:webrtc, which covers similar ground with a different layout, a different set of controls, and a different file format, so a Firefox capture is not a webrtc-internals dump and tooling built for one will not read the other. Safari has no equivalent page at all: you turn on WebRTC logging from the Develop menu and read it in the Web Inspector console. In practice this means a bug that only reproduces on Safari is the hardest one to get data for, and it is a good argument for collecting getStats() from your own application code rather than depending on whichever tool the user's browser happens to ship.
What webrtc-internals cannot do
Everything above happens in one tab, on one machine, for one session, and that is the boundary of the tool. It cannot capture a session it was not open for, so the calls you most want to see, the ones a user complained about two hours ago, are already gone. You cannot deploy it or collect from it, so every production incident starts by asking a person to reproduce a bug on demand. It holds a bounded amount of history, which means a long session starts forgetting its own beginning, and closing the tab discards the rest. There is no search, no comparison across sessions, no way to share a live view with a colleague, and no opinion at all about which of those numbers mattered. You have to know, and you have to be there while it happens.
What can you use instead of webrtc-internals
When the answer you need is about a call that already happened, in somebody else's browser, you need the session captured before the complaint rather than after it. That is what rtcStats does. rtcstats-js is an open source client SDK you add to your application once, and it captures the same getStats() data that webrtc-internals captures, from every session you choose to instrument, sending it to rtcstats-server running inside your own infrastructure. rtcstats-server then forwards what you choose to rtcstats.com for analysis, which gives you Observations for the moments that mattered, Deductions for what those moments meant for the user, an Experience Score, and an AI Summary in plain English. The collection layer runs in your infrastructure and you control what gets sent, which is the part webrtc-internals never asked you to think about because it never left the tab.
You do not have to integrate anything to try it. Upload a webrtc-internals dump file you already have and read it there instead.
Two next steps from here, depending on what you came for. For a feature-by-feature view of where the browser tool ends and rtcStats picks up, read the rtcStats vs webrtc-internals comparison. If you already have a dump file on your desk and you want the answer rather than the haystack, read how to debug a webrtc-internals dump with AI. Background reading on the tool itself lives in the webrtc-internals glossary entry.
Stop scrolling through graphs. Start free at https://rtcstats.com