Sizing the canvas
This is the single most common source of "it looks squashed" or "it looks blurry". It takes one minute to get right and then never bothers you again.
The rule
Size the canvas in CSS. Never set the width/height attributes yourself.
CanvasGlobe reads clientWidth/clientHeight, multiplies by the device pixel ratio (capped at 2),
and sets the backing store to match. A ResizeObserver repeats that whenever the element changes
size.
#globe { width: 100%; max-width: 520px; aspect-ratio: 1; }
Globe mode
A globe is round. Use a square canvas:
aspect-ratio: 1;
The sphere fills radiusRatio (default 0.4) of the smaller side,
so a non-square canvas just adds empty space rather than distorting anything.
Map mode
A flat map is not square, and the right ratio depends on both the projection and the latitude window. Ask the library:
import { mapAspect } from "canvas-globe";
mapAspect(); // 0.386: equirectangular, default latRange
mapAspect([83, -56], "mercator"); // 0.633
mapAspect([90, -90]); // 0.5: full-height equirectangular
mapAspect returns height ÷ width, so CSS aspect-ratio wants its reciprocal:
canvas.style.aspectRatio = String(1 / mapAspect(latRange, projection));
/* equirectangular with the default latRange */
#map { width: 100%; aspect-ratio: 360 / 139; }
:::info Letterboxing, not stretching If the canvas ratio does not match the projection, CanvasGlobe fits the map uniformly and centres it. The map is never distorted: you just get empty space at the sides or top. :::
Framing a single country
A country has its own shape, so neither square nor world-map ratios suit it.
countryAspect() gives you the right one:
const ratio = globe.countryAspect("India"); // height / width
canvas.style.aspectRatio = String(1 / ratio);
globe.resize();
globe.focusOn("India", { isolate: true });
Retina and DPR
The backing store is cssSize × min(2, devicePixelRatio). Capping at 2 keeps 3× phones from paying
for nine times the pixels with no visible gain.
You never deal with this directly: project() and
unproject() both speak CSS pixels, as do the positions handed to
onHover and onClick.
Resizing
ResizeObserver handles layout changes automatically. Call resize()
manually only when you change the canvas size in a way the observer cannot see: for example right
after switching the CSS aspect-ratio in the same frame:
canvas.classList.add("flat");
requestAnimationFrame(() => globe.resize());
Common mistakes
| Symptom | Cause | Fix |
|---|---|---|
| Blurry on a phone or retina screen | width/height attributes set by hand | Size it in CSS only |
| Map looks squashed | Canvas ratio does not match the projection | Use mapAspect() |
| Canvas has zero height | A parent with display: flex and no height | Give the canvas an aspect-ratio |
| Nothing renders | Canvas is display: none at construction | Construct it when visible, or call resize() after showing it |