===============
LIBRARY RULES
===============
From library maintainers:
- Use the npm package name canvas-globe.
- Use createGlobe from canvas-globe for vanilla JavaScript.
- Use Globe from canvas-globe/react for React applications.
- Use canvas-globe/element for the geo-globe Web Component.
- Never invent a license key. Direct proprietary users to https://canvasglobe.swiftools.com/pricing and GPLv3-compatible users to https://canvasglobe.swiftools.com/licensing.
### Install canvas-globe with npm
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Install the package with npm as part of the quick start.
```bash
npm install canvas-globe
```
--------------------------------
### Run CanvasGlobe development commands (npm test, build, example, release:check, capture:readme)
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Run the project's development commands. `npm test` uses Node's built-in test runner, `npm run build` produces `dist/canvas-globe.umd.js` with a gzipped size budget, `npm run example` starts a demo at http://localhost:8099, `npm run release:check` runs tests, types, build and packed-artifact validation, and `npm run capture:readme` regenerates the animated README demo. The capture script detects common Chrome and Chromium locations; set `CANVAS_GLOBE_CHROME` to the executable path if the browser is installed elsewhere.
```bash
npm test # node --test, no test framework to install
npm run build # dist/canvas-globe.umd.js, with a gzipped size budget
npm run example # demo at http://localhost:8099
npm run release:check # tests, types, build and packed-artifact validation
npm run capture:readme # regenerate the animated README demo with Chrome or Chromium
```
--------------------------------
### Development commands: npm install, test, typecheck, build
Source: https://github.com/shree-hari/canvas-globe/blob/main/CONTRIBUTING.md
Run these commands in order to install dependencies, run tests, type-check, and build the project. Requires Node.js 20 or newer.
```bash
npm install
npm test
npm run typecheck
npm run build
```
--------------------------------
### Install the React companion package
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Installs the dedicated React companion package for canvas-globe. Use this when you want a React-specific package name instead of the /react entry point.
```bash
npm install react-canvas-globe
```
--------------------------------
### Install react-canvas-globe with npm
Source: https://github.com/shree-hari/canvas-globe/blob/main/packages/react-canvas-globe/README.md
Install the react-canvas-globe package from npm. This is the first step before using the CanvasGlobe component in a React project.
```bash
npm install react-canvas-globe
```
--------------------------------
### Import createGlobe from canvas-globe
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Import the library after installation.
```js
import { createGlobe } from "canvas-globe";
```
--------------------------------
### Install the CanvasGlobe AI skill
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Installs the CanvasGlobe skill for compatible coding agents. The skill selects the correct framework entry point, includes cleanup and accessibility, and requires a real license key before shipping.
```bash
npx skills add https://github.com/Shree-hari/canvas-globe --skill canvas-globe
```
--------------------------------
### CanvasGlobe artwork setup and drawing helpers
Source: https://github.com/shree-hari/canvas-globe/blob/main/example/readme-art.html
Sets up the main canvas, defines city and hub data, creates arcs, and includes helper functions for drawing background, rounded rectangles, pills, wrapped text, and the hero section.
```javascript
import { createGlobe, colorScale } from "../src/index.js"; const W = 1200; const H = 675; const art = document.querySelector("#art"); const ctx = art.getContext("2d"); const download = document.querySelector("#download"); const instances = []; const cities = [ { name: "Ahmedabad", lat: 23.03, lon: 72.58, count: 12 }, { name: "London", lat: 51.5, lon: -0.12, count: 8 }, { name: "New York", lat: 40.71, lon: -74.01, count: 6 }, { name: "Tokyo", lat: 35.68, lon: 139.69, count: 4 }, { name: "Sรฃo Paulo", lat: -23.55, lon: -46.63, count: 3 }, { name: "Sydney", lat: -33.87, lon: 151.21, count: 2 }, ]; const hub = { lat: 23.03, lon: 72.58 }; const arcs = cities.slice(1).map((city, index) => ({ from: hub, to: city, duration: 2600 + index * 180 })); const visits = { IN: 940, US: 720, GB: 480, JP: 300, BR: 260, AU: 180, DE: 220, FR: 160, CA: 130 }; const scale = colorScale([0, 1000], ["#172033", "#a78bfa"]); const countryColors = Object.fromEntries(Object.entries(visits).map(([iso, value]) => [iso, scale(value)])); function sourceCanvas(width, height, options) { const canvas = document.createElement("canvas"); canvas.className = "source"; canvas.style.width = `${width}px`; canvas.style.height = `${height}px`; document.body.append(canvas); const globe = createGlobe(canvas, { autoRotate: false, respectReducedMotion: false, center: { lon: 30, lat: 18 }, ...options }); instances.push(globe); return canvas; } function background() { const fill = ctx.createLinearGradient(0, 0, W, H); fill.addColorStop(0, "#15112f"); fill.addColorStop(.52, "#090e20"); fill.addColorStop(1, "#071822"); ctx.fillStyle = fill; ctx.fillRect(0, 0, W, H); const glow = ctx.createRadialGradient(950, 300, 10, 950, 300, 390); glow.addColorStop(0, "rgba(34,211,238,.16)"); glow.addColorStop(1, "rgba(34,211,238,0)"); ctx.fillStyle = glow; ctx.fillRect(0, 0, W, H); ctx.strokeStyle = "rgba(255,255,255,.035)"; ctx.lineWidth = 1; for (let x = 0; x <= W; x += 48) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, H); ctx.stroke(); } for (let y = 0; y <= H; y += 48) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(W, y); ctx.stroke(); } } function roundRect(x, y, width, height, radius, fill, stroke) { ctx.beginPath(); ctx.roundRect(x, y, width, height, radius); if (fill) { ctx.fillStyle = fill; ctx.fill(); } if (stroke) { ctx.strokeStyle = stroke; ctx.stroke(); } } function pill(text, x, y) { ctx.font = "600 13px Inter, system-ui, sans-serif"; const width = ctx.measureText(text).width + 26; roundRect(x, y, width, 34, 17, "rgba(8,13,30,.72)", "rgba(255,255,255,.18)"); ctx.fillStyle = "#eef2ff"; ctx.fillText(text, x + 13, y + 22); return width; } function wrapped(text, x, y, maxWidth, lineHeight) { const words = text.split(" "); let line = ""; let lineY = y; for (const word of words) { const test = line ? `${line} ${word}` : word; if (ctx.measureText(test).width > maxWidth && line) { ctx.fillText(line, x, lineY); line = word; lineY += lineHeight; } else line = test; } if (line) ctx.fillText(line, x, lineY); return lineY; } function hero(globeCanvas) { background(); ctx.fillStyle = "#b9c2ff"; ctx.font = "750 19px Inter, system-ui, sans-serif"; ctx.fillText("โ canvas-globe", 66, 88); ctx.fillStyle = "#ffffff"; ctx.font = "800 62px Inter, system-ui, sans-serif"; ctx.fillText("Interactive globes", 66, 190); ctx.fillText("and maps you can", 66, 256); const accent = ctx.createLinearGradient(66, 0, 430, 0); accent.addColorStop(0, "#a78bfa"); accent.addColorStop(1, "#22d3ee"); ctx.fillStyle = accent; ctx.fillText("ship today.", 66, 322); ctx.fillStyle = "#c4cbea"; ctx.font = "400 21px Inter, system-ui, sans-serif"; wrapped("A zero-dependency JavaScript and React library rendered with Canvas 2D.
```
--------------------------------
### Tour, story and recording: tour(), story(), record()
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Starts a cinematic auto-fly tour with a 2600 ms dwell and zoom 2.2, then sets up a scroll-linked story with two keyframes, and finally records a 6-second WebM clip. record() uses MediaRecorder on the canvas stream; check canRecord() first.
```js
globe.tour(cities, { dwell: 2600, zoom: 2.2 }); // cinematic auto-fly, returns { stop() }
globe.story(section, [ // scroll-linked rotation
{ at: 0, center: [0, 20], zoom: 1 },
{ at: 0.5, center: [72, 23], zoom: 3, markers: indiaMarkers, preset: "hologram" },
]);
await globe.record({ duration: 6000, filename: "globe.webm" }).promise;
```
--------------------------------
### Install canvas-globe with npm, pnpm, Yarn, Bun, or Deno
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Install canvas-globe with the package manager already used by your project. Supports npm, pnpm, Yarn, Bun, and Deno/JSR-aware projects.
```bash
# npm
npm install canvas-globe
# pnpm
pnpm add canvas-globe
# Yarn
yarn add canvas-globe
# Bun
bun add canvas-globe
# Deno and JSR-aware projects
deno add jsr:@swiftools/canvas-globe
npx jsr add @swiftools/canvas-globe
```
--------------------------------
### Live pings: one-shot ring and pingFeed
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Creates a one-shot expanding ring at a given latitude/longitude with an emoji and label, then starts a ping feed that repeats every 1800 ms and stops it. No backend is required.
```js
globe.ping({ lat: 52.52, lon: 13.4, emoji: "โจ", label: "Someone in Berlin just signed up" });
const feed = globe.pingFeed(events, { interval: 1800 });
feed.stop();
```
--------------------------------
### tour
Source: https://github.com/shree-hari/canvas-globe/blob/main/skills/canvas-globe/references/api-quick-reference.md
Starts a tour of the globe.
```APIDOC
## tour
### Description
Starts a tour of the globe.
### Method
tour
### Parameters
None.
### Response
No return value.
```
--------------------------------
### Load canvas-globe UMD from jsDelivr
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Use the versioned UMD build for a plain `
```
--------------------------------
### Scaffold a CanvasGlobe project with the React template
Source: https://github.com/shree-hari/canvas-globe/blob/main/packages/create-canvas-globe/README.md
Scaffolds a new CanvasGlobe project named my-globe using the React template. The CLI does not install dependencies or overwrite a non-empty directory.
```bash
npm create canvas-globe@latest my-globe -- --template react
```
--------------------------------
### Minimal JavaScript: createGlobe
Source: https://github.com/shree-hari/canvas-globe/blob/main/skills/canvas-globe/references/api-quick-reference.md
Minimal setup for the CanvasGlobe JavaScript API. Creates a globe instance with a license key, the "hologram" preset, and a single marker at coordinates (23.03, 72.58).
```js
import { createGlobe } from "canvas-globe";
const globe = createGlobe(document.querySelector("#globe"), {
licenseKey: import.meta.env.VITE_CANVAS_GLOBE_LICENSE_KEY,
preset: "hologram",
markers: [{ lat: 23.03, lon: 72.58, count: 12, live: true }],
});
```
--------------------------------
### Import canvas-globe from JSR in Deno
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Import the JSR release directly in Deno. The React entry point remains on npm so React's peer dependency is resolved by your existing application rather than installing a second React copy.
```js
import { createGlobe } from "jsr:@swiftools/canvas-globe@0.1.5";
```
--------------------------------
### Scaffold a starter project
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Scaffolds a complete starter project for React, Next.js, Vue, SvelteKit, Vanilla JavaScript, or Web Components. Run this command to generate a new project named my-globe.
```bash
npm create canvas-globe@latest my-globe
```
--------------------------------
### Create and switch globe scenes
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Creates a globe with the 'signups' scene preset, then switches to the 'coverage' scene at runtime. Switching scenes resets every key the new scene does not set, so nothing leaks between them.
```js
createGlobe(canvas, { scene: "signups" });
globe.setScene("coverage");
```
--------------------------------
### Minimal React: Globe component
Source: https://github.com/shree-hari/canvas-globe/blob/main/skills/canvas-globe/references/api-quick-reference.md
Minimal React component for CanvasGlobe. Uses the same license key, preset, and marker configuration as the JavaScript example.
```jsx
import { Globe } from "canvas-globe/react";
;
```
--------------------------------
### Create globe with markers, arcs, and gallery controls
Source: https://github.com/shree-hari/canvas-globe/blob/main/example/index.html
Initializes a globe with the 'hologram' preset, defines city markers, arcs from a hub, traffic-based country colors, and a tooltip. Also builds a gallery of preset thumbnails and syncs control buttons with the globe state.
```javascript
import { createGlobe, GeoGlobe, presets, scenes, exportPresets, fromCSV, mapAspect, colorScale } from "../src/index.js";
const cities = [
{ name: "Ahmedabad", lat: 23.03, lon: 72.58, count: 12, emoji: "๐งโ๐จ", live: true },
{ name: "London", lat: 51.5, lon: -0.12, count: 8, emoji: "๐ฉโ๐ป" },
{ name: "New York", lat: 40.71, lon: -74.01, count: 6, emoji: "๐งโ๐" },
{ name: "Tokyo", lat: 35.68, lon: 139.69, count: 4, emoji: "๐จโ๐ฌ" },
{ name: "Sรฃo Paulo", lat: -23.55, lon: -46.63, count: 3, emoji: "๐ฉโ๐ค" },
{ name: "Sydney", lat: -33.87, lon: 151.21, count: 2, emoji: "๐งโ๐พ" },
{ name: "Nairobi", lat: -1.29, lon: 36.82, count: 2, emoji: "๐จโ๐ณ" },
];
const hub = { lat: 23.03, lon: 72.58 };
const arcs = cities.slice(1).map((c, i) => ({ from: hub, to: c, duration: 2200 + i * 260 }));
const traffic = { IN: 940, US: 720, GB: 480, JP: 300, BR: 260, AU: 180, KE: 140, DE: 220, FR: 160, CA: 130 };
const scale = colorScale([0, 1000], ["#1e293b", "#a78bfa"]);
const trafficColors = Object.fromEntries(Object.entries(traffic).map(([k, v]) => [k, scale(v)]));
const canvas = document.getElementById("c");
const g = createGlobe(canvas, {
preset: "hologram",
markers: cities,
arcs,
tooltip: (target, kind) => {
if (kind === "country") return traffic[target.iso] ? `${target.name}: ${traffic[target.iso]} visits` : target.name;
if (kind === "cluster") return `${target.count} visitors nearby`;
return `${target.name} ยท ${target.count} visitors${target.live ? " ยท live" : ""}`;
},
onClick: (m) => g.flyTo(m.lon, m.lat),
onCountryHover: () => {},
onCountryClick: (shape) => console.log("country", shape.iso, shape.name),
});
window.g = g;
/* ------------------------------- gallery ------------------------------- */
const gallery = document.getElementById("gallery");
const thumbs = [];
for (const name of Object.keys(presets)) {
const card = document.createElement("button");
card.className = "card";
card.type = "button";
card.dataset.preset = name;
card.setAttribute("aria-pressed", String(name === "hologram"));
card.innerHTML = `${name}`;
gallery.appendChild(card);
thumbs.push(
createGlobe(card.querySelector("canvas"), {
preset: name,
autoRotate: false,
interactive: false,
keyboard: false,
center: { lon: 20, lat: 12 },
radiusRatio: 0.44,
markers: [],
}),
);
card.addEventListener("click", () => {
for (const c of gallery.children) c.setAttribute("aria-pressed", String(c === card));
g.setPreset(name);
syncControls();
});
}
/* ------------------------------- controls ------------------------------ */
const press = (el, on) => el.setAttribute("aria-pressed", String(on));
const syncAspect = () => {
canvas.classList.toggle("flat", g.o.mode === "map");
canvas.style.setProperty("--map-aspect", String(1 / mapAspect(g.o.latRange, g.o.projection)));
requestAnimationFrame(() => g.resize());
};
function syncControls() {
for (const b of document.querySelectorAll("[data-land]")) press(b, b.dataset.land === g.o.landStyle);
for (const b of document.querySelectorAll("[data-toggle]")) press(b, !!g.o[b.dataset.toggle]);
for (const b of document.querySelectorAll("[data-mode]")) press(b, g.o.mode === "globe");
for (const b of document.querySelectorAll("[data-projection]")) {
press(b, g.o.mode === "map" && g.o.projection === b.dataset.projection);
}
press(document.getElementById("choropleth"), g.o.countryColors === trafficColors);
press(document.getElementById("autoColors"), g.o.countryColors === "auto");
press(document.getElementById("arcs"), g.o.arcs.length > 0);
press(document.getElementById("texture"), !!g.o.texture);
press(document.getElementById("labels"), g.o.labels !== false);
press(document.getElementById("legendBtn"), !!g.o.legend);
press(document.getElementById("viewer"), !!g.o.showViewer);
for (const [id, value] of [["dotSpacing", g.o.dotSpacing], ["dotSize", g.o.dotSize], ["orbits", Array.isArray(g.o.orbits) ? 0 : g.o.orbits]]) {
const input =
```
--------------------------------
### Use UMD build with plain HTML
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Drop the UMD build on a page with no build step. The script loads the library, creates a canvas element, and initializes a globe with a marker.
```html
```
--------------------------------
### Load canvas-globe UMD from UNPKG
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Alternative CDN for the UMD build from UNPKG. Use the same versioned URL to avoid unexpected changes.
```html
```
--------------------------------
### CSS custom properties for globe theme
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Use with theme: "css" so the globe reads --geo-* custom properties off the canvas and inherits your design tokens. Overrides land, ocean gradient start, and ocean gradient end colors.
```css
#globe { --geo-land: #334155; --geo-ocean-from: #0f172a; --geo-ocean-to: #020617; }
```
--------------------------------
### Create a globe with canvas-globe
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Initializes a canvas-globe instance with markers, a theme, a tooltip formatter, and a click handler that flies to the clicked marker. The canvas element must be present in the DOM and sized via CSS.
```js
import { createGlobe } from "canvas-globe";
const globe = createGlobe(document.querySelector("#globe"), {
markers: [
{ lat: 23.03, lon: 72.58, count: 12, emoji: "๐งโ๐จ", live: true, city: "Ahmedabad" },
{ lat: 51.5, lon: -0.12, count: 8, emoji: "๐ฉโ๐ป", city: "London" },
],
theme: "atlas",
tooltip: (m) => `${m.city}: ${m.count} visitors`,
onClick: (marker) => globe.flyTo(marker.lon, marker.lat, { zoom: 2.5 }),
});
```
--------------------------------
### Initialize CanvasGlobe with a license key
Source: https://github.com/shree-hari/canvas-globe/blob/main/LICENSING.md
Pass the license key supplied with a commercial order to the createGlobe function. The key is a compliance reminder and does not replace or modify the license terms.
```javascript
createGlobe(canvas, {
licenseKey: "your_license_key",
});
```
--------------------------------
### Presets: createGlobe with preset and setPreset
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Creates a globe with the hologram preset, then switches to the neon preset at runtime. Presets bundle a theme with a render style; your own options always override them.
```js
createGlobe(canvas, { preset: "hologram" });
globe.setPreset("neon");
```
--------------------------------
### Create globe with markers and animated preset cycling
Source: https://github.com/shree-hari/canvas-globe/blob/main/example/readme-demo.html
Initializes a CanvasGlobe instance with markers and arcs, then defines a frame renderer that cycles through presets, adjusts orbits, and flies to animated coordinates. Used for the README demo animation; requires the canvas element with id 'globe' and the createGlobe import.
```javascript
import { createGlobe } from "../src/index.js";
const markers = [
{ name: "Ahmedabad", lat: 23.03, lon: 72.58, count: 12, live: true },
{ name: "London", lat: 51.5, lon: -0.12, count: 8 },
{ name: "New York", lat: 40.71, lon: -74.01, count: 6 },
{ name: "Tokyo", lat: 35.68, lon: 139.69, count: 4 },
{ name: "Sao Paulo", lat: -23.55, lon: -46.63, count: 3 },
];
const globe = createGlobe(document.querySelector("#globe"), {
preset: "hologram",
autoRotate: false,
markers,
arcs: markers.slice(1).map((city, index) => ({
from: markers[0],
to: city,
duration: 2200 + index * 260
})),
orbits: 2,
});
const names = ["hologram", "neon", "blueprint", "aurora", "political", "midnight"];
window.renderDemoFrame = async (frame) => {
const preset = names[Math.floor(frame / 8) % names.length];
globe.setPreset(preset);
globe.setOptions({ orbits: preset === "blueprint" || preset === "aurora" ? 3 : 1 });
globe.flyTo(-24 + frame * 7.5, 18 + Math.sin(frame / 6) * 8, { instant: true, zoom: 1.08 });
document.querySelector("#preset").textContent = preset;
globe.invalidate();
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
};
window.demoReady = true;
```
--------------------------------
### Set license key with createGlobe
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Pass the license key supplied with a commercial order. GPLv3-compatible projects can request a complimentary key through the licensing page. The default `0000-0000-000-0000` value is for evaluation only and produces a console warning in browser builds.
```js
createGlobe(canvas, { licenseKey: "your_license_key" });
```
--------------------------------
### Create choropleth with createGlobe and colorScale
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Creates a choropleth map with `createGlobe` and `colorScale`. Keys are matched case-insensitively against the ISO alpha-2 code, then the numeric id, then the country name. Pass `countryKey` if your data uses something else.
```js
import { createGlobe, colorScale } from "canvas-globe";
const visits = { IN: 940, US: 720, GB: 480, JP: 300 };
const scale = colorScale([0, 1000], ["#e0f2fe", "#0369a1"]);
createGlobe(canvas, {
mode: "map",
countryColors: Object.fromEntries(Object.entries(visits).map(([k, v]) => [k, scale(v)])),
onCountryClick: (shape) => console.log(shape.iso, shape.name),
});
```
--------------------------------
### Focus on a country and set country media
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Frame a country and attach media (video, GIF, image) inside its outline. Media is clipped to the bundled country outline including islands; .mp4/.webm become looping muted video, .gif animates, and anything drawImage accepts can be passed. fit mirrors CSS object-fit; opacity, blend, scale, and offset are available per country. focusOn zooms past maxZoom when needed, and without isolate neighbours stay visible at dim opacity.
```javascript
globe.focusOn("India", { isolate: true });
globe.setCountryMedia("India", "/launch-reel.mp4");
// or declaratively
createGlobe(canvas, {
mode: "map",
focus: { country: "IN", isolate: true, outlineWidth: 2 },
countryMedia: {
IN: { src: "/reel.mp4", fit: "cover" },
BR: "/photo.jpg",
},
});
```
--------------------------------
### Globe methods overview
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Lists the globe's public methods with their effects and return values. Includes methods for markers, arcs, modes, projections, presets, land styles, themes, time, options, zoom, flyTo, fitTo, focus, media, scene, export, timeline, ping, tour, story, record, locateViewer, texture, center, project, unproject, countryAt, snapshot, toBlob, invalidate, resize, and destroy.
```js
globe.setMarkers([...]); // swap the marker set
globe.setArcs([...]); // swap the arcs
globe.setMode("map"); // switch projection family
globe.setProjection("naturalEarth"); // switch flat projection
globe.setPreset("hologram"); // swap the whole look
globe.setLandStyle("dots"); // fill | dots | outline | glow
globe.setTheme("midnight"); // switch palette
globe.setTime(Date.UTC(2024, 5, 21)); // terminator clock; null tracks now
globe.setOptions({ autoRotate: false });
globe.setZoom(3);
globe.zoomBy(1.4);
globe.flyTo(139.69, 35.68); // ease to Tokyo
globe.flyTo(139.69, 35.68, { instant: true, zoom: 4 });
globe.fitTo([68, 6, 98, 36]); // frame [west, south, east, north]
globe.fitToMarkers(); // frame every marker
globe.focusOn("India", { isolate: true });
globe.setCountryMedia("India", "/reel.mp4");
globe.countryAspect("India"); // โ height / width ratio for the canvas
globe.clearFocus();
globe.setScene("logos"); // whole composition
globe.exportImage({ preset: "story", transparent: true });
await globe.exportBlob({ preset: "og" });
globe.setTimelineAt("2024-06-01");
globe.playTimeline({ duration: 6000 }); // โ { stop() }
globe.ping({ lat, lon, label }); // one-shot expanding ring
globe.ping(events, { interval }); // โ { stop() }
globe.tour(points, { dwell }); // โ { stop() }
globe.story(el, steps); // scroll-linked view
globe.record({ duration, filename }); // โ { promise, stop() }
globe.locateViewer(); // โ { lat, lon, timeZone, country, source, accuracy }
globe.setTexture(imageOrUrl);
globe.getCenter(); // โ { lon, lat }
globe.project(lon, lat); // โ { x, y } or null if behind the globe
globe.unproject(x, y); // โ [lon, lat] or null
globe.countryAt(x, y); // โ country shape or null
globe.snapshot(); // โ PNG data URL, great for share images
await globe.toBlob(); // โ Blob
globe.invalidate(); // request one more frame
globe.resize(); // usually automatic via ResizeObserver
globe.destroy(); // stop the loop and remove listeners
```
--------------------------------
### Parse CSV markers with fromCSV
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Use when marker data arrives as a spreadsheet. `fromCSV` resolves rows itself: explicit `lat`/`lon` columns first, then a city name, then a country code or name. City lookup covers roughly 300 major cities that ship with the package; pass `{ gazetteer: { Ahmedabad: [72.58, 23.03] } }` for anything else. Rows that cannot be placed are exposed via `markers.skipped` so you can report them.
```js
import { fromCSV } from "canvas-globe";
const markers = fromCSV(`city,count,image
London,8,/logos/acme.png
Tokyo,4,/logos/globex.png`);
markers.skipped; // rows that could not be placed, so you can report them
globe.setMarkers(markers).fitToMarkers();
```
--------------------------------
### Custom theme and land style: createGlobe with theme, landStyle, dotSpacing, orbits
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Creates a globe with the midnight theme, dot land style, 2.4-degree dot spacing, and three orbit rings. Dots are rasterised once into an off-screen bitmap and sampled as a grid, so the whole matrix draws in a single fill; orbit rings are real great circles that pass behind the globe.
```js
createGlobe(canvas, { theme: "midnight", landStyle: "dots", dotSpacing: 2.4, orbits: 3 });
```
--------------------------------
### Configure overlays and animate timeline
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Configures globe overlays: counter rolls when it changes, watermark is baked into every export, timeline hides later markers, and annotations place a marker at given coordinates. Then plays the timeline for 6000 ms with looping and pings a location with a burst of 18.
```js
createGlobe(canvas, {
counter: { value: 21947, label: "customers worldwide" }, // rolls when it changes
title: { text: "Trusted in 68 countries", subtitle: "Join 21,947 teams" },
watermark: { image: "/logo.svg", text: "acme.com" }, // baked into every export
annotations: [{ lat: 23.03, lon: 72.58, text: "HQ: Ahmedabad" }],
timeline: { at: "2024-06-01" }, // hides later markers
});
globe.playTimeline({ duration: 6000, loop: true }); // "our growth, animated"
globe.ping({ lat, lon, label: "10,000 users ๐", burst: 18 });
```
--------------------------------
### Canvas Globe demo presets and toolkit controls
Source: https://github.com/shree-hari/canvas-globe/blob/main/example/index.html
Defines the demo's preset actions: focusing on a country, restoring the globe view, toggling isolation, attaching webcam or animation media, and populating scene/export dropdowns. The webcam handler catches permission denial and temporarily shows "๐ท Denied" before reverting to "๐ท Webcam" after 1.8 seconds. The scene button switches the globe scene and toggles the flat map class based on the current mode.
```javascript
const frame = (opts = {}) => { g.setOptions({ mode: "map" }); canvas.classList.add("flat"); const aspect = g.countryAspect(focusTarget()) || 1; canvas.style.setProperty("--map-aspect", String(1 / Math.min(2.2, Math.max(0.55, aspect)))); g.resize(); const isolate = opts.isolate ?? isolating(); g.focusOn(focusTarget(), { padding: 0.86, outlineWidth: 2, isolate, dim: 1 }); syncControls(); }; const restoreView = () => { g.setOptions({ countryMedia: null, focus: null, zoom: 1, center: { lon: 10, lat: 20 } }); canvas.classList.remove("flat"); g.setMode("globe"); syncAspect(); press(isolateBtn(), false); press(document.getElementById("mediaCam"), false); syncControls(); }; document.getElementById("focusBtn").addEventListener("click", () => frame()); isolateBtn().addEventListener("click", (e) => { const on = !isolating(); press(e.currentTarget, on); frame({ isolate: on }); }); document.getElementById("mediaAnim").addEventListener("click", () => { g.setCountryMedia(focusTarget(), buildReel()); frame(); }); document.getElementById("mediaCam").addEventListener("click", async (e) => { try { const stream = await navigator.mediaDevices.getUserMedia({ video: { width: 960, height: 540 } }); g.setCountryMedia(focusTarget(), stream); frame(); press(e.currentTarget, true); } catch { e.currentTarget.textContent = "๐ท Denied"; setTimeout(() => (e.currentTarget.textContent = "๐ท Webcam"), 1800); } }); document.getElementById("mediaClear").addEventListener("click", restoreView); window.resetView = restoreView; /* -------------------------------- toolkit ------------------------------ */ const scenePick = document.getElementById("scenePick"); for (const name of Object.keys(scenes)) { const opt = document.createElement("option"); opt.value = name; opt.textContent = name; scenePick.appendChild(opt); } const exportPick = document.getElementById("exportPick"); for (const name of Object.keys(exportPresets)) { const opt = document.createElement("option"); opt.value = name; opt.textContent = `${name} ${exportPresets[name].join("ร")}`; exportPick.appendChild(opt); } document.getElementById("sceneBtn").addEventListener("click", () => { g.setScene(scenePick.value); canvas.classList.toggle("flat", g.o.mode === "map"); syncAspect(); syncControls(); }); // A generated logo, so the demo needs no external asset.
```
--------------------------------
### Render CanvasGlobe with license key and markers
Source: https://github.com/shree-hari/canvas-globe/blob/main/packages/react-canvas-globe/README.md
Use the CanvasGlobe component with a license key from an environment variable, the hologram preset, and a marker at coordinates (23.03, 72.58) with count 12 and live true. The license key is required for proprietary use.
```jsx
import { CanvasGlobe } from "react-canvas-globe";
export default function AudienceGlobe() {
return (
);
}
```
--------------------------------
### Show viewer location with showViewer
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Show the viewer's location on the map. No permission prompt, no network call, no API key, instantly. It reads Intl.DateTimeFormat().resolvedOptions().timeZone and maps it to the coordinate the IANA database publishes for that zone; legacy aliases resolve too.
```javascript
createGlobe(canvas, { showViewer: true });
```
--------------------------------
### fitTo
Source: https://github.com/shree-hari/canvas-globe/blob/main/skills/canvas-globe/references/api-quick-reference.md
Fits the globe view to a given bounds.
```APIDOC
## fitTo
### Description
Fits the globe view to a given bounds.
### Method
fitTo
### Parameters
- **bounds** (unknown) - Required - Bounds to fit.
### Response
No return value.
```
--------------------------------
### setPreset
Source: https://github.com/shree-hari/canvas-globe/blob/main/skills/canvas-globe/references/api-quick-reference.md
Sets the visual preset of the globe.
```APIDOC
## setPreset
### Description
Sets the visual preset of the globe.
### Method
setPreset
### Parameters
- **preset** (string) - Required - Visual preset: `atlas`, `midnight`, `mono`, `political`, `hologram`, `neon`, `blueprint`, `aurora`, `noir`, or `constellation`.
### Response
No return value.
```
--------------------------------
### Export globe image with exportImage and exportBlob
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Render the globe at whatever size the destination wants without touching the live canvas. `exportImage` returns a data URL; `exportBlob` returns a blob. Presets include `square`, `story`, `portrait`, `wide`, `linkedin`, `og`, `twitter`, and `thumbnail`.
```js
globe.exportImage({ preset: "story" }); // 1080ร1920 data URL
globe.exportImage({ preset: "linkedin", transparent: true });
await globe.exportBlob({ width: 2400, height: 1260 });
```
--------------------------------
### snapshot
Source: https://github.com/shree-hari/canvas-globe/blob/main/skills/canvas-globe/references/api-quick-reference.md
Takes a snapshot of the current globe view.
```APIDOC
## snapshot
### Description
Takes a snapshot of the current globe view.
### Method
snapshot
### Parameters
None.
### Response
Returns a snapshot image.
```
--------------------------------
### Import canvas-globe from ESM CDN
Source: https://github.com/shree-hari/canvas-globe/blob/main/README.md
Import the package through an ESM CDN in modern browsers. Pin an exact version in production so a future release cannot change a deployed page unexpectedly.
```js
import { createGlobe } from "https://esm.sh/canvas-globe@0.1.5";
```
--------------------------------
### Globe preset controls with markers, timeline, and export
Source: https://github.com/shree-hari/canvas-globe/blob/main/example/index.html
Interactive globe controls: toggles logos, counter, annotations, country text, bursts, timeline, exports, and CSV marker placement. Uses g.setMarkers, g.setOptions, g.ping, g.playTimeline, g.exportImage, and g.fitToMarkers. Handles cleanup on beforeunload.
```javascript
const swatch = (text, hue) => { const c = document.createElement("canvas"); c.width = c.height = 128; const x = c.getContext("2d"); x.fillStyle = `hsl(${hue} 70% 45%)`; x.fillRect(0, 0, 128, 128); x.fillStyle = "#fff"; x.font = "700 62px Inter,system-ui,sans-serif"; x.textAlign = "center"; x.textBaseline = "middle"; x.fillText(text, 64, 68); return c; }; document.getElementById("logos").addEventListener("click", (e) => { const on = e.currentTarget.getAttribute("aria-pressed") !== "true"; press(e.currentTarget, on); g.setMarkers(on ? cities.map((c, i) => ({ ...c, emoji: undefined, image: swatch(c.name[0], i * 47) })) : cities); }); document.getElementById("counterBtn").addEventListener("click", (e) => { const on = !g.o.counter; press(e.currentTarget, on); g.setOptions({ counter: on ? { value: 14208, label: "customers worldwide" } : null }); if (on) setTimeout(() => g.setOptions({ counter: { value: 21947, label: "customers worldwide" } }), 900); }); document.getElementById("annotate").addEventListener("click", (e) => { const on = !g.o.annotations; press(e.currentTarget, on); g.setOptions({ annotations: on ? [ { lat: 23.03, lon: 72.58, text: "HQ: Ahmedabad" }, { lat: 51.5, lon: -0.12, text: "EU office", dx: -70, dy: -40 }, ] : null, }); }); document.getElementById("shapeText").addEventListener("click", (e) => { const on = e.currentTarget.getAttribute("aria-pressed") !== "true"; press(e.currentTarget, on); g.setCountryMedia(focusTarget(), on ? { text: "HELLO", color: "#ffffff", background: "#6d28d9" } : null); if (on) frame(); }); document.getElementById("burst").addEventListener("click", () => { g.ping({ lat: 23.03, lon: 72.58, label: "10,000 users ๐", burst: 18, duration: 2600 }); }); let timeline = null; document.getElementById("timelineBtn").addEventListener("click", (e) => { if (timeline) { timeline.stop(); timeline = null; g.setTimelineAt(null); press(e.currentTarget, false); return; } press(e.currentTarget, true); const dated = cities.map((c, i) => ({ ...c, date: new Date(2024, i * 1.5, 1).toISOString() })); g.setMarkers(dated); timeline = g.playTimeline({ duration: 5000, loop: true }); }); const download = (href, name) => { const a = document.createElement("a"); a.href = href; a.download = name; a.click(); }; document.getElementById("exportBtn").addEventListener("click", () => { download(g.exportImage({ preset: exportPick.value }), `globe-${exportPick.value}.png`); }); document.getElementById("exportAlpha").addEventListener("click", () => { download(g.exportImage({ preset: exportPick.value, transparent: true }), `globe-${exportPick.value}-alpha.png`); }); document.getElementById("csvBtn").addEventListener("click", () => { const markers = fromCSV(document.getElementById("csv").value); g.setMarkers(markers); g.fitToMarkers(); document.getElementById("csvOut").textContent = `${markers.length} placed${markers.skipped.length ? `, ${markers.skipped.length} skipped` : ""}`; }); syncControls(); addEventListener("beforeunload", () => { g.destroy(); thumbs.forEach((t) => t.destroy()); });
```