libping
v1.0.0
Published
Browser Trickle ICE latency probes for WebRTC STUN gateways — pick the fastest regional gateway without a full WebRTC call.
Downloads
172
Maintainers
Readme
libping
Measure UDP latency to WebRTC STUN gateways in the browser using Trickle ICE — without a full WebRTC call, signaling, or microphone access.
Use it to pick the fastest regional media gateway before connecting.
Install
npm install libpingBrowser (jsDelivr):
<script src="https://cdn.jsdelivr.net/npm/libping/libping.js"></script>
<script>
const { pingGateways, TWILIO_GATEWAYS } = LibPing;
</script>Requirements
- A secure context:
https://orhttp://localhost - A browser with
RTCPeerConnection(Chrome, Firefox, Safari, Edge)
Quick start
import LibPing from 'libping';
const results = await LibPing.pingGateways(LibPing.TWILIO_GATEWAYS);
const fastest = results.find((r) => r.rtt < Infinity);
console.log(fastest?.code, fastest?.rtt); // e.g. "ie1", 42Pass your own gateways:
const gateways = [
{ code: 'us1', region: 'US East', url: 'stun:ashburn.stun.twilio.com:3478' },
{ code: 'de1', region: 'Europe', url: 'stun:frankfurt.stun.twilio.com:3478' },
];
const results = await LibPing.pingGateways(gateways);Each result includes your gateway fields plus:
| Field | Type | Description |
|-------|------|-------------|
| rtt | number | Estimated RTT in ms, or Infinity if unreachable |
| status | 'success' \| 'timeout' \| 'failed' | Probe outcome |
Results are sorted fastest first.
How it works
- Create an isolated
RTCPeerConnectionpinned to one STUN server. - Open a dummy data channel (no mic/camera permission).
- Generate a local offer and call
setLocalDescription()to start ICE gathering. - On the first server-reflexive (
srflx) candidate, record elapsed time and close the connection immediately. - Estimate RTT as
elapsed − 15ms(browser ICE thread overhead).
No SDP answer is sent. No media flows.
API
pingGateways(gateways, options?)
Probe all gateways and return sorted results.
gateways — non-empty array of { url: string, code?: string, region?: string, ... }.
options
| Option | Default | Description |
|--------|---------|-------------|
| warmup | false | One discarded probe per unique hostname before measuring (dedupes shared domains) |
| randomOrder | false | Shuffle probe order each round |
| rounds | 1 | Repeat count; values > 1 probe sequentially and rank by median RTT |
| timeoutMs | 1500 | Per-gateway timeout (ms) |
| roundSettleMs | 100 | Pause between rounds when rounds > 1 |
Execution strategy
| rounds | randomOrder | Behavior |
|----------|---------------|----------|
| 1 | false | All gateways in parallel (fastest) |
| 1 | true | One sequential pass in shuffled order |
| > 1 | either | Sequential each round; median RTT when rounds > 1 |
// Warm DNS/ICE cache, then measure
await LibPing.pingGateways(gateways, { warmup: true });
// Robust selection: 3 shuffled rounds, median RTT
const results = await LibPing.pingGateways(LibPing.TWILIO_GATEWAYS, {
warmup: true,
randomOrder: true,
rounds: 3,
});When rounds > 1, each result also includes samples, successCount, and rounds.
TWILIO_GATEWAYS
Default Twilio regional STUN endpoints:
| Code | Region | STUN URL |
|------|--------|----------|
| us1 | US East (Ashburn) | stun:ashburn.stun.twilio.com:3478 |
| us2 | US West (Oregon) | stun:umatilla.stun.twilio.com:3478 |
| de1 | Europe (Frankfurt) | stun:frankfurt.stun.twilio.com:3478 |
| ie1 | Europe (Ireland) | stun:dublin.stun.twilio.com:3478 |
| jp1 | Asia Pacific (Tokyo) | stun:tokyo.stun.twilio.com:3478 |
| sg1 | Asia Pacific (Singapore) | stun:singapore.stun.twilio.com:3478 |
| au1 | Australia (Sydney) | stun:sydney.stun.twilio.com:3478 |
| br1 | South America (São Paulo) | stun:sao-paulo.stun.twilio.com:3478 |
| in1 | India (Mumbai) | stun:mumbai.stun.twilio.com:3478 |
parseStunHostname(url)
Extract the hostname from a stun:, stuns:, turn:, or turns: URL.
uniqueGatewaysByDomain(gateways)
Return one gateway per unique hostname — used internally for warmup deduplication.
Constants
ICE_OVERHEAD_MS—15PROBE_TIMEOUT_MS—1500
CommonJS
const LibPing = require('libping');
LibPing.pingGateways(LibPing.TWILIO_GATEWAYS).then(console.log);Limitations
What is measured: time to first srflx candidate, not pure ICMP-style RTT. This includes browser ICE overhead and, on a cold DNS cache, hostname resolution (~50–150 ms). Use { warmup: true } or { rounds: 2 } to reduce DNS skew.
Browser only: requires RTCPeerConnection in a secure context. Not for Node.js server-side use.
STUN-specific: measures reachability and latency to a STUN endpoint, not end-to-end media path quality.
Demo UI
This repo includes a browser demo (index.html) with parallel and robust test modes, optional DNS warmup, and resource-usage metrics.
git clone <repo-url>
cd ping
python3 -m http.server 8080Open http://localhost:8080.
| File | Purpose |
|------|---------|
| libping.js | Library (published to npm) |
| index.html, app.js | Strike UI demo |
| ping.js, metrics.js | Demo-only probe orchestration and metrics |
License
MIT
