Measure your internet speed programmatically.
You may have used tools like Ookla or the speed test by Google to check your internet speed or troubleshoot your connection. But have you ever wondered how you could do this programmatically? If yes, then you've come to the right place. Let’s begin!
Here are some key pre-requisites to assume for readers:
- ReactJs (UI and logic)
- Javascript (speed test SDK)
- npm (installing dependencies)
- Vite (bootstrapping the project)
Since the original library is in Javascript and CLI tools are available in other languages like Java, Go, etc, choosing a frontend is up to you. You can also do this on Android & iOS apps if you want to.
This is the same tool that Google is using behind the scenes in their browser-based speed test feature.
M-lab has done a great job of writing client and server SDKs on multiple platforms.
Let’s start by creating a simple React application using Vite. This command will take you through a series of questions to help you decide on a few key configurations for your project.
npm create vite@latest
Once your project folder has been created, open it with your favorite editor and install all the dependencies using this command
npm install
Great. Now you have successfully set up your project. Clear the App.jsx file, App.css file, and index.css file. I usually delete the default SVG(s) inside the assets folder as I don’t use them.
The next step is to install the speed test client-side dependency provided by m-lab. Install it to your project using this command
npm i @m-lab/ndt7
M-Lab provides the largest collection of open Internet performance data on the planet. They have multiple servers all over the world that can be used to measure internet speed and the best part is that all of the measurement tools hosted by M-Lab are open-source which means anyone with time and skills can contribute.
The ndt7 (Network Diagnostic Tool) protocol measures the application-level download or upload performance using WebSockets over a single TCP connection.
Here’s a simplified breakdown of the speed test process:
- Data Transfer: The speed test client sends a request to a remote server. The server responds with a file of a known size.
- Time Measurement: The client records the time it takes to download the file.
- Calculation: The download speed is calculated by dividing the file size by the download time.
- Upload Test: A similar process is followed for the upload test, where the client sends a file to the server and measures the upload time.
Let’s jump into the code now.
import { useState } from "react";
import ndt7 from "@m-lab/ndt7";
const App = () => {
const [downloadSpeed, setDownloadSpeed] = useState(0);
const [uploadSpeed, setUploadSpeed] = useState(0);
const [complete, setComplete] = useState(true);
const [testTime, setTestTime] = useState(0);
const startSpeedtest = () => {
// Reset state variables
setDownloadSpeed(0);
setUploadSpeed(0);
setTestTime(0);
setComplete(false);
const startTime = Date.now();
ndt7
.test(
{
userAcceptedDataPolicy: true,
downloadworkerfile: "/ndt7-download-worker.min.js",
uploadworkerfile: "/ndt7-upload-worker.min.js",
metadata: {
client_name: "speedtest-sample",
},
},
{
// Get download speed
downloadMeasurement: function (data) {
if (data.Source === "client") {
setDownloadSpeed(data.Data.MeanClientMbps.toFixed(2) + " Mb/s");
}
},
downloadComplete: function (data) {
const clientGoodput = data.LastClientMeasurement.MeanClientMbps;
setDownloadSpeed(clientGoodput.toFixed(2) + "Mb/s");
},
// Get upload speed
uploadMeasurement: function (data) {
if (data.Source === "server") {
setUploadSpeed(
(
(data.Data.TCPInfo.BytesReceived /
data.Data.TCPInfo.ElapsedTime) *
8
).toFixed(2) + " Mb/s"
);
}
},
uploadComplete: function (data) {
const bytesReceived =
data.LastServerMeasurement.TCPInfo.BytesReceived;
const elapsed = data.LastServerMeasurement.TCPInfo.ElapsedTime;
const throughput = (bytesReceived * 8) / elapsed;
setUploadSpeed(throughput.toFixed(2) + "Mb/s");
},
error: function (err) {
console.log("Error while running the test:", err.message);
setComplete(false);
},
}
)
.then((exitcode) => {
setTestTime((Date.now() - startTime) / 1000);
setComplete(true);
});
};
return (
<div className="container">
<button onClick={startSpeedtest} disabled={!complete}>
{complete ? "Start Test" : "Running..."}
</button>
<div className="measurement-container">
<div className="measurement">
<h1>Download Speed</h1>
<div className="speed-value">{downloadSpeed || "0 Mb/s"}</div>
</div>
<div className="measurement">
<h1>Upload Speed</h1>
<div className="speed-value">{uploadSpeed || "0 Mb/s"}</div>
</div>
</div>
<div className="test-time">Test Duration: {testTime || 0} seconds</div>
</div>
);
};
export default App;
@m-lab/ndt7
library uses Web Workers for measuring download and upload speeds:
- Performance Isolation: Runs speed test logic in a separate thread, preventing interference with the UI and ensuring accurate results.
- Asynchronous Data Handling: Manages data streams for speed tests without blocking the main thread, ensuring smooth data transfer.
- Accurate Timing: Provides precise timing of data transfers by avoiding the potential delays and interruptions of the main thread.
- Reduced UI Impact: Prevents user interactions and UI updates from affecting the speed test, delivering more reliable measurements.
- Concurrent Connections: Efficiently handles multiple network connections for testing maximum throughput, enhancing measurement accuracy.
Since we use Vite, we need to include the download and upload worker files from the source code in the public directory so that they can be directly accessed by the component.
The first argument to the ndt7.test
method takes an object where we specify the upload & download worker files, and accept the data policy and some metadata about the client.
The second argument to ndt7.test
is an object containing callback functions that are triggered during the speed test. This is where we get the result for our download and upload network speed measurements.
downloadMeasurement
and uploadMeasurement
methods provide us with real-time updates.
downloadMeasurement
data.Source
: Checks if the data is from the client.data.Data.MeanClientMbps
: Represents the average download speed in megabits per second (Mbps).
uploadMeasurement
data.Source
: Checks if the data is from the server.data.Data.TCPInfo.BytesReceived
: The total bytes received during the test.data.Data.TCPInfo.ElapsedTime
: The time elapsed during the test in microseconds.
Since we get the values with a lot of decimal places we round it to 2 digits and set it to our state.
downloadComplete
and uploadComplete
methods run after each test has been completed providing us with some details like the time taken for each test and the final measured value which again needs to be rounded to 2-digit decimal places.
The error
callback handles any errors that occur during the test.
I’ve chosen to skip the CSS code for this tutorial as it’s not what we are focusing on. This is what the final output looks like.
If you’re worried about the correctness of the result, don’t be! I have verified the download and upload speed values from this library with multiple other speed test measuring platforms like Ookla & fast.com by Netflix and the results are highly accurate.
Source code : https://github.com/Nikhiladiga/react-speedtest