Skip to main content

Status page by region

A status page's job is to answer one question in under two seconds: is the bit I use working? For anyone running in more than one region, a list of nine region names does not answer that as fast as nine coloured dots in the right places.

Loading globe…

The code

import { createGlobe } from "canvas-globe";

const COLOR = { ok: "#22c55e", degraded: "#f59e0b", down: "#ef4444" };

function toMarker(region) {
return {
label: region.name,
lat: region.lat,
lon: region.lon,
color: COLOR[region.status],
live: region.status !== "ok", // only unhealthy regions pulse
latency: region.latency,
};
}

const globe = createGlobe(canvas, {
mode: "map",
preset: "blueprint",
projection: "naturalEarth",
autoRotate: false,
labels: "markers",
markers: regions.map(toMarker),
legend: {
title: "Region health",
items: [
{ color: COLOR.ok, label: "Operational" },
{ color: COLOR.degraded, label: "Degraded" },
{ color: COLOR.down, label: "Outage" },
],
},
showViewer: { label: "You" },
tooltip: (m) => (m.latency ? `${m.label}: ${m.latency} ms` : `${m.label}: no response`),
onClick: (m) => m && scrollToIncident(m.label),
});

// Refresh on the same cadence as your health checks.
setInterval(async () => {
const regions = await fetch("/api/status").then((r) => r.json());
globe.setMarkers(regions.map(toMarker));
}, 30000);

Why only the broken ones pulse

live: true gives a marker a slow pulse. The instinct is to put it on everything, because it looks alive. Do not: if all nine pulse, none of them stand out, and you have built a decoration instead of a signal.

Keep healthy markers green, still, and small. Reserve motion, saturation, and size for the region that needs attention.

Colour is not enough

Roughly one in twelve men cannot reliably separate your green from your red. A status page that encodes state only in hue fails exactly the people who most need it to work.

  • Keep the text list. The map is a summary of a table that is still on the page, in the DOM, readable by a screen reader and by anyone who prefers reading.
  • Vary size as well as colour. A down region can be a larger marker.
  • Use the label. With labels: "markers" the region name is on the map regardless of colour.
<ul class="regions">
<li><span data-status="ok">Operational</span> eu-west: 18 ms</li>
<li><span data-status="degraded">Degraded</span> eu-central: 187 ms</li>
<li><span data-status="down">Outage</span> sa-east: no response</li>
</ul>

See Accessibility for how the canvas exposes itself to assistive tech.

Latency instead of state

If you are showing performance rather than uptime, drive the colour continuously and let the legend carry a scale:

import { colorScale } from "canvas-globe";

const latencyColor = colorScale([20, 250], ["#22c55e", "#ef4444"]);

markers: regions.map((r) => ({ ...r, color: latencyColor(r.latency) })),
legend: { title: "p95 latency (ms)", scale: { domain: [20, 250], range: ["#22c55e", "#ef4444"] } },

Pick the domain from your actual SLO, not from the data. A scale that rescales itself every refresh makes a good day and a bad day look identical.

Show them their region

The most useful thing a status map can do is point at the region the visitor is actually served from. If you know it, say so:

globe.setViewerLocation({ lat: 51.47, lon: -0.45, label: "You are served from eu-west" });

If you do not, showViewer falls back to the visitor's time zone: no permission prompt, no request, and accurate enough to be useful at this zoom. See Viewer location.

Internal dashboards

The same map on a wall screen wants different settings:

{
mode: "map",
preset: "midnight",
interactive: false, // nobody is going to drag a TV
labels: "markers",
fps: 15, // it will run for months
}

Add spikes keyed to request volume if the board also needs to answer "where is the traffic", and check Performance before leaving anything running indefinitely.

Next