umami-maps
v0.1.0
Published
Free open-source ClustrMaps / RevolverMaps alternative: a 3D visitor globe widget powered by your own Umami analytics data.
Maintainers
Readme
UmamiMaps — free ClustrMaps & RevolverMaps alternative
A free, open-source 3D visitor globe widget for your website, powered by your own Umami analytics data.
ClustrMaps is down. RevolverMaps shut down in late 2024. If you're looking for a ClustrMaps alternative or a RevolverMaps replacement — a rotating globe (visitor counter / hit map) that shows where your website visitors come from — this is it. Unlike those hosted services, UmamiMaps doesn't add yet another tracker to your site: it reuses the analytics you already collect with Umami, and it can never "go down" because you host it yourself.
- 🌍 Rotating WebGL globe (cobe, ~5KB) with visitor markers per country
- 🔌 Works with self-hosted Umami and Umami Cloud
- 🔒 Credentials stay on your server — the browser only sees aggregated JSON
- 🧩 Use it as a React component, a vanilla JS function, or a plain
<script>tag - 🌓 Light & dark themes, fully customizable colors
- 🪶 Zero extra tracking, zero cookies, zero third-party requests
How it works
Browser widget ──fetch──▶ /api/visitor-globe (your server) ──▶ Umami API
(cobe globe) (proxy + cache, keeps secrets) (country metrics)You add one small server endpoint (so your Umami credentials never reach the browser) and drop the widget anywhere on your site.
Install
npm install umami-maps1. Server endpoint
Next.js (App Router)
// app/api/visitor-globe/route.ts
import { createGlobeHandler } from "umami-maps/server";
export const dynamic = "force-dynamic";
export const GET = createGlobeHandler({
umamiUrl: process.env.UMAMI_API_URL!, // e.g. https://umami.example.com
websiteId: process.env.UMAMI_WEBSITE_ID!,
// Self-hosted:
username: process.env.UMAMI_USERNAME,
password: process.env.UMAMI_PASSWORD,
// Or Umami Cloud (umamiUrl: "https://api.umami.is"):
// apiKey: process.env.UMAMI_API_KEY,
});createGlobeHandler returns a standard () => Promise<Response>, so the same code works in Hono, SvelteKit, Remix, Bun, Deno, etc.
Express
import express from "express";
import { createUmamiMapsClient } from "umami-maps/server";
const app = express();
const client = createUmamiMapsClient({
umamiUrl: process.env.UMAMI_API_URL!,
websiteId: process.env.UMAMI_WEBSITE_ID!,
username: process.env.UMAMI_USERNAME,
password: process.env.UMAMI_PASSWORD,
});
app.get("/api/visitor-globe", async (_req, res) => {
res.json(await client.getGlobeData());
});2. The widget
React
import { UmamiGlobe } from "umami-maps/react";
<UmamiGlobe endpoint="/api/visitor-globe" size={250} />
<UmamiGlobe dark markerColor={[0.2, 0.8, 1]} />Vanilla JS
import { renderUmamiGlobe } from "umami-maps";
const globe = renderUmamiGlobe(document.getElementById("globe")!, {
endpoint: "/api/visitor-globe",
size: 250,
dark: true,
});
// later: globe.destroy();Plain <script> tag (no build step)
<div id="visitor-globe"></div>
<script
src="https://unpkg.com/umami-maps/dist/embed.global.js"
data-endpoint="/api/visitor-globe"
data-target="#visitor-globe"
data-size="250"
data-dark="false"
></script>Options
Server (createGlobeHandler / createUmamiMapsClient)
| Option | Default | Description |
| --- | --- | --- |
| umamiUrl | — | Base URL of your Umami instance (https://api.umami.is for Cloud) |
| websiteId | — | Umami website ID |
| username / password | — | Self-hosted credentials |
| apiKey | — | Umami Cloud API key |
| startAt | 0 | Aggregation window start (ms timestamp). 0 = all-time, like ClustrMaps |
| cacheTtlMs | 1800000 | In-memory cache TTL (30 min) |
| minMarkerSize / maxMarkerSize | 0.025 / 0.1 | Marker size range (visitor counts are log-scaled between them) |
Widget (renderUmamiGlobe / <UmamiGlobe /> / data-*)
| Option | Default | Description |
| --- | --- | --- |
| endpoint | /api/visitor-globe | JSON endpoint URL |
| size | 250 | Globe size in px |
| dark | false | Dark color scheme |
| rotateSpeed | 0.004 | Rotation speed, 0 disables |
| baseColor / markerColor / glowColor | theme defaults | [r, g, b] each 0–1 |
| caption | true | "N visitors from M countries" caption; pass a function to customize |
| data | — | Provide GlobeData directly and skip fetching |
Demo
npm install && npm run build && npm run demo
# open http://localhost:4173/demo/index.htmlMigrating from ClustrMaps or RevolverMaps
For years, ClustrMaps and RevolverMaps were the way to put a visitor globe on a blog or website. RevolverMaps announced "RevolverMaps has shut down" in late 2024, and ClustrMaps has been unreachable since 2026 — leaving millions of embedded widgets blank and years of visitor history gone.
UmamiMaps is built so that can't happen again:
| | ClustrMaps / RevolverMaps | UmamiMaps | | --- | --- | --- | | Hosting | Third-party service (now offline) | Your own server — can't shut down on you | | Tracking | Their own tracking script + cookies | None — reuses your existing Umami data | | Your data | Locked in their database, lost at shutdown | Lives in your Umami database | | Privacy | Third-party requests on every page view | No third-party requests, no cookies | | Rendering | Flash / heavy JS applet | Lightweight WebGL (~5KB cobe) | | Customization | Limited presets | Colors, size, speed, caption — all yours | | Price | Free with branding / paid tiers | Free & open source (MIT) |
To migrate, remove the dead clustrmaps.com/globe.js or rf.revolvermaps.com script tag, install Umami if you don't have it yet (it's free and self-hostable), and follow the two steps above. Your globe will repopulate from your Umami history — if Umami was already running, you keep your accumulated visitor stats instead of starting from zero.
FAQ
Is there an alternative to ClustrMaps?
Yes — UmamiMaps gives you the same cumulative visitor globe ClustrMaps was known for, rendered from your own Umami analytics. Since startAt defaults to 0, the globe shows your all-time visitor history, just like the old ClustrMaps counter.
RevolverMaps shut down — what's the replacement? UmamiMaps replicates the RevolverMaps experience (rotating 3D globe, visitor dots, live counter caption) without depending on any hosted service. It's the closest open-source successor to the RevolverMaps globe widget.
Why do I need a server endpoint? The Umami API requires authentication. Putting credentials in the browser would let anyone read your full analytics. The endpoint proxies only the aggregated, non-sensitive numbers (country → visitor count) and caches them.
Does it work with Umami Cloud?
Yes — set umamiUrl: "https://api.umami.is" and pass apiKey.
Does it work on WordPress, Blogger, Hugo, or static sites?
The widget itself is a plain <script> tag, so yes — anywhere you can paste HTML. You just need somewhere to run the small JSON endpoint (a serverless function on Vercel/Netlify/Cloudflare Workers is plenty).
Where does the per-country data come from?
Umami already geolocates visitors. UmamiMaps aggregates metrics?type=country and maps ISO country codes to coordinates with a built-in centroid table. No extra tracking happens.
Showcase
UmamiMaps was built for and is live on FWFW — Find Websites From World, a directory for discovering great websites around the globe. Using UmamiMaps on your site? Open a PR to add yours here!
