=============== 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()); }); ```