Realtime in Asia: where your WebSocket server is matters
· Hibira
Why distance sets a floor under realtime latency, what that floor is from Hong Kong to the rest of Asia, and how to measure your own users’ round trip.

A chat message, a price tick or a "someone is typing" indicator feels instant when it arrives within about a tenth of a second. Most of that budget goes to the network, and most of the network time is set by one thing you choose: where the WebSocket server is.
Distance sets the floor
Light in optical fibre covers about 200 km per millisecond, roughly two-thirds of its speed in a vacuum. A round trip to a server 1,000 km away therefore takes at least 10 ms, before any routing, queuing or processing. Real cables don't follow the great circle and real routers add delay, so measured round trips are often two to four times this floor: we measured about 30 ms from Hong Kong to Taipei, against a floor of 8 ms.
Here is the floor from Hong Kong, where Hibira runs today (AWS ap-east-1), to cities across the region:
| From Hong Kong to | Great-circle distance | Minimum round trip |
|---|---|---|
| Taipei | 810 km | 8 ms |
| Manila | 1,120 km | 11 ms |
| Bangkok | 1,730 km | 17 ms |
| Seoul | 2,090 km | 21 ms |
| Singapore | 2,590 km | 26 ms |
| Tokyo | 2,880 km | 29 ms |
| Jakarta | 3,270 km | 33 ms |
| Ashburn, Virginia (US East) | 13,090 km | 131 ms |
These are physics, not benchmarks: no network can beat them, and yours will be slower. The last row is there for scale. Many US services run their realtime infrastructure in US East; Pusher's default cluster, mt1, is there too. Pusher does offer clusters in Asia, so if you're on Pusher, check which cluster your app was created in.
Why it matters more for realtime than for web pages
A web page pays the round trip a few times while it loads. A realtime feature pays it on every interaction:
- Typing indicators and cursors send client events many times a second; a slow round trip makes them lag behind the user.
- Chat goes from one user to your server, to the WebSocket service, and out to everyone else. Every leg adds its distance.
- Presence (who's online) updates when members join and leave; slow updates show people as online after they've left.
- Reconnects after a phone switches networks cost a TLS handshake plus subscriptions, several round trips in a row.
Count every leg
When your backend publishes an event, it travels:
- from the user's action to your backend,
- from your backend to the WebSocket service's HTTP API,
- from the WebSocket service to every subscribed client.
Legs 1 and 3 depend on where your users are. Leg 2 depends on where your backend is. A backend in Tokyo publishing through a WebSocket service in Virginia crosses the Pacific twice for an event between two users in Taipei. Put the WebSocket service near your users, and if you can, put your backend near it.
Measure it yourself
What this test measures: pusher:ping/pusher:pong measures an already-established WebSocket round trip to the realtime endpoint, not publish-to-subscriber latency or connection setup time. It excludes your backend, event fan-out and browser rendering. Compare multiple runs from representative networks (including mobile), and track the median and p95; DNS, TLS, routing and congestion can change the result.
Tables are a starting point; your users' networks are what count. The Pusher protocol has a ping message that the server answers immediately, so you can time the round trip without publishing anything. Save this as ping.mjs and run it with Node.js 22 or newer (which has WebSocket built in):
// node ping.mjs YOUR_APP_KEY [host]
const [key, host = 'ws.hibira.io'] = process.argv.slice(2);
const ws = new WebSocket(`wss://${host}/app/${key}?protocol=7&client=js&version=8.4.0`);
const times = [];
let sent = 0;
const ping = () => {
sent = performance.now();
ws.send(JSON.stringify({ event: 'pusher:ping', data: {} }));
};
ws.onmessage = (msg) => {
const { event } = JSON.parse(msg.data);
if (event === 'pusher:pong') times.push(performance.now() - sent);
if (event !== 'pusher:connection_established' && event !== 'pusher:pong') return;
if (times.length < 20) return void setTimeout(ping, 250);
times.sort((a, b) => a - b);
console.log(`best ${times[0].toFixed(1)} ms, median ${((times[9] + times[10]) / 2).toFixed(1)} ms`);
ws.close();
};Run it from where your users are: a cloud server in each country you serve, or a colleague's laptop on a home connection. The median is the number to compare; the best shows how close the route gets to the floor. Mobile networks add their own delay on top, so test on a phone hotspot too.
For a view from real users, time how long pusher-js takes from connecting to connected and send it to your analytics with the user's country.
What we're doing about it
Hibira runs in Hong Kong today because it sits near the middle of East and Southeast Asia. Tokyo is the next region we're preparing, then Singapore. Apps will stay pinned to one region, so you'll pick the one closest to most of your users.
If you'd like to see the numbers for your own users before you switch, the free plan is enough to run the script above against your own app.