WebRTC getStats: Monitor Connection Health in JS

WebRTC statistics provide detailed, real-time telemetry on the state, performance, and media quality of peer-to-peer connections. By utilizing the browser’s built-in RTCPeerConnection.prototype.getStats() method in JavaScript, developers can programmatically inspect critical network metrics such as packet loss, round-trip time (RTT), jitter, and bitrate. This guide explores the architecture of WebRTC statistics, identifies the key metrics to track, and demonstrates how to implement automated connection health monitoring in your web applications.


What Are WebRTC Statistics?

WebRTC statistics represent an extensive collection of metrics gathered at the browser level across various layers of the real-time communication pipeline. These metrics encompass network transport information, ICE (Interactive Connectivity Establishment) candidate negotiation, RTP (Real-time Transport Protocol) audio and video streams, and device-level hardware performance (such as encoding and decoding pipelines).

When queried, the browser returns an RTCStatsReport object. This report is a map-like collection of individual RTCStats dictionaries, each categorized by a specific type (such as inbound-rtp, outbound-rtp, candidate-pair, or media-source) and identified by a unique id.


The getStats() Method

The getStats() API is an asynchronous method available on the RTCPeerConnection interface. It queries the underlying WebRTC engine and resolves a Promise containing the RTCStatsReport.

peerConnection.getStats(selector)
  .then(statsReport => {
    statsReport.forEach(report => {
      // Process individual metric objects
    });
  })
  .catch(error => {
    console.error('Error fetching WebRTC stats:', error);
  });

The optional selector parameter allows you to pass a specific MediaStreamTrack to filter the output down to metrics associated with that track. Passing no argument returns the complete set of statistics for the entire peer connection.


Key Health Metrics to Monitor

To diagnose network degradation and maintain high call quality, focus on the following primary metric types and fields:

1. Network Latency (Round-Trip Time)

2. Packet Loss

3. Jitter

4. Throughput and Bitrate

5. Video Performance


Implementing Health Monitoring in JavaScript

Because getStats() returns cumulative values (totals since the connection started), monitoring requires polling at periodic intervals (e.g., every 1 to 2 seconds) and calculating the delta between readings.

class WebRTCConnectionMonitor {
  constructor(peerConnection, intervalMs = 2000) {
    this.pc = peerConnection;
    this.intervalMs = intervalMs;
    this.timerId = null;
    this.previousStats = new Map();
  }

  start() {
    this.timerId = setInterval(() => this.collectMetrics(), this.intervalMs);
  }

  stop() {
    if (this.timerId) {
      clearInterval(this.timerId);
      this.timerId = null;
    }
  }

  async collectMetrics() {
    try {
      const statsReport = await this.pc.getStats();

      statsReport.forEach(report => {
        if (report.type === 'candidate-pair' && report.state === 'succeeded' && report.nominated) {
          const rtt = report.currentRoundTripTime ? (report.currentRoundTripTime * 1000).toFixed(2) : 'N/A';
          console.log(`[Network] Current RTT: ${rtt} ms`);
        }

        if (report.type === 'inbound-rtp' && report.kind === 'video') {
          const prev = this.previousStats.get(report.id);

          if (prev) {
            const timeDiff = (report.timestamp - prev.timestamp) / 1000;
            const bytesDiff = report.bytesReceived - prev.bytesReceived;
            const bitrateKbps = ((bytesDiff * 8) / (timeDiff * 1000)).toFixed(2);

            const lostDiff = report.packetsLost - prev.packetsLost;
            const totalPacketsDiff = (report.packetsReceived - prev.packetsReceived) + lostDiff;
            const lossRate = totalPacketsDiff > 0 ? ((lostDiff / totalPacketsDiff) * 100).toFixed(2) : 0;

            console.log(`[Video Inbound] Bitrate: ${bitrateKbps} kbps | Packet Loss: ${lossRate}% | Jitter: ${(report.jitter * 1000).toFixed(2)} ms`);
          }

          this.previousStats.set(report.id, report);
        }
      });
    } catch (err) {
      console.error('Failed to collect WebRTC metrics:', err);
    }
  }
}

// Usage:
// const monitor = new WebRTCConnectionMonitor(peerConnection);
// monitor.start();

Best Practices for Connection Health Monitoring