Skip to main content

Where our customers are

:::note Example data All customer counts and locations on this page are fictional demonstration data. They are not CanvasGlobe usage claims. :::

The single most requested marketing map: proof that people outside your own postcode pay you. Two layers do the work: countries shaded by volume so the eye reads the spread instantly, and city bubbles sized by count so the density is honest.

Loading globe…

The code

import { createGlobe, colorScale } from "canvas-globe";

// Whatever your analytics already returns.
const byCountry = { IN: 412, US: 388, GB: 214, DE: 168, CA: 96, BR: 84 /* … */ };
const cities = [
{ city: "Ahmedabad", lat: 23.03, lon: 72.58, count: 214 },
{ city: "London", lat: 51.5, lon: -0.12, count: 164 },
// …
];

const scale = colorScale([0, 420], ["#ede9fe", "#4c1d95"]);

const globe = createGlobe(canvas, {
mode: "map",
preset: "political",
projection: "naturalEarth",

countryColors: Object.fromEntries(
Object.entries(byCountry).map(([iso, n]) => [iso, scale(n)]),
),

markers: cities,
markerStyle: "bubble",
markerScale: 0.9,

legend: { title: "Customers", scale: { domain: [0, 420], range: ["#ede9fe", "#4c1d95"] } },
title: { text: `Used by teams in ${Object.keys(byCountry).length} countries` },

tooltip: (m) => `${m.city}: ${m.count} customers`,
onCountryClick: (shape) => filterTable(shape.iso),
});

Why these choices

Map, not globe. A globe hides half the world at any moment. For a count: "we are in 68 countries": hiding half of them undercuts the claim. Use the globe when the motion is the message; use the map when the coverage is.

naturalEarth over mercator. Mercator inflates Europe and North America and shrinks everywhere near the equator. If a chunk of your customer base is in India, Brazil, Nigeria or Indonesia, Mercator makes your business look smaller than it is. Natural Earth is the polite default. See Projections.

Two layers, one story. Countries answer where, bubbles answer how many. Either alone leaves a question open, and a viewer with an open question scrolls.

A title on the canvas, not in a <h2>. It costs the same to draw and it means exportImage() gives you the finished asset for a deck, a tweet or an email: no design tool in the loop. See Social share card generator.

Joining data you already have

Country codes rarely arrive in the shape you want. Keys are matched case-insensitively against ISO alpha-2, then the numeric id, then the country name: so most joins need no mapping at all:

countryColors: { in: "#4c1d95", "United States": "#5b21b6", 826: "#6d28d9" }

If your warehouse speaks alpha-3, map it once:

const alpha2 = { IND: "IN", USA: "US", GBR: "GB" /* … */ };
countryColors: Object.fromEntries(rows.map((r) => [alpha2[r.iso3], scale(r.total)])),

For a spreadsheet export with city names and no coordinates, fromCSV resolves around 300 bundled cities plus every country, and hands back a skipped list so you know exactly what did not match:

import { fromCSV } from "canvas-globe";

const markers = fromCSV(csvText); // reads city / country / count columns
if (markers.skipped.length) console.warn("Unresolved rows:", markers.skipped);
globe.setMarkers(markers);

Keeping it honest

A map of customers is a claim, and claims get checked.

  • Do not plot one customer as a bubble the size of a country. markerScale is a multiplier on a square-root scale, so a city with 200 customers is already only ~4.5× the radius of one with 10. Resist the urge to exaggerate it.
  • Say what a dot is. "Customers", "signups", "trial accounts" and "waitlist entries" are wildly different numbers. Put the word in the legend title.
  • Do not scatter fake dots to fill empty ocean-facing continents. Anyone who works in sales at your company will notice, and they are the people who repost it.

Where it goes

PlacementChange
Above the foldDrop the legend, keep the title, cap it at ~420 px tall
Pricing pageColour by plan availability instead of volume
Sales deckexportImage({ width: 2400, height: 1260 })
Investor updateAdd playTimeline to animate the last four quarters
EmailExport a PNG: no email client will run canvas

Next