Skip to main content

Country canvas

Frame one country and paint media inside its outline. A still, an animated GIF, a video, another canvas, or a live camera stream: clipped to the real boundary.

globe.focusOn("India", { isolate: true });
globe.setCountryMedia("India", "/launch-reel.mp4");
Loading globe…

Focusing a country

globe.focusOn("India", { isolate: true, outlineWidth: 2 });
globe.clearFocus();

Or declaratively:

createGlobe(canvas, {
mode: "map",
focus: { country: "IN", isolate: true, dim: 0.16, outlineWidth: 2 },
});
FieldDefaultMeaning
countryNot setISO code, numeric id or name
isolatefalseDrop every other country entirely
dim0.16Opacity of the others when not isolating. 1 leaves them alone
outlineWidth1.6Stroke on the focused country
padding0.82Fraction of the viewport to fill

:::tip focusOn lifts the zoom ceiling Framing India needs roughly 10.6× against a default maxZoom of 8, so focusOn raises the ceiling to whatever the frame needs. clearFocus() puts it back. :::

Size the canvas to the country or it will letterbox:

canvas.style.aspectRatio = String(1 / globe.countryAspect("India"));
globe.resize();

Adding media

createGlobe(canvas, {
countryMedia: {
IN: { src: "/reel.mp4", fit: "cover" },
BR: "/photo.jpg",
US: { src: "/loop.gif", opacity: 0.9 },
},
});

Keys match the same way choropleth keys do: ISO, numeric id or name.

FieldDefaultMeaning
srcNot setURL, image, video, canvas, ImageBitmap or MediaStream
fit"cover"Mirrors CSS object-fit: cover, contain, fill
typeautoForce "image" or "video" when the URL has no useful extension
opacity1
blendNot setAny canvas composite operation, e.g. "screen"
scale1Extra zoom on top of the fit
offset[0, 0]Pixel nudge

Source type is inferred from the extension: .mp4, .webm, .ogv, .mov become looping muted video; .gif keeps animating; everything else loads as an image.

:::note Why GIFs need a DOM node Browsers only advance an animated GIF that is attached to the document. CanvasGlobe parks the <img> off-screen at 1×1 with near-zero opacity so drawImage sees live frames. Nothing is visible. :::

Text cut out of a country

The same clip path, filled with type instead of pixels. The text auto-sizes to the shape's width.

globe.setCountryMedia("India", {
text: "MADE IN INDIA",
color: "#ffffff",
background: "#6d28d9",
});
FieldMeaning
textThe string
color, backgroundFill colours
font, weightDefaults to bold Inter
sizeFixed px; omit to auto-fit
fillFraction of the shape width to span. Default 0.86
opacity, offset

Live streams

Because MediaStream is accepted directly, a webcam works:

const stream = await navigator.mediaDevices.getUserMedia({ video: true });
globe.setCountryMedia("India", stream);
globe.focusOn("India", { isolate: true });

Exporting it

Combine with exporting to produce an asset:

// A transparent PNG of just the country, media and all
globe.exportImage({ preset: "square", transparent: true });

// Or a six-second clip
await globe.record({ duration: 6000, filename: "launch.webm" }).promise;

Cleaning up

setCountryMedia(country, null) releases one source; destroy() releases them all. Video elements are paused and detached, so nothing keeps decoding in the background.