Build a MapLibre style from four anchor colours. Self-hosted maps, no API key.
Runs on your machine from its package; the steps are below.
Not assessed yet.
Odel charges nothing per call. The publisher may charge for its own service.
Runs locally over stdio
This server isn't hosted — your MCP client launches it from a package registry. Use one of the commands below, or drop the config into your client (e.g. Claude Desktop).
Run
npx -y @mmj-map/palette
MCP client config
{
"mcpServers": {
"mmj-palette": {
"command": "npx",
"args": [
"-y",
"@mmj-map/palette"
]
}
}
}Modern Map — put a real map on your page, and own it. No API key. No per-view billing. No tile server to run.
Open the live demo · press play on the one above · Install guide · 日本語
Everything on this page is static files on GitHub Pages. There is no backend behind any of it — including the animation above.
Eight regions you can switch between — Osaka, Hanoi, Ho Chi Minh City, New York, Singapore, Shanghai, Taipei, Seoul — and seventeen demo pages in all.
You are billed per view. The better your site does, the worse the graph looks, and the number is not yours to cap. MMJ has no meter — you cut the area you care about once (10–60 MB for a city, under a minute) and serve it as a file. Traffic costs what your static host charges for a file, which for most sites is nothing.
A hall, a basement, a festival site, a disaster drill. GPS still works without a network — positioning comes from satellites, not from your carrier. The only missing piece was that the map itself was behind a network call.
One of the demo pages keeps working with the network switched off: 1.9 MB of tiles + 562 KB of glyphs + 1.15 MB of renderer. That works because a venue is small — the whole of Japan could not do the same.
Your shops, your facilities, your inspection sites. You want them styled like your site, labelled in your users' language, with your photos in the popup — and you do not want to publish your point list to anyone to get that.
Your data never leaves your hosting. The components read the GeoJSON you serve, and MMJ has no delivery origin to send it to.
One command and a copy-paste. Cut your own town, then put it on a page.
npx @mmj-map/tiles extract osaka --bbox=135.4,34.6,135.6,34.8
Cutting from the current upstream build 20260924.pmtiles (basemap 4.15.2).
Done: ./tiles/osaka.20260924.pmtiles
9.8 MB — fits on GitHub Pages (100 MB per file, 1 GB per site).
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@6.12.0/dist/maplibre-gl.css">
<style>mmj-map { display: block; height: 70vh; }</style>
<mmj-map
tiles="./tiles/osaka.20260924.pmtiles"
style-url="./styles/modern-dark.json"
center="135.5023,34.6937"
zoom="12">
<mmj-marker lnglat="135.4959,34.7024" popup="Umeda"></mmj-marker>
</mmj-map>
<script type="module">
import * as maplibregl from "https://cdn.jsdelivr.net/npm/maplibre-gl@6.12.0/dist/maplibre-gl.mjs";
window.maplibregl = maplibregl;
</script>
<script src="https://cdn.jsdelivr.net/npm/pmtiles@4.4.0/dist/pmtiles.js"></script>
<script type="module" src="./elements/index.js"></script>
center is longitude,latitude — the same order as GeoJSON, not the order you say out loud.
Eight elements ship in the package: mmj-map, mmj-marker, mmj-poi, mmj-cluster,
mmj-route, mmj-circle, mmj-raster, mmj-fill. All of them are plain ESM; there is
nothing to compile.
| Components | @mmj-map/elements |
| Tile extractor | @mmj-map/tiles |
| Full install guide | English · 日本語 · 繁體中文 · 简体中文 · Tiếng Việt |
| Every attribute, in one fetch | https://meta-taro.github.io/mmj-map/llms-full.txt |
Status: usable, early. There are no hosted tiles — you cut your own, which is the point. There are no Japanese glyphs, so CJK labels fall back to the viewer's font and the letterforms change per machine. The six styles are proposals, not an approved palette. See
PRD.mdfor scope.
There are two complaints about maps, and they point in different directions.
And almost nobody needs the whole planet. You need the area your site is about, and that you can hold yourself — 10–60 MB for a city, under a minute to cut. Because you are not serving the world, you do not need a server.
MMJ solves those three at once. We borrow the data and own the presentation.
The remaining alternatives each have a catch:
So the map you can actually ship ends up being someone else's map, with someone else's look, on someone else's meter.
Saying the default look is not good is not a complaint about the commons. This map stands on the people and organisations who survey, fund and edit OpenStreetMap.
CREDITS.mdnames them. Owning the presentation is how we take that part on ourselves.
That is a fair objection, and for the data it is simply correct. If a road is wrong, the place to fix it is OpenStreetMap, not this repository.
But the look is not the kind of thing you fix upstream. The default OSM style has to answer every use on earth with one design. Anything that answers everything is optimal for nothing. That is a difference in role, not a defect.
What MMJ carries is the part that should never go upstream:
Every one of those is one site's business, and none of it is data. Data improvements go to OSM. Presentation belongs to each site. That is the line.
Starting from zero, four things stand between you and one working map. MMJ removes them.
| What you skip | What it costs you otherwise |
|---|---|
| Working out the rights | The OSM Tile Usage Policy rules out commercial and heavy-traffic use, and dropping the ODbL attribution from the screen is a breach |
| Learning how serving works | Some hosts answer Range with 200. The map still draws, so you cannot see it — but every tile pulls the whole archive. MMJ ships a check for exactly this (npx @mmj-map/tiles check-range <url>) |
| Learning how upstream works | Daily builds disappear. Ours vanished nine days after we pinned it (measured 2026-09-24). Without a pin and a fallback, one morning you simply cannot cut tiles any more |
| Hunting for a look | Six hand-written styles. That whole search is gone |
There are more, and they are all the kind you find out about by stepping on them.
name:zh does not exist — it is zh-Hant / zh-Hans. Guessing gets you nothing.
MMJ switches with one attribute (lang)You could work all of this out. The problem is that you work it out afterwards.
Data we borrow. Presentation we own.
Anywhere Protomaps covers, which is the planet. Nothing in the tooling is Japan-specific: point the extractor at a bounding box or a polygon and you get tiles.
Japan is simply the region whose styling is furthest along. Taiwan, Shanghai, Vietnam and Singapore are next (D-018). What is per-region is the style, not the code.
Two licences, and they do not overlap.
| Part | Licence |
|---|---|
| Code, styles, UI components | MIT (LICENSE) |
| Base map data (OpenStreetMap) | ODbL 1.0 — attribution required on screen |
Read LICENSES.md before you ship. Short version: keep
© OpenStreetMap contributors visible, and your own data layered on top stays yours.
docs/install/ — how to put a map on your site (English, 日本語, 繁體中文, 简体中文, Tiếng Việt)docs/tiles/README.md — build the base tiles yourself (public data and public tools only; written in Japanese)docs/elements/README.md — every attribute of every componentPRINCIPLES.md — what this project is for, how it chooses data, who is welcomeCREDITS.md — the work this map stands onPRD.md — scope, and what we deliberately do not builddocs/decisions.md — decisions and the reasoning behind themdocs/origin/ — the original proposal, in Japanese, uneditedREADME.ja.md — 日本語See CONTRIBUTING.md. Issues in Japanese or English are both fine.