Overlays
Layers that sit on top of the base map. All of them compose.
Heatmap
Additive density blobs can replace individual markers or sit underneath them.
createGlobe(canvas, {
heatmap: { radius: 30, intensity: 0.5, color: "#f97316" },
markers,
});
| Field | Default |
|---|---|
radius | 30: blob radius in px at full weight |
intensity | 0.5: peak opacity |
color | theme marker |
Spikes
Bars standing off the surface, scaled by count. Markers ride the tip, so the bar is never buried.
createGlobe(canvas, { spikes: { height: 0.28, width: 2.4 }, markers });
height is the tallest spike as a fraction of the globe radius.
Labels
Text with collision avoidance: the highest-weight label wins and overlapping ones are dropped rather than allowed to pile up.
labels: "markers"; // marker.label / .name / .city
labels: "countries"; // country names, sized to the shape
labels: "both";
Legend
// Continuous ramp
legend: { title: "Visits", scale: { domain: [0, 1000], range: ["#e0f2fe", "#0369a1"] } }
// Discrete swatches
legend: { title: "Tier", items: [{ color: "#0369a1", label: "Enterprise" }] }
position accepts "top-left", "top-right", "bottom-left" (default) or "bottom-right".
Counter
A rolling headline number. It eases whenever the value changes, which makes a live figure feel alive.
createGlobe(canvas, {
counter: {
value: 21947,
label: "customers worldwide",
format: (n) => `$${Math.round(n).toLocaleString()}`,
position: "top-left",
},
});
Title
Headline text is painted onto the canvas. exportImage() therefore returns a
complete graphic without requiring a separate HTML overlay.
createGlobe(canvas, {
title: {
text: "Trusted in 68 countries",
subtitle: "21,947 teams and counting",
position: "top-center",
},
});
| Field | Default |
|---|---|
text | required |
subtitle | Not set |
size | 6.2% of the smaller canvas side |
subtitleSize | 42% of size |
weight | 800 |
font | Inter,system-ui,sans-serif |
color / subtitleColor | theme label |
position | "top-left": also top-center, top-right, bottom-* |
padding | 22 |
Watermark
A logo, a wordmark, or both. Anything you can draw works as image: a URL, an <img>, a
<canvas> or an ImageBitmap.
createGlobe(canvas, {
watermark: {
image: "/logo.svg",
text: "acme.com",
height: 32,
opacity: 0.85,
position: "bottom-right",
},
});
Because it is part of the render, every exportImage(), exportBlob() and record() frame
carries it. That is the difference between a screenshot someone can crop your brand out of and an
asset that stays attributed wherever it gets reposted.
Callouts
Leader lines with a label: for explainer graphics where a tooltip will not do.
annotations: [
{ lat: 23.03, lon: 72.58, text: "HQ: Ahmedabad" },
{ lat: 51.5, lon: -0.12, text: "EU office", dx: -70, dy: -40 },
];
dx/dy offset the label from the point; negative dx flips the box to the left.
Timeline
Hide markers whose date has not arrived yet, then animate the range.
const markers = [
{ lat: 23.03, lon: 72.58, date: "2024-01-15" },
{ lat: 51.5, lon: -0.12, date: "2024-06-02" },
];
globe.setTimelineAt("2024-03-01"); // static cut-off
globe.playTimeline({ duration: 6000, loop: true }); // animate the whole range
globe.stopTimeline();
With no from/to, playTimeline derives the range from your markers' dates. Markers with no
date always show.