mappo — dotted world map web component

Mappo is the dotted world map that sits beautifully in any hero, section, or website page — one zero-dependency ES module, 28 KB gzipped. No build step: drop it in with a plain script tag, an import map, or npm. Land dots at any resolution, city markers by name, flat map or rotating globe, six animations, tilt, hover, clicks.

Hover the dots. Click a city.

Use it in 10 seconds

<script type="module" src="https://unpkg.com/mappo"></script>

<world-map cities="London, Lagos, Singapore" tilt="40" animation="wave"></world-map>

<!-- or the whole planet, spinning and grabbable -->
<world-map mode="globe"></world-map>

That's the entire integration — one ES module, no bundler required, 28 KB gzipped (91 KB raw) with the whole world included. Attributes are live: change one, the map re-renders. Import maps (Rails importmap-rails included) and npm get the same file:

<script type="importmap">
{ "imports": { "mappo": "https://unpkg.com/mappo" } }
</script>
<script type="module">import "mappo"</script>

Or npm install mappo and import "mappo" from your bundle — though the whole point is that you don't need one.

Why this one

Dots are derived, not drawn

A ~22 KB packed land bitmask (Natural Earth, public domain) generates the grid at whatever resolution you ask for. Resolution is a slider, not a redesign.

Cities by name

A built-in registry of ~160 world cities: cities="Nairobi, Seoul" just works. Coastal coordinates snap to the nearest land dot. Custom { name, lat, lon } entries mix in freely.

Animations that count

Six animation modes — a thin rolling wave, two-octave perlin noise, radial ripple, sonar sweep, scattered sparkle — surface displacement that reads as height under tilt, on the flat map and the globe alike. Plain-language knobs: period, height, width.

Performance, measured

Differential updates (color/animation changes never rebuild geometry), adaptive debouncing tuned to your machine's real frame cost, and a load gate for high densities. Budgeted and regression-tested — see the repo's perf harness.

Real elements, your CSS

The flat map is SVG in light DOM: every dot hoverable, every marker focusable, everything restylable from your stylesheet. Events bubble as worldmap:cityclick & friends in both modes — the globe re-earns them with canvas hit-testing. Or pass callbacks to the JS API.

Zero everything

No dependencies, no build step, no network calls, no API keys, no tracking. One 28 KB ES module — script tag, import map, or npm. prefers-reduced-motion respected. MIT.

Pointing at places new in 0.4

A globe that faces a country, drops a real map pin on it, and lights up the whole territory — three attributes, no geo library:

<world-map mode="globe" rotate-speed="0"
  focus="46.5,2.35"                 <!-- start facing this point; 0 speed holds it -->
  markers="France@48.86,2.35"       <!-- coordinate pins, no gazetteer -->
  marker-shape="pin"                <!-- the map-pin silhouette, anchored at its tip -->
  highlight-color="#8fabe0"
  highlight-polygon='[[[51.1,2.5],[50.4,1.5], …]]'  <!-- your rings; every dot inside glows -->
></world-map>

highlight-polygon takes JSON rings of [lat, lon] pairs — mappo ships no boundary data on purpose; feed it Natural Earth shapes (a country compacts to 1–3 KB at dot-grid resolution). The point-in-polygon test runs once per geometry build, never per frame.

The attributes

attributewhat it does
citiesComma-separated registry names — markers appear where you say. (JS API also takes custom coordinates.)
colsResolution: dots across the full longitude span (auto: 120 flat · 170 globe; max 260).
mode / rotate-speed / globe-ringflat (default) · globe — a rotating, grabbable canvas sphere (drag to spin, flick for momentum). tilt becomes the axial tilt; speed in degrees per second (0 parks it); globe-ring="true" adds a hairline halo (off by default).
ocean-color / backgroundWater cells as filler dots in their own shade (off-white, off-dark…) · a uniform fill behind everything (flat rect / globe disc). Both default to none.
dot-shape / dot-color / dot-sizecircle · square · triangle · custom SVG path; any color; size as a fraction of a cell. Hover color auto-derives as a shade of dot-color (override: dot-hover-color / dot-hover-scale).
marker-shape / marker-color / marker-pulsecircle · square · triangle · pin (map-pin silhouette, anchored at its tip); any color; pulse is a radar ping (expanding fading ring).
markersCoordinate pins without the registry: "Name@lat,lon;…" — same pipeline and events as cities.
focus"lat,lon" the globe starts facing — with rotate-speed 0 it holds there; pair with tilt for latitude.
highlight-polygon / highlight-colorJSON rings of [lat, lon] — every land dot inside draws in the highlight colour (the whole country glows). You supply the shape; globe mode.
tilt / rotate / perspectiveThe lying-down hero look — degrees of rotateX/rotateZ, perspective in px.
animationnone · wave · noise · ripple · sweep · sparkle.
animation-period / -height / -widthSeconds per cycle · crest height in cells · crest thinness as a fraction of the cycle.
interactive / cursor / marker-cursorTurn pointer behavior off for pure decoration; set cursors.

Full API — the WorldMap class, callbacks, low-level exports like isLand(lat, lon) and the city registry — in the README.