Skip to main content

Overview

SpeedTest is the main class exported by @cloudflare/speedtest. Instantiate it with new to create and optionally auto-start a measurement run.
import SpeedTest from '@cloudflare/speedtest';

const engine = new SpeedTest(config?: ConfigOptions);

Constructor

new SpeedTest(config?: ConfigOptions)
Creates a new SpeedTest engine instance. On instantiation the constructor:
  1. Merges the provided config with default values.
  2. Creates an internal Results object to accumulate measurements.
  3. Calls play() automatically if autoStart is true (the default).
config
ConfigOptions
Optional configuration object. All fields are optional — omitting config entirely runs the engine with default settings. See ConfigOptions for the full field reference.

Properties

All properties are read-only getters.
results
Results
required
The current Results object. Methods on this object return partial values while the test is still running and their final values once all measurements complete.
const bw = engine.results.getDownloadBandwidth(); // may be undefined while running
isRunning
boolean
required
true while the engine is actively performing measurements; false when paused, not yet started, or finished.
isFinished
boolean
required
true once all measurements in the sequence have completed. The engine does not transition back to false after finishing — call restart() to begin a fresh run.

Methods

play()

play(): void
Starts or resumes measurements.
  • Clears the browser’s PerformanceResourceTiming buffer and sets its size to 10,000 entries before starting.
  • Does nothing if the engine is already running (isRunning === true).
  • Does nothing if the engine has finished (isFinished === true). Call restart() instead.

pause()

pause(): void
Pauses the current measurement mid-run.
  • Sets isRunning to false and triggers onRunningChange(false).
  • Only has an effect on pausable measurement types: latency, download, and upload. A packetLoss measurement in progress runs to completion before the pause takes effect.
  • Does nothing if the engine is already paused or finished.

restart()

restart(): void
Clears all accumulated results and restarts measurements from the beginning.
  • Destroys any in-progress measurement engine.
  • Resets isRunning and isFinished to false.
  • Calls play() immediately after clearing.

Event callbacks

Assign functions to these properties to receive notifications as the engine runs. You can reassign them at any time before or after starting the engine.
onRunningChange
(running: boolean) => void
Called whenever the engine starts or stops. running is the new value of isRunning. See Events reference for details.
onResultsChange
({ type: string }) => void
Called whenever any measurement result is updated. type is the measurement category that changed ("latency", "download", "upload", or "packetLoss"). This fires many times during a test run. See Events reference for details.
onPhaseChange
({ measurement: MeasurementConfig, measurementId: number }) => void
Called when the engine advances to the next measurement in the sequence. measurementId is the zero-based index into the measurements array. See Events reference for details.
onFinish
(results: Results) => void
Called once when all measurements are complete. results is the final Results object with all values available. See Events reference for details.
onError
(error: string) => void
Called when a measurement error occurs (for example, a connection failure or TURN server credential error). The engine skips the failing measurement and continues to the next one. See Events reference for details.

Complete example

The following example wires up all callbacks, controls playback, and reads results.
import SpeedTest from '@cloudflare/speedtest';
import type { ConfigOptions, MeasurementConfig, Results } from '@cloudflare/speedtest';

const config: ConfigOptions = {
  autoStart: false, // start manually below
  measureDownloadLoadedLatency: true,
  measureUploadLoadedLatency: true,
  bandwidthPercentile: 0.9,
};

const engine = new SpeedTest(config);

// Track running state
engine.onRunningChange = (running: boolean) => {
  console.log(`Engine is now ${running ? 'running' : 'paused'}`);
  document.getElementById('status')!.textContent = running ? 'Running…' : 'Paused';
};

// Live result updates
engine.onResultsChange = ({ type }: { type: string }) => {
  const results: Results = engine.results;

  if (type === 'download') {
    const bw = results.getDownloadBandwidth();
    if (bw !== undefined) {
      console.log(`Download so far: ${(bw / 1e6).toFixed(1)} Mbps`);
    }
  }

  if (type === 'latency') {
    const latency = results.getUnloadedLatency();
    if (latency !== undefined) {
      console.log(`Latency so far: ${latency.toFixed(1)} ms`);
    }
  }
};

// Phase transitions
engine.onPhaseChange = ({
  measurement,
  measurementId,
}: {
  measurement: MeasurementConfig;
  measurementId: number;
}) => {
  console.log(`Phase ${measurementId}: ${measurement.type}`);
};

// Final results
engine.onFinish = (results: Results) => {
  const summary = results.getSummary();
  console.log('Test complete:', summary);

  const scores = results.getScores();
  console.log('AIM scores:', scores);
};

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

// Manual playback controls
const startBtn = document.getElementById('start')!;
const pauseBtn = document.getElementById('pause')!;
const restartBtn = document.getElementById('restart')!;

startBtn.addEventListener('click', () => engine.play());
pauseBtn.addEventListener('click', () => engine.pause());
restartBtn.addEventListener('click', () => engine.restart());

// Kick off the test
engine.play();
Assigning onFinish after the engine has already started is safe — the callback is stored and invoked when measurements complete regardless of when it was assigned.
Use onResultsChange to power live UI updates during the test and onFinish to render the final summary. Avoid doing heavy work inside onResultsChange because it fires on every individual data point.

Build docs developers (and LLMs) love