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
| attribute | what it does |
|---|---|
| cities | Comma-separated registry names — markers appear where you say. (JS API also takes custom coordinates.) |
| cols | Resolution: dots across the full longitude span (auto: 120 flat · 170 globe; max 260). |
| mode / rotate-speed / globe-ring | flat (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 / background | Water 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-size | circle · 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-pulse | circle · square · triangle · pin (map-pin silhouette, anchored at its tip); any color; pulse is a radar ping (expanding fading ring). |
| markers | Coordinate 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-color | JSON 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 / perspective | The lying-down hero look — degrees of rotateX/rotateZ, perspective in px. |
| animation | none · wave · noise · ripple · sweep · sparkle. |
| animation-period / -height / -width | Seconds per cycle · crest height in cells · crest thinness as a fraction of the cycle. |
| interactive / cursor / marker-cursor | Turn 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.
