bandwidth

Download/upload speed, latency, and jitter from a live measurement, as async global atoms.

Runs a measurement
Reading a measured atom triggers a download/upload test on first subscribe. Call invalidateMeasurement() to discard the result and re-run.

Install

bun add @valdres/bandwidth

Live example

Loading demo…

Usage

import { Suspense } from "react"
import { useValue } from "valdres-react"
import { downloadSpeedAtom, latencyAtom } from "@valdres/bandwidth"

function Speed() {
    const download = useValue(downloadSpeedAtom)
    const latency = useValue(latencyAtom)
    return <span>{download.toFixed(1)} Mbps · {latency.toFixed(0)} ms</span>
}

// The measured atoms suspend until the test resolves
export function App() {
    return (
        <Suspense fallback="Measuring…">
            <Speed />
        </Suspense>
    )
}

Exports

ExportKindType
downloadSpeedAtomatom (read-only, async)number — Mbps
uploadSpeedAtomatom (read-only, async)number — Mbps
latencyAtomatom (read-only, async)number — ms
jitterAtomatom (read-only, async)number — ms
measurementStatusAtomatom (settable)MeasurementStatus
lastMeasurementAtomatom (settable)number | null — timestamp
invalidateOnAtomatom (settable)GlobalAtom<unknown>[]
measureBandwidthutil fn(options?: MeasureBandwidthOptions) => Promise<BandwidthResult>
invalidateMeasurementutil fn() => void
MeasurementStatustype"idle" | "measuring-latency" | "measuring-download" | "measuring-upload" | "complete" | "error"
BandwidthResulttype{ downloadMbps, uploadMbps, latencyMs, jitterMs, timestamp: number }
MeasureBandwidthOptionstype{ latencySamples?, maxDurationMs?, minDurationMs?, warmupMs?, startStreams?, maxStreams?, stabilityThreshold?: number; signal?: AbortSignal; fresh?: boolean }

Cross-framework

One in-flight measurement is shared across every store and framework. invalidateMeasurement() clears the result and, if anything is subscribed, kicks off a fresh run.