Skip to main content

Install the package

npm install @cloudflare/speedtest

Simplest usage

The shortest path to a result is a single line. Instantiate SpeedTest and assign onFinish — the engine starts automatically and calls your handler when all measurements are done:
import SpeedTest from '@cloudflare/speedtest';

new SpeedTest().onFinish = results => console.log(results.getSummary());
getSummary() returns an object with the key metrics:
{
  download: 94500000,         // bps
  upload: 22300000,           // bps
  latency: 12,                // ms (unloaded)
  jitter: 1,                  // ms
  downLoadedLatency: 18,      // ms (latency during download)
  downLoadedJitter: 3,        // ms
  upLoadedLatency: 24,        // ms (latency during upload)
  upLoadedJitter: 5,          // ms
  packetLoss: 0,              // ratio 0–1
}
Values are undefined for any measurement that did not run or did not produce enough data points. Bandwidth values are in bits per second (bps), not bytes.

Complete example with all event handlers

Use all four event handlers to track engine state, stream live results, and handle errors:
import SpeedTest from '@cloudflare/speedtest';

const engine = new SpeedTest();

engine.onRunningChange = running => {
  console.log(running ? 'Test started' : 'Test paused or finished');
};

engine.onResultsChange = ({ type }) => {
  // Called after each individual measurement completes.
  // `type` indicates which measurement just finished: 'latency', 'download', 'upload', 'packetLoss'
  console.log(`Measurement updated: ${type}`);
  console.log(engine.results.getSummary());
};

engine.onFinish = results => {
  console.log('All measurements complete');
  console.log(results.getSummary());
  console.log(results.getScores());
};

engine.onError = error => {
  console.error('Measurement error:', error);
};

Browser-based example

Here is a minimal HTML page that loads the SDK from a CDN, waits for a button click before starting, and displays live results as they arrive:
<body>
  <div id="controls"></div>
  <div id="result"></div>

  <script type="module">
    import SpeedTest from 'https://cdn.skypack.dev/@cloudflare/speedtest';

    const controlEl = document.getElementById('controls');
    const resEl = document.getElementById('result');

    const engine = new SpeedTest({
      autoStart: false
    });

    engine.onRunningChange = running =>
      controlEl.textContent = running ? 'Running...' : 'Finished!';

    engine.onResultsChange = ({ type }) => {
      !engine.isFinished && setResult(engine.results.raw);
      console.log(type);
    };

    engine.onFinish = results => {
      setResult(results.getSummary());
      console.log(results.getSummary());
      console.log(results.getScores());
    };

    engine.onError = (e) => console.log(e);

    const playButton = document.createElement('button');
    playButton.textContent = 'Start Speed Measurement';
    playButton.onclick = () => engine.play();
    controlEl.appendChild(playButton);

    function setResult(obj) {
      const resTxt = document.createElement('pre');
      resTxt.textContent = JSON.stringify(obj, null, 2);
      resEl.textContent = '';
      resEl.appendChild(resTxt);
    }
  </script>
</body>

Manual start with autoStart: false

By default the engine starts measuring immediately on instantiation. Set autoStart: false to defer measurement until you call engine.play() — useful when you want the user to trigger the test:
import SpeedTest from '@cloudflare/speedtest';

const engine = new SpeedTest({ autoStart: false });

engine.onFinish = results => console.log(results.getSummary());

// Start when ready — for example, on a button click
document.getElementById('start-btn').addEventListener('click', () => {
  engine.play();
});
You can also pause and resume in progress:
engine.pause();   // pauses the current measurement
engine.play();    // resumes where it left off
engine.restart(); // clears results and starts over from the beginning

Reading results

getSummary() gives you a high-level snapshot. For more granular data, use the individual result methods:
engine.onFinish = results => {
  // Bandwidth (in bps)
  console.log(results.getDownloadBandwidth());
  console.log(results.getUploadBandwidth());

  // Unloaded latency and jitter (in ms)
  console.log(results.getUnloadedLatency());
  console.log(results.getUnloadedJitter());

  // Latency under load
  console.log(results.getDownLoadedLatency());
  console.log(results.getUpLoadedLatency());

  // All individual download measurement points
  console.log(results.getDownloadBandwidthPoints());
  // Each point: { bytes, bps, duration, ping, measTime, serverTime, transferSize }

  // AIM quality scores
  console.log(results.getScores());
};
Results are available incrementally — you can call any result method inside onResultsChange to display live progress before the test finishes.

Next steps

Installation

CDN usage, TypeScript support, and ES module details.

Configuration options

Customize measurement URLs, percentiles, timeouts, and more.

Measurement config

Define a custom measurement sequence with specific payload sizes and counts.

Results API

Full reference for all methods on the Results object.

Build docs developers (and LLMs) love