Using navigator.connection.downlink to Measure Network Speed

An interactive front-end demo about reading the Network Information API, why bandwidth alone is unreliable, and how product experience can respond to real loading behavior.

1. Read Network Information

The browser may expose estimated bandwidth, latency, connection type, and data-saving preference through navigator.connection.

Code:
function readNetworkInformation() {
  const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;

  if (!connection) {
    return {
      supported: false,
      message: "Network Information API is not supported in this browser."
    };
  }

  return {
    supported: true,
    downlink: connection.downlink ?? "unknown",
    downlinkMax: connection.downlinkMax ?? "unknown",
    effectiveType: connection.effectiveType ?? "unknown",
    rtt: connection.rtt ?? "unknown",
    saveData: connection.saveData ?? false,
    type: connection.type ?? "unknown"
  };
}
Result:

2. Use Bandwidth as a Hint, Not a Truth

A high downlink value does not guarantee a fast image load. Server pressure, packet loss, throttling, and competing downloads still affect the real experience.

Code:
function chooseImageQuality(downlink, saveData) {
  if (saveData) {
    return "low";
  }

  if (downlink >= 5) {
    return "high";
  }

  return "low";
}
Result:

3. Listen for Connection Changes

When supported, the change event can update front-end behavior after the network condition changes.

Code:
function watchConnectionChanges(onChange) {
  const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;

  if (!connection) {
    onChange("Connection change events are not supported here.");
    return;
  }

  connection.addEventListener("change", () => {
    onChange("Network changed: " + connection.effectiveType + ", " + connection.downlink + " Mbps");
  });
}
Result:
No connection events recorded yet.

4. Measure Actual Resource Loading

For image-heavy feeds, measuring several resource loads is more useful than trusting one bandwidth estimate.

Code:
function measureResourceLoad(sizeKb, speedKbps, onProgress, onComplete) {
  const totalBytes = sizeKb * 1024;
  let loadedBytes = 0;
  const bytesPerTick = speedKbps * 1024 / 10;
  const startedAt = performance.now();

  const timer = setInterval(() => {
    loadedBytes = Math.min(totalBytes, loadedBytes + bytesPerTick);
    onProgress({
      loaded: loadedBytes,
      total: totalBytes,
      percent: Math.round(loadedBytes / totalBytes * 100)
    });

    if (loadedBytes === totalBytes) {
      clearInterval(timer);
      onComplete(Math.round(performance.now() - startedAt));
    }
  }, 100);
}
Result:
Ready to measure a 900 KB image-like resource.

5. Give the User a Choice

When the network looks poor, a product can prioritize readable content and let the user request the original quality.

Code:
function applyUserQualityChoice(choice) {
  const state = {
    quality: choice,
    label: choice === "original" ? "Original image requested" : "Compressed image shown first",
    estimatedSize: choice === "original" ? "3.6 MB" : "420 KB"
  };

  return state;
}
Result:

Compressed

Show content quickly with a smaller file.

View Original

Load the sharper image when the user asks for it.

6. Estimate Upload Time

Upload flows can respond to slow connections by extending patience and giving the user a realistic duration message.

Code:
function estimateUpload(fileMb, uploadMbps) {
  const seconds = Math.ceil(fileMb * 8 / uploadMbps);
  const slow = seconds > 20;

  return {
    seconds,
    timeoutSeconds: slow ? seconds + 30 : seconds + 10,
    message: slow
      ? "Your current network is not very good. This may take a while."
      : "Upload should finish soon."
  };
}
Result:
Slow network