Vanilla JavaScript is the simplest way to integrate the SpeedOf.Me speed test API. No frameworks or build tools required.
<script src="https://speedof.me/api/api.js"></script>
// Required configuration
SomApi.account = "YOUR_API_KEY";
SomApi.domainName = "your-domain.com";
// Callbacks
SomApi.onTestCompleted = function(result) {
console.log('Download:', result.download, 'Mbps');
console.log('Upload:', result.upload, 'Mbps');
console.log('Latency:', result.latency, 'ms');
};
SomApi.onError = function(error) {
console.error('Error:', error.code, error.message);
};
// Start test
SomApi.startTest();
// Test behavior
SomApi.config.sustainTime = 6; // 1-8 seconds (higher = more accurate)
SomApi.config.testServerEnabled = true; // Include server location
SomApi.config.userInfoEnabled = true; // Include IP/hostname
SomApi.config.latencyTestEnabled = true; // Run latency test
SomApi.config.uploadTestEnabled = true; // Run upload test
// Progress updates
SomApi.config.progress.enabled = true; // Enable progress callbacks
SomApi.config.progress.verbose = true; // Detailed progress data
SomApi.onTestCompleted = function(result) {
result.download; // Download speed in Mbps
result.upload; // Upload speed in Mbps
result.latency; // Latency in ms
result.jitter; // Jitter in ms
result.testServer; // Server location code
result.ip_address; // Client IP address
result.hostname; // Client hostname
};
SomApi.onProgress = function(progress) {
progress.type; // 'download' or 'upload'
progress.currentSpeed; // Current speed in Mbps
progress.percentDone; // 0-100
progress.pass; // Current test pass number
};
SomApi.onError = function(error) {
error.code; // 1001 = invalid account, 1002 = domain mismatch
error.message; // Human-readable message
};
Minimal implementation with start button and results display:
<!DOCTYPE html>
<html>
<head>
<title>Speed Test</title>
<script src="https://speedof.me/api/api.js"></script>
<style>
body { font-family: sans-serif; max-width: 400px; margin: 50px auto; text-align: center; }
button { padding: 15px 40px; font-size: 18px; cursor: pointer; }
#results { margin-top: 20px; font-size: 18px; }
</style>
</head>
<body>
<h1>Speed Test</h1>
<button onclick="startTest()">Start Test</button>
<div id="results"></div>
<script>
SomApi.account = "YOUR_API_KEY";
SomApi.domainName = "your-domain.com";
SomApi.onTestCompleted = function(r) {
document.getElementById('results').innerHTML =
'Download: ' + r.download + ' Mbps<br>' +
'Upload: ' + r.upload + ' Mbps<br>' +
'Latency: ' + r.latency + ' ms';
};
SomApi.onError = function(e) {
document.getElementById('results').innerHTML = 'Error: ' + e.message;
};
function startTest() {
document.getElementById('results').innerHTML = 'Testing...';
SomApi.startTest();
}
</script>
</body>
</html>
Full-featured implementation with Bootstrap 5, settings panel, progress bar, and styled results. Copy and paste this entire file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Speed Test - Advanced</title>
<!-- Bootstrap 5 CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.2/font/bootstrap-icons.css" rel="stylesheet">
<!-- SpeedOf.Me API -->
<script src="https://speedof.me/api/api.js"></script>
<style>
body {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
min-height: 100vh;
color: #fff;
}
.card {
background: rgba(255, 255, 255, 0.95);
border: none;
border-radius: 1rem;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
}
.settings-card {
background: #f8f9fa;
border-radius: 0.75rem;
}
.result-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
}
.speed-value {
font-size: 2.5rem;
font-weight: 700;
line-height: 1;
}
.speed-unit {
font-size: 0.9rem;
opacity: 0.8;
}
.btn-start {
background: linear-gradient(135deg, #00d4ff 0%, #090979 100%);
border: none;
padding: 0.875rem 2.5rem;
font-size: 1.1rem;
font-weight: 600;
border-radius: 50px;
transition: transform 0.2s, box-shadow 0.2s;
}
.btn-start:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 212, 255, 0.4);
}
.btn-start:disabled { opacity: 0.6; }
.progress-section {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 0.75rem;
color: #fff;
}
.progress-bar-custom {
height: 8px;
border-radius: 4px;
background: rgba(255,255,255,0.2);
overflow: hidden;
}
.progress-bar-fill {
height: 100%;
background: #fff;
border-radius: 4px;
transition: width 0.3s ease;
}
.form-check-input:checked {
background-color: #667eea;
border-color: #667eea;
}
.live-speed {
font-size: 3rem;
font-weight: 700;
}
</style>
</head>
<body>
<div class="container py-5">
<!-- Header -->
<div class="text-center mb-4">
<h1 class="display-5 fw-bold mb-2">
<i class="bi bi-speedometer2 me-2"></i>Speed Test
</h1>
</div>
<div class="row justify-content-center">
<div class="col-lg-10">
<div class="card">
<div class="card-body p-4">
<div class="row">
<!-- Settings Column -->
<div class="col-lg-4 mb-4 mb-lg-0">
<div class="settings-card p-3">
<h5 class="mb-3">
<i class="bi bi-sliders me-2"></i>Settings
</h5>
<!-- Sustain Time -->
<div class="mb-4">
<label class="form-label d-flex justify-content-between">
<span>Sustain Time</span>
<span class="badge bg-primary" id="sustainTimeValue">6</span>
</label>
<div class="d-flex align-items-center">
<small class="text-muted me-2">Fast</small>
<input type="range" class="form-range flex-grow-1" id="sustainTime"
min="1" max="8" value="6" oninput="updateSustainTime()">
<small class="text-muted ms-2">Accurate</small>
</div>
</div>
<!-- Options -->
<div class="mb-3">
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="latencyTest" checked>
<label class="form-check-label" for="latencyTest">
<i class="bi bi-clock me-1"></i>Latency Test
</label>
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="uploadTest" checked>
<label class="form-check-label" for="uploadTest">
<i class="bi bi-arrow-up me-1"></i>Upload Test
</label>
</div>
</div>
<!-- Start Button -->
<div class="d-grid mt-4">
<button id="btnStart" class="btn btn-primary btn-start" onclick="startTest()">
<i class="bi bi-play-fill me-2"></i>Start Test
</button>
</div>
</div>
</div>
<!-- Results Column -->
<div class="col-lg-8">
<!-- Initial State -->
<div id="initialState" class="text-center py-5">
<i class="bi bi-speedometer2 text-muted" style="font-size: 4rem;"></i>
<h4 class="text-muted mt-3">Ready to Test</h4>
<p class="text-muted">Click Start Test to begin</p>
</div>
<!-- Progress State -->
<div id="progressState" style="display: none;">
<div class="progress-section p-4 mb-4">
<div class="text-center mb-3">
<div class="text-uppercase small opacity-75" id="progressType">Initializing</div>
<div class="live-speed" id="liveSpeed">--</div>
<div class="small opacity-75">Mbps</div>
</div>
<div class="progress-bar-custom">
<div class="progress-bar-fill" id="progressBar" style="width: 0%"></div>
</div>
<div class="text-center mt-2 small opacity-75" id="progressPercent">0%</div>
</div>
</div>
<!-- Results State -->
<div id="resultsState" style="display: none;">
<div class="row g-3 mb-4">
<div class="col-6">
<div class="card result-card h-100">
<div class="card-body text-center py-4">
<i class="bi bi-arrow-down-circle"></i>
<div class="speed-value mt-2" id="downloadSpeed">--</div>
<div class="speed-unit">Mbps Download</div>
</div>
</div>
</div>
<div class="col-6">
<div class="card result-card h-100">
<div class="card-body text-center py-4">
<i class="bi bi-arrow-up-circle"></i>
<div class="speed-value mt-2" id="uploadSpeed">--</div>
<div class="speed-unit">Mbps Upload</div>
</div>
</div>
</div>
<div class="col-6">
<div class="card bg-light h-100">
<div class="card-body text-center py-3">
<i class="bi bi-clock text-primary"></i>
<div class="fs-3 fw-bold text-dark mt-2" id="latency">--</div>
<div class="text-muted small">ms Latency</div>
</div>
</div>
</div>
<div class="col-6">
<div class="card bg-light h-100">
<div class="card-body text-center py-3">
<i class="bi bi-activity text-primary"></i>
<div class="fs-3 fw-bold text-dark mt-2" id="jitter">--</div>
<div class="text-muted small">ms Jitter</div>
</div>
</div>
</div>
</div>
<!-- Additional Info -->
<div class="p-3 bg-light rounded">
<div class="row text-center g-2">
<div class="col-md-4">
<small class="text-muted d-block">Test Server</small>
<span class="fw-semibold text-dark small" id="testServer">--</span>
</div>
<div class="col-md-4">
<small class="text-muted d-block">Your IP</small>
<span class="fw-semibold text-dark small" id="ipAddress">--</span>
</div>
<div class="col-md-4">
<small class="text-muted d-block">Hostname</small>
<span class="fw-semibold text-dark small" id="hostname">--</span>
</div>
</div>
</div>
</div>
<!-- Error State -->
<div id="errorState" style="display: none;">
<div class="alert alert-danger text-center">
<i class="bi bi-exclamation-triangle-fill me-2"></i>
<span id="errorMessage">An error occurred</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// ========================================
// SpeedOf.Me API Configuration
// ========================================
SomApi.account = "YOUR_API_KEY"; // Replace with your API key
SomApi.domainName = "your-domain.com"; // Replace with your domain
// Callbacks
SomApi.onTestCompleted = onTestCompleted;
SomApi.onError = onError;
SomApi.onProgress = onProgress;
// Enable progress
SomApi.config.progress.enabled = true;
SomApi.config.progress.verbose = true;
// ========================================
// UI Functions
// ========================================
function updateSustainTime() {
var value = document.getElementById('sustainTime').value;
document.getElementById('sustainTimeValue').textContent = value;
}
function startTest() {
// Apply settings from UI
SomApi.config.sustainTime = document.getElementById('sustainTime').value;
SomApi.config.latencyTestEnabled = document.getElementById('latencyTest').checked;
SomApi.config.uploadTestEnabled = document.getElementById('uploadTest').checked;
// Update UI
document.getElementById('initialState').style.display = 'none';
document.getElementById('progressState').style.display = 'none';
document.getElementById('resultsState').style.display = 'none';
document.getElementById('errorState').style.display = 'none';
document.getElementById('btnStart').disabled = true;
document.getElementById('btnStart').innerHTML =
'<span class="spinner-border spinner-border-sm me-2"></span>Testing...';
SomApi.startTest();
}
function onProgress(progress) {
var type = (progress.type || '').toLowerCase();
// Show progress during download/upload
if (type.includes('download') || type.includes('upload')) {
document.getElementById('progressState').style.display = 'block';
document.getElementById('progressType').textContent = progress.type;
document.getElementById('liveSpeed').textContent = progress.currentSpeed || '--';
document.getElementById('progressBar').style.width = progress.percentDone + '%';
document.getElementById('progressPercent').textContent = progress.percentDone + '%';
}
}
function onTestCompleted(result) {
document.getElementById('progressState').style.display = 'none';
document.getElementById('resultsState').style.display = 'block';
document.getElementById('downloadSpeed').textContent = result.download || '--';
document.getElementById('uploadSpeed').textContent = result.upload || '--';
document.getElementById('latency').textContent = result.latency || '--';
document.getElementById('jitter').textContent = result.jitter || '--';
document.getElementById('testServer').textContent = result.testServer || '--';
document.getElementById('ipAddress').textContent = result.ip_address || '--';
document.getElementById('hostname').textContent = result.hostname || '--';
resetButton();
}
function onError(error) {
document.getElementById('progressState').style.display = 'none';
document.getElementById('errorState').style.display = 'block';
document.getElementById('errorMessage').textContent =
'Error ' + error.code + ': ' + error.message;
resetButton();
}
function resetButton() {
document.getElementById('btnStart').disabled = false;
document.getElementById('btnStart').innerHTML =
'<i class="bi bi-arrow-clockwise me-2"></i>Test Again';
}
</script>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>