Viewer location
createGlobe(canvas, { showViewer: true });
That pins the person looking at your page. No permission prompt, no network call, no API key, and it resolves instantly.
How it works
Intl.DateTimeFormat().resolvedOptions().timeZone is available in every modern browser, requires no
permission, and costs nothing. The package ships the IANA time-zone table: 312 zones and 237
aliases and maps the reported zone to its published coordinate.
const found = globe.locateViewer();
// {
// lat: 23.29, lon: 82.52,
// timeZone: "Asia/Kolkata", country: "IN",
// source: "timezone", accuracy: "region",
// accuracyMeters: 2242000
// }
:::note Legacy zone names are load-bearing
Chrome commonly reports Asia/Calcutta, not Asia/Kolkata. Alias resolution is why this works for
a large share of real users rather than silently failing.
:::
It is a region, not a pinpoint
A time zone narrows someone to its area, and the tz database publishes one representative city
per zone. Asia/Kolkata covers all of India, so a naive pin would sit confidently on Kolkata even
for someone in Ahmedabad, 1,600 km away.
Two things prevent that:
Anchoring. For countries wider than 8°, the pin uses the country centroid instead of the zone's city, which roughly halves the average error. Smaller countries keep their real city: London stays London.
An uncertainty circle. The pin is surrounded by a dashed circle sized to the actual radius, so the graphic says somewhere in here rather than exactly here.
showViewer: {
anchor: "auto", // "country" | "timezone" to force it
accuracyCircle: true, // false to hide it
}
Asking for precision
const found = await globe.locateViewer({ precise: true });
globe.setViewerLocation(found);
This requests the browser's high-accuracy provider and reports the device's own accuracyMeters, so
you can tell a 20 m GPS fix from a 40 km Wi-Fi one. If the visitor declines, it resolves to the
time-zone estimate and never rejects.
| Prompt | Network | Always works | Typical radius | |
|---|---|---|---|---|
| Time zone (default) | No | No | Yes | Country-sized |
| Locale fallback | No | No | Yes | Country-sized |
precise, GPS | Yes | No | Only if allowed | 5-50 m |
precise, Wi-Fi/IP | Yes | Yes (by the browser) | Only if allowed | 1-50 km |
:::caution On a desktop, "precise" often is not
A laptop with no GPS falls back to network positioning, which frequently lands on your ISP's city.
The reported accuracyMeters is the only way to tell: show it, or size a circle with it, rather
than implying a street address.
:::
Options
showViewer: {
emoji: "📍",
label: "You",
color: "#f97316",
live: true, // pulsing ring
anchor: "auto",
accuracyCircle: true,
accuracyColor: "#22c55e",
flyTo: true, // centre on them once located
ping: true, // fire a ping at their position
precise: false, // ask for GPS on construction
onLocate: (location) => track(location.country),
}
The pin lives outside the markers array, so setMarkers() never wipes it.
Standalone helpers
Useful without a globe at all:
import { locateViewer, timeZoneLocation, countryLocation, placeLocation } from "canvas-globe";
locateViewer(); // the full result
timeZoneLocation("Asia/Calcutta"); // → resolves the alias
countryLocation("IN");
placeLocation("New York");
Privacy
Nothing leaves the device. The time zone is read from the local Intl API and matched against a
bundled table. There is no request, no third party, and nothing to disclose in a cookie banner: though if you send the result to your own analytics, that is your call to document.