# MapToPoster JS, a browser poster generator that never sends your map off the machine

> MapToPoster JS turns a searched location into a printable map poster, using Leaflet for raster tiles and MapLibre GL for procedural vector themes, with a client-side export pipeline that can reach 50,000 pixels wide. Tile imagery, geocoding and routing are fetched from third-party services.

**dimartarmizi/map-to-poster** — MapToPoster JS is a client-side web app that turns any location into a high-resolution, customizable map poster with various themes, layouts, and export options.

- Repository: https://github.com/dimartarmizi/map-to-poster
- Website: https://maptoposter.tarmizi.id
- Stars: 1,097 · Forks: 169
- Language: JavaScript
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/dimartarmizi-map-to-poster

## Two rendering paths for two kinds of map look

The central technical decision in MapToPoster JS is the hybrid rendering system. The README describes it as switching between efficient tile-based mapping with Leaflet and procedural vector artistry with MapLibre GL, and the whole theme catalogue is split along that same line.

The standard themes are Leaflet based and built on raster tiles from established providers. Minimal White is described as clean and modern and uses CartoDB Positron. Midnight Dark is the dark mode option on CartoDB Dark Matter. Classic Street is standard OpenStreetMap cartography. Modern Voyager is the colourful and detailed CartoDB Voyager style. Satellite View uses high-resolution Esri World Imagery.

The artistic themes are MapLibre GL styles, and the word procedural is doing real work there. Arctic Frost is pale blues and crisp whites, Aurora Glow is iridescent greens and pinks, Cyber Glitch uses neon accents, Paper Heritage is vintage sepia with inked roads, Volcanic Ash is deep charcoal with glowing ember accents, and Blueprint Classic is a technical cyanotype look for an architectural feel. The README also names Retro Synth, Charcoal Sketch and Sakura Bloom among others. A vector style here is not a picture of a map, it is a set of rules about how to draw one, which is why the palette can be procedural rather than photographic.

You can add your own artistic theme by editing src/core/artistic-themes.js and adding an object to the artisticThemes export, with colour slots for the background, text, water, parks and four road classes. The application picks it up automatically and shows it in the selection menu, so the extension point is a plain data file rather than a plugin system.

## Search, markers and plotted travel paths

Finding the place is the first step, and the README calls it precision geocoding, powered by the Nominatim API for instant global location search. Nominatim is a third-party geocoder, and the search runs straight from the browser, which is worth remembering when you read the privacy claim further down.

Once the location is set, dynamic markers let you place, drag and style multiple location indicators with various icons and adjustable sizes. This is the interactive core of the tool. A poster of a city is more interesting when you can drop a pin on the stadium or the airport and have the layout respond.

Custom travel paths are the feature that goes furthest. The README describes visualising journeys or specific itineraries with integrated route plotting and dynamic path fetching, and the API list names OSRM for routing. So a path is not a drawn line you trace by hand; the points are sent to a routing service and the resulting geometry is drawn on the map. Combined with draggable markers, that turns the app from a static poster generator into a small mapping editor.

The framing and typography then turn that into something you would hang. Mat and passepartout framing applies a classic gallery-style effect with adjustable width, border thickness and opacity, typography offers a curated selection of premium fonts with support for custom text and coordinate overrides, and a draggable UI overlay holds the city label with automatic edge clamping and symmetric safety padding so the label never runs off the edge of the paper.

The layout controls are independent toggles for country names, geographic coordinates and map labels, and background edge effects add soft vignette shading or subtle transparency along the edges to improve readability. Every one of these is a per-poster decision rather than a global setting, which is what separates a poster from a map screenshot.

## Exporting to 50,000 pixels, entirely in the browser

The export claim is the one that makes this useful for printing. The README says the app generates high-fidelity PNG files at custom resolutions or ultra-high resolutions, up to 50,000 pixels, and that the rendering is done with html2canvas. The tech stack list names html2canvas under rendering, and package.json confirms it as a runtime dependency at version 1.4.1.

The pipeline is straightforward to reason about. The map is a DOM tree, html2canvas walks that tree and rasterises it, and the result is a PNG. There is no server rendering step, so the resolution ceiling is a matter of how much memory the browser will spend on a canvas, not what a build pipeline can handle.

That also means the 100% client-side rendering claim and the geocoding claim sit in slight tension. The privacy statement says your data never leaves your browser, and by data the README means the poster configuration, which is persisted in LocalStorage. The location search and the route requests do go out to Nominatim and OSRM, which is unavoidable for a geocoder and a router. If that matters to you, it is a hosting and privacy decision rather than a code change.

The stack is otherwise plain. Vanilla JavaScript with ES modules, no framework, Vite 5 as the bundler, Tailwind CSS 3 for styling with autoprefixer and postcss in the build, Google Fonts including Outfit, Playfair Display and Cormorant Garamond, and Leaflet 1.9.4 and maplibre-gl 5.18.0 as the only two runtime libraries besides html2canvas. There is no state management library either, because the technical overview describes an observer-pattern based store in src/core/state.js that synchronises changes across the UI and both mapping layers.

## Running it locally and shipping it as a container

The getting started section is a standard Vite project, with Node.js 18.0.0 or higher as the prerequisite and npm for the package manager. The clone and install steps are:

```bash
git clone https://github.com/dimartarmizi/map-to-poster.git
cd map-to-poster
```

```bash
npm install
```

The development server then comes up with npm run dev, and the app is available at http://localhost:5173. A production build is npm run build, and the optimised files land in the dist/ folder. The package.json scripts are just dev, build and preview, all three pointing at Vite.

The Docker path is the interesting part for anyone who wants to run this as a service on their own hardware. It is a two stage build. The build stage uses node:20-alpine, copies the package files, runs npm ci, copies the source and runs npm run build. The runtime stage uses nginx:1.27-alpine, copies the Nginx configuration into place, copies the built dist directory into the web root, exposes port 80 and runs Nginx in the foreground:

```bash
docker build -t map-to-poster .
docker run -d -p 8080:80 --name map-to-poster map-to-poster
```

The app is then reachable at http://localhost:8080. Updating a deployment means rebuilding the image, removing the old container and starting a new one with the same command. The repository also includes a .dockerignore to keep the build context small and a docker/nginx.conf that provides SPA fallback to index.html, which matters because the app is a single page application and any unknown path has to return the entry document rather than a 404.

The hosted version is at maptoposter.tarmizi.id, so the repository is the source of a running public service rather than a project you have to build yourself.

## Where the limits are, and what to check before you rely on it

The obvious constraint is that nothing is server-side. There is no queue, no render farm and no scheduled export, so producing fifty posters means clicking export fifty times, and a headless browser would be the way around that. The README does not document a headless mode, so that is on you to build.

The second constraint is the tile providers. The standard themes resolve to CartoDB, OpenStreetMap and Esri endpoints, which are public services with usage policies. A deployment that suddenly gets popular would be relying on someone else's free infrastructure, and the README says nothing about supplying your own tile source for the standard themes. The artistic themes, being procedural MapLibre styles, do not have that problem, which is a good reason to prefer them for anything you intend to publish at volume.

Third, the export ceiling is a browser limit. A 50,000 pixel canvas is a lot of memory, and the failure mode on a smaller machine is a browser that runs out of space or a tab that stops responding. The README gives the maximum but not the practical guidance about hardware, so treat the ultra-high resolution as something to test on the target device.

Fourth, the routing and geocoding calls are unproxied and unauthenticated, which means the behaviour of the hosted service depends on two third-party APIs being available and fast.

The project state is worth noting. The last push was on 2026-06-06, the repository has no GitHub releases, and the version in package.json is 1.0.0, which for a JavaScript application often means a first stable release rather than a mature one. The licence is MIT with the LICENSE file at the root, the homepage is maptoposter.tarmizi.id, and the repository layout is a flat one: index.html, main.js, style.css, vite.config.js, and src/, public/ and docker/ directories.

## Conclusion

MapToPoster JS suits someone who wants a printable map of a place without paying a print shop, and it suits developers who want to embed a map poster generator in their own static site, since the whole thing is a Vite build served by Nginx. It is the wrong choice if you need batch export or server-side rendering, because the design is explicitly client-side and the export path goes through a browser canvas, and it is the wrong choice if you need your own tile provider, since the standard themes point at fixed raster providers and the artistic themes are procedural styles rather than data. Before deploying it, check the rate limits on the Nominatim and OSRM endpoints it calls, since those requests are not proxied, and read the technical overview in the README for the state management and rendering details that the feature list does not cover.

## FAQ

### How do I run MapToPoster JS locally?

Clone the repository, run npm install, then npm run dev, and the app is available at http://localhost:5173. Node.js 18.0.0 or higher is required, and npm run build produces optimised files in the dist/ folder.

### Does MapToPoster JS upload my poster or my data anywhere?

The rendering is entirely client-side and settings persist in LocalStorage, so the poster configuration stays in your browser. The location search and the route plotting do call the Nominatim and OSRM APIs, which are third-party services.

### How do I add my own map theme?

Edit src/core/artistic-themes.js and add an object to the artisticThemes export with name, description and colour values for bg, text, water, parks and the four road classes. The application picks it up and shows it in the theme selection menu automatically.

### How do I deploy MapToPoster JS with Docker?

Build the image with docker build -t map-to-poster . and run it with docker run -d -p 8080:80 --name map-to-poster map-to-poster, then open http://localhost:8080. The image is multi-stage, with node:20-alpine for the Vite build and nginx:1.27-alpine serving dist/ on port 80.

### What resolution can MapToPoster JS export?

The README says it generates high-fidelity PNG files at custom resolutions or ultra-high resolutions, up to 50,000 pixels, and the rendering is done with html2canvas in the browser, so the ceiling depends on the memory available in your browser.

## Sources

- [dimartarmizi/map-to-poster on GitHub](https://github.com/dimartarmizi/map-to-poster)
- [Issues](https://github.com/dimartarmizi/map-to-poster/issues)
- [License: MIT](https://github.com/dimartarmizi/map-to-poster/blob/main/LICENSE)
- [Project website](https://maptoposter.tarmizi.id)
- [README](https://github.com/dimartarmizi/map-to-poster/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/dimartarmizi-map-to-poster
