npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

umami-maps

v0.1.0

Published

Free open-source ClustrMaps / RevolverMaps alternative: a 3D visitor globe widget powered by your own Umami analytics data.

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-maps

1. 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.html

Migrating 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!

License

MIT © Selenium39