orihon
v2.0.1
Published
Orihon Maps — a free, open-source browser map engine. Apache 2.0. Use it anywhere.
Maintainers
Readme
Orihon Maps
A fast, typed browser map engine with a small path from first map to large-scale GIS.
Orihon Maps is a free, open-source browser mapping library. Start with a map-centric API for common work, move to explicit layers when you need more control, and opt into GPU rendering and large-data tools only when the application needs them.
Apache 2.0. No engine key. No paid runtime license.
The product is Orihon Maps; the package is orihon. Every install, import and global uses the
short identifier — npm i orihon, from "orihon", globalThis.Orihon — and so does this document
wherever it is talking about code rather than about the product.
Start here
Starting from an empty folder? One command writes a project that already draws a map:
npm create orihon-app my-map
cd my-map
npm install
npm run devTemplates are vanilla and react, both on Vite; npm create orihon-app my-map -- --template react --yes skips the prompts. The generated project already contains the stylesheet import, a container with a height, an attribution and one working map, so the first thing you see is a map rather than a setup checklist.
Already have a Vite, React, Vue or other ESM application?
npm install orihonThen create a map with orihon/easy:
import { createMap } from "orihon/easy";
import "orihon/orihon.css";
const map = createMap("map", {
center: { lat: 52.52, lng: 13.405 },
zoom: 12,
basemap: {
url: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
attribution: "© OpenStreetMap contributors"
}
});
map.addMarker({
position: { lat: 52.52, lng: 13.405 },
appearance: { shape: "pin", color: "#2563eb" },
popup: "Berlin"
});Your page needs a container with a real size:
<div id="map"></div>
<style>
html,
body {
margin: 0;
height: 100%;
}
#map {
height: 100vh;
min-height: 360px;
}
</style>That is enough for a pannable, zoomable OpenStreetMap basemap with a marker and popup.
A <div> has no height of its own, which is the most common reason a first map looks broken: tiles are requested, layers exist, nothing is painted. Orihon says so in the console instead of leaving you to guess — see Troubleshooting.
Add common map objects
The Easy API is map-centric and object-first. Each operation takes one options object, so the fields are visible in autocomplete and there are no positional Easy overloads to memorize.
map.addPolyline({
points: [
{ lat: 52.51, lng: 13.37 },
{ lat: 52.53, lng: 13.41 },
{ lat: 52.50, lng: 13.44 }
],
style: {
stroke: "#2563eb",
strokeWidth: 4
}
});
map.addPolygon({
rings: [
{ lat: 52.50, lng: 13.38 },
{ lat: 52.54, lng: 13.39 },
{ lat: 52.53, lng: 13.45 },
{ lat: 52.50, lng: 13.38 }
],
style: {
fill: "#2563eb",
fillOpacity: 0.2,
stroke: "#2563eb"
},
popup: "District"
});
const places = map.addGeoJSON({
data: {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: { name: "Alexanderplatz" },
geometry: {
type: "Point",
coordinates: [13.4132, 52.5219]
}
}
]
}
});
map.fitBounds(places.getBounds());Easy currently covers the common first-map operations:
addMarker({ position, ... })addPolyline({ points, ... })addPolygon({ rings, ... })addGeoJSON({ data, ... })addTileLayer({ url, ... })setBasemap(...)/getBasemap()
The objects returned by those methods are normal Orihon layers, not wrappers. You can use their events, popup APIs, setters and normal remove() lifecycle immediately.
See the Easy API guide for the complete contract.
When you need more control
You do not need to choose the whole architecture before drawing the first map.
Start with orihon/easy. Move to the Layer API only where the application needs explicit composition:
import { polygon } from "orihon/standard";
const area = polygon(
[
{ lat: 52.50, lng: 13.38 },
{ lat: 52.54, lng: 13.39 },
{ lat: 52.53, lng: 13.45 },
{ lat: 52.50, lng: 13.38 }
],
{
fill: "#0f766e",
fillOpacity: 0.2,
stroke: "#0f766e"
}
).addTo(map);
area.bindPopup("Custom layer");The two public sentence forms are intentional:
| API | Sentence | Use it for |
| --- | --- | --- |
| Easy | map.addMarker({ ... }) | First maps and common application work |
| Layer API | marker(position).addTo(map) | Explicit composition and the full layer surface |
There is no third generic map.add({ type, ... }) dialect.
Coordinates without guessing
Application-facing geographic coordinates use named values:
const berlin = { lat: 52.52, lng: 13.405 };When converting from another convention, make the order explicit:
import {
latLng,
lngLat,
fromGeoJSONPosition,
toGeoJSONPosition
} from "orihon/standard";
const moscow = latLng(55.751244, 37.618423); // latitude, longitude
const berlin = lngLat(13.405, 52.52); // longitude, latitude
const point = fromGeoJSONPosition([13.405, 52.52]);
const geojsonPosition = toGeoJSONPosition(point); // [13.405, 52.52]A list names its order once instead of repeating lat and lng on every point:
import { latLngs, lngLats, fromGeoJSONPositions, polyline } from "orihon/standard";
polyline(latLngs([[52.51, 13.37], [52.53, 13.41], [52.50, 13.44]]));
polyline(lngLats(maplibreCoordinates));
polyline(fromGeoJSONPositions(feature.geometry.coordinates));latLngs() and lngLats() also read a flat run of numbers — latLngs([52.51, 13.37, 52.53, 13.41]), or a Float64Array straight from a worker, which skips building one pair object per point. An odd length throws instead of shifting every later point by one place.
GeoJSON keeps the standard [longitude, latitude] order. Normal Orihon geographic APIs prefer { lat, lng } so a bare numeric tuple cannot silently swap the two.
Choose a package tier later
Package size and API difficulty are separate concerns. The Easy API is a beginner-oriented adapter over Standard; Core, Standard and Advanced describe capability and bundle size.
| Tier | Import | What it includes |
| --- | --- | --- |
| Core | orihon/core | Map, camera, events, geometry, DOM raster tiles and grid primitives |
| Standard | orihon/standard | Core + markers, SVG/canvas vectors, GeoJSON, popups, overlays, controls and locales |
| Advanced | orihon | Standard + WebGL/WebGPU, MVT, heat, ObjectManager, workers, routing, traffic and offline tooling |
A normal application can stay on Standard indefinitely. Importing the Advanced root is for cases where dataset size, rendering load or infrastructure features justify it.
GPU rendering is explicit:
import { tileLayer } from "orihon";
tileLayer("/tiles/{z}/{x}/{y}.png");
// DOM renderer — stable default.
tileLayer("/tiles/{z}/{x}/{y}.png", { renderer: "auto" });
// Prefer WebGPU, then WebGL, then DOM.
tileLayer("/tiles/{z}/{x}/{y}.png", { renderer: "webgl" });
// WebGL is required; unsupported capability throws instead of silently changing renderer.Optional product-specific entry points stay separate from those tiers:
orihon/easy— map-centric first-map APIorihon/source— reactiveFeatureSourceorihon/react— React bindingsorihon/draw— drawing and editingorihon/controls— fullscreen, measure, minimap and graticuleorihon/geo— additional geographic helpersorihon/popup-content— declarative rich popup contentorihon/pmtiles,orihon/mvt,orihon/mlt,orihon/mvt-wasm— packed tile formatsorihon/webgpu— explicit WebGPU integration
Common next steps
Reactive data
If the same data should drive several renderers, use FeatureSource instead of rebuilding application state around a particular layer:
import { featureSource } from "orihon/source";
import { geoJSON } from "orihon/standard";
const source = featureSource();
const layer = geoJSON(source).addTo(map);
source.add({
type: "Feature",
id: "station-1",
properties: { name: "Central Station" },
geometry: {
type: "Point",
coordinates: [13.3694, 52.5251]
}
});One source can feed GeoJSON, labels and high-volume rendering. See FeatureSource.
Large datasets
The Advanced entry includes ObjectManager, GPU point/path rendering, heatmaps, vector tiles, workers and performance diagnostics. ObjectManager is intended for tens of thousands to millions of application objects without one DOM marker per object.
For large cooperative imports:
import { objectManager } from "orihon";
const manager = objectManager({
clusterize: true,
clusterRenderer: "auto",
layoutWorker: "auto"
}).addTo(map);
await manager.addAsync(objects, {
chunkSize: 10_000,
yieldMode: "task",
signal: abortController.signal
});The detailed data, styling, clustering, heatmap and lifecycle contracts live in the API reference instead of this README.
React
React bindings are published from orihon/react and use the same map/layer concepts. React and React DOM are optional peer dependencies, so non-React applications do not pull them in.
See the API reference and the runnable example under examples/react.
Drawing and controls
Drawing/editing is opt-in through orihon/draw. Additional UI such as fullscreen, measurement, minimap and graticule lives under orihon/controls.
Keeping these entry points separate means a normal map does not pay for product-specific UI it never uses.
TypeScript and API contracts
Orihon is written in strict TypeScript and publishes generated declarations for every public entry point.
The public API follows a small set of rules:
- options objects are preferred when several independent values would otherwise become positional arguments;
- geographic units are visible in names such as
durationMs,radiusMetersandradiusPixels; addTo(map)attaches reusable layers and controls;remove()detaches them;- resource-owning services use terminal, idempotent
destroy(); - cancellation uses
AbortSignal/AbortError; - calls made after terminal destruction use
DestroyedErrorrather than pretending the operation was cancelled; - live map state is read-only from the public surface and changes through explicit methods;
- built-in events are typed by event name and payload.
The complete conventions are documented in API-DESIGN.md.
Performance
Orihon keeps rendering cost proportional to the job instead of forcing every application through the heaviest pipeline.
Core and Standard stay CPU/DOM. Advanced adds GPU backends for the workloads where they pay off: large point sets, heat, GPU raster tiles and large vector paths.
The repository includes two reproducible browser demos:
- Scale showcase — Core → Standard → Advanced, then large-data scenes (live)
- Engine benchmark — the same point workload through Orihon, Leaflet, OpenLayers and MapLibre (live)
Run the benchmarks rather than relying on a headline number; browser, GPU, dataset shape and interaction pattern all matter.
Size
Nothing Orihon ships crosses 150 KiB gzip. npm run size fails the build when a published artifact exceeds its budget and checks this table against dist/release-manifest.json.
| Artifact | Budget | What it carries |
| --- | ---: | --- |
| orihon.geo.esm.js | ≤ 2 KiB gzip | Geometry helpers only |
| orihon.popup-content.esm.js | ≤ 5 KiB gzip | Popup content blocks |
| orihon.controls.esm.js | ≤ 8 KiB gzip | Optional controls |
| orihon.draw.esm.js | ≤ 12 KiB gzip | Draw/edit tools |
| orihon.core.esm.js | ≤ 18 KiB gzip | Map, events, geometry, DOM tiles |
| orihon.standard.esm.js | ≤ 38 KiB gzip | Everyday GIS, no WebGL |
| orihon.esm.js | ≤ 132 KiB gzip | Advanced: Standard + GPU, MVT, ObjectManager and WASM |
| orihon.react.esm.js | ≤ 118 KiB gzip | React bindings over the Advanced surface |
| orihon.global.js | ≤ 149 KiB gzip | Standalone script-tag build |
Prefer the smallest entry point that contains the capability you need. Exact raw and gzip sizes for the current build are written to dist/release-manifest.json.
Browser builds
npm run build emits modular ESM, generated TypeScript declarations, minified single-file ESM bundles, CSS and a standalone globalThis.Orihon build.
Main artifacts include:
dist/core.jsdist/standard.jsdist/index.jsdist/orihon.core.esm.jsdist/orihon.standard.esm.jsdist/orihon.esm.jsdist/orihon.global.jsdist/orihon.css
If you self-host the standalone files, a script-tag page can use:
<link rel="stylesheet" href="/vendor/orihon/orihon.css" />
<script src="/vendor/orihon/orihon.global.js"></script>
<script>
const map = Orihon.createMap("map", {
center: { lat: 52.52, lng: 13.405 },
zoom: 12
});
</script>The global build exposes globalThis.Orihon and globalThis.OrihonReady.
Documentation
Start with the guide that matches what you are doing:
- Easy API — first maps and map-centric methods
- API reference — complete public surface
- Project starter — what
npm create orihon-appwrites - Recipes — task-oriented examples
- FeatureSource — shared reactive data
- Troubleshooting — blank maps, missing tiles, renderer errors
- Migrating from Leaflet
- Migrating to the next major
- Security model
- Developer Guide — generated searchable function catalogue with runnable examples
- Examples hub
- Plugin development
- Development, versions and benchmarks
- Pricing — what is free and what Studio adds
- Enhancement roadmap
Development
Repository development and release tooling requires Node.js 22 or newer. .node-version pins the tested LTS version.
npm install
npm run build
npm run checkUseful commands:
npm run typecheck
npm test
npm run test:browser
npm run test:e2e
npm run size
npm run docs:build
npm run docs:check
npm run demo:docs
npm run demo:showcase
npm run demo:benchnpm run check runs the type checks, unit tests, size budgets and documentation consistency checks used before publishing.
Design goals
- Make the first map require very little API knowledge.
- Keep one predictable grammar inside each API level.
- Make coordinates, units, ownership and lifecycle explicit.
- Keep Core, Standard and Advanced as capability tiers with enforced size budgets.
- Let applications move from DOM/SVG/canvas to GPU rendering without replacing their map model.
- Keep I/O-heavy services provider-based so applications can supply local, commercial or test implementations.
- Prefer browser primitives and small data structures over mandatory heavyweight runtime stacks.
Brand assets
Production-ready SVG/PNG logos, favicons and design tokens are published under orihon/brand/*.
License
Orihon is licensed under the Apache License 2.0. Use it in personal, educational and commercial projects without a separate paid engine license.
See LICENSE, LICENSE-NOTICE.md and the License FAQ.
Copyright 2026 whahe.
