babylonjs-mapping renders tiled maps, terrain, and geographic feature data in
Babylon.js scenes. It provides a reusable tile
grid, raster providers, building and vector-feature loaders, coordinate
conversion helpers, and Mapbox terrain support.
The package is browser-oriented, published as ES modules, and includes TypeScript declarations.
npm install babylonjs-mapping @babylonjs/core @babylonjs/gui @babylonjs/loadersCreate the tile geometry before selecting its geographic location. The tile width and feature dimensions use Babylon world units.
import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera.js";
import { Engine } from "@babylonjs/core/Engines/engine.js";
import { HemisphericLight } from "@babylonjs/core/Lights/hemisphericLight.js";
import { Vector2, Vector3 } from "@babylonjs/core/Maths/math.vector.js";
import { Scene } from "@babylonjs/core/scene.js";
import { RasterOSM, TileSet } from "babylonjs-mapping";
const canvas = document.querySelector<HTMLCanvasElement>("#renderCanvas")!;
const engine = new Engine(canvas, true);
const scene = new Scene(engine);
const camera = new ArcRotateCamera(
"camera",
-Math.PI / 2,
Math.PI / 3,
100,
Vector3.Zero(),
scene,
);
camera.attachControl(canvas, true);
new HemisphericLight("light", new Vector3(0, 1, 0), scene);
const tiles = new TileSet(scene, engine);
tiles.setRasterProvider(new RasterOSM(tiles));
tiles.createGeometry(new Vector2(4, 4), 20, 2);
tiles.updateRaster(36.0014, -78.9382, 16);
engine.runRenderLoop(() => scene.render());
window.addEventListener("resize", () => engine.resize());createGeometry(tileCount, tileWidth, meshPrecision) creates the reusable
mesh grid. updateRaster(latitude, longitude, zoom) centers that grid on a
standard slippy-map coordinate and requests imagery from the configured raster
provider.
| Data | Provider | Credentials or configuration |
|---|---|---|
| OpenStreetMap raster imagery | RasterOSM |
None |
| Mapbox raster imagery | RasterMB |
Mapbox access token |
| GEBCO bathymetry imagery | RasterGEBCO |
None |
| WMTS imagery | RasterWMTS |
WMTS endpoint and layer |
| OSM Buildings geometry | BuildingsOSM |
OSM Buildings/OneGeo access token |
| Mapbox landmark models | BuildingsMB |
Mapbox access token |
| Mapbox or custom MVT features | BuildingsVectorTile |
Token for Mapbox; URL and source layers for custom services |
| Overture Maps buildings | BuildingsOverture |
PMTiles source; defaults to the latest public release |
| GeoServer, WFS, or ArcGIS features | BuildingsWFS |
Service URL, layer, and source CRS |
| Google Photorealistic 3D Tiles | Google3DTiles |
Google Maps Platform API key with Map Tiles API access |
| Spherical raster maps | GlobeSet and GlobeNavigator |
Any supported raster provider |
| Mapbox terrain | TerrainMB through TileSet |
Mapbox access token |
External services retain their own usage terms, attribution requirements, rate limits, and CORS policies. Keep API tokens out of source control and inject them through your application's normal secret/configuration mechanism.
Initialize raster coordinates before requesting buildings. Building requests use the current tile coordinates and are processed while the Babylon scene is rendering.
import { BuildingsOSM } from "babylonjs-mapping";
const buildings = new BuildingsOSM(tiles);
buildings.accessToken = osmbAccessToken;
buildings.generateBuildings();Feature widths and diameters are Babylon world units, even when source data is EPSG:4326 or EPSG:3857:
buildings.lineWidth = 0.25;
buildings.pointDiameter = 0.5;For ArcGIS-hosted data, use the matching setup helper before loading:
import { BuildingsWFS, EPSG_Type } from "babylonjs-mapping";
const features = new BuildingsWFS(
"buildings",
"https://services.arcgis.com/example/FeatureServer",
"0",
EPSG_Type.EPSG_4326,
tiles,
);
features.setupAGOLFeatureService();
features.generateBuildings();setupAGOL() supports an ArcGIS WFS endpoint, while setupGeoServer()
configures GeoServer-style requests. Both WFS and ArcGIS Feature Service
loading handle paginated results.
Google's Photorealistic 3D Tiles can be loaded directly into the Babylon scene
without adding Cesium. The provider follows the authenticated tile hierarchy
for the current TileSet extent, loads GLB content, and rebases Earth-centered
coordinates around the map center:
import { Google3DTiles } from "babylonjs-mapping";
const googleTiles = new Google3DTiles(tiles, {
apiKey: googleMapsApiKey,
maxDepth: 22,
maxTiles: 256,
});
await googleTiles.load();The API key must have the Google Maps Platform Map Tiles API enabled and billing
configured. Call load() again after updateRaster() when the map moves.
maxDepth and maxTiles control quality and memory use; exaggeration adjusts
the local vertical axis. Google data credits returned by loaded tiles are
displayed through the library attribution UI. Review Google's
Photorealistic 3D Tiles documentation
and Map Tiles API policies
before using the service.
The Google 3D Tiles demo reads its browser key
from an ignored public/google-key.txt file. The Pages workflow supplies that
file from the GOOGLE_MAPS_API_KEY repository secret, keeping credentials out
of Git history.
GlobeSet curves Web Mercator raster tiles onto a configurable sphere while
retaining the raster-provider and tile lifecycle APIs. GlobeNavigator
connects it to an ArcRotateCamera, reports the visible geographic center,
supports animated coordinate-aware flights, and streams raster detail as the
camera moves or zooms.
import { GlobeNavigator, GlobeSet, RasterOSM } from "babylonjs-mapping";
const detail = new GlobeSet(scene, engine, {
radius: 50.05,
backingSurface: false,
});
detail.setRasterProvider(new RasterOSM(detail));
detail.createGeometry(new Vector2(5, 5), 20, 12);
const globeCamera = new ArcRotateCamera(
"globe camera",
0,
Math.PI / 2,
150,
Vector3.Zero(),
scene,
);
globeCamera.attachControl(canvas, true);
const navigator = new GlobeNavigator(detail, globeCamera, {
minZoom: 3,
maxZoom: 18,
});
navigator.setView(35.2271, -80.8431, { zoom: 3 });
navigator.flyTo(36.1069, -112.1129, { zoom: 11, durationMs: 1400 });getSurfacePosition(), getSurfaceNormal(), and
getSurfaceCoordinates() support markers and click-to-fly interactions. Keep
a low-resolution base globe beneath a detail layer so imagery remains visible
while higher-resolution tiles load.
For terrain and streamed features, use GlobeDataController with the same
providers used by planar maps. TerrainRGB supplies signed elevation data,
including ocean depth, while RasterGEBCO is imagery only. The controller
loads elevation before draped features, limits concurrent work, and can be
refreshed with invalidate() when sources or settings change. See the
globe-mode example for terrain, bathymetry,
buildings, roads, imported GeoJSON, and camera navigation.
Use a sufficiently high mesh precision when terrain detail matters. Terrain generation is asynchronous.
import { RasterMB } from "babylonjs-mapping";
const terrainTiles = new TileSet(scene, engine);
const raster = new RasterMB(terrainTiles);
raster.accessToken = mapboxAccessToken;
terrainTiles.setRasterProvider(raster);
terrainTiles.createGeometry(new Vector2(4, 4), 50, 32);
terrainTiles.ourTerrainMB.accessToken = mapboxAccessToken;
terrainTiles.updateRaster(36.1005, -112.1127, 14);
await terrainTiles.generateTerrain(1);
terrainTiles.setupTerrainLOD([16, 4, 1, 0], [64, 128, 256, 512]);The final terrain LOD precision may be 0 to hide distant tiles. LOD
distances and terrain dimensions use Babylon world units.
moveAllTiles() recycles tiles that leave the grid. Subscribe to
onTilePositionUpdatedObservable when application-owned markers or meshes
must move with that lifecycle:
tiles.onTilePositionUpdatedObservable.add(({ tile, previousTileCoords, tileCoords }) => {
removeObjectsForTile(previousTileCoords);
addObjectsForTile(tileCoords, tile);
});Pass true as the fifth moveAllTiles() argument to reload terrain when a
tile is recycled.
Static scenes can freeze matrices and disable interactions they do not use. Keep tile world matrices unfrozen when tiles move.
buildings.setOptimizationOptions({
freezeWorldMatrices: true,
disablePicking: true,
disableCollisions: true,
prioritizeRequestsByDistance: true,
});
tiles.setOptimizationOptions({
freezeRasterMaterials: true,
freezeTileWorldMatrices: false,
disableTilePicking: false,
disableTileCollisions: true,
});Building billboard LOD is opt-in:
buildings.buildingLOD = {
enabled: true,
distance: 100,
};Use setPerformanceMonitoringEnabled(true), getPerformanceStats(), and
resetPerformanceStats() to measure queue depth, geometry reduction, LOD
selection, and sampled frame times.
Providers that support RetrievalLocation.Local read from map_cache/ by
default. Change localPathPrefix when the cache is hosted elsewhere:
raster.localPathPrefix = "assets/map_cache/";
buildings.localPathPrefix = "assets/map_cache/";Runnable applications are under examples-npm:
- OpenStreetMap Hello World
- Endless OpenStreetMap
- OpenStreetMap user data at real scale
- Mapbox terrain
- GEBCO bathymetry
- Globe navigation
Each example has its own README and npm scripts. A typical example can be run with:
cd examples-npm/OpenStreetMap-HelloWorld
npm install
npm startExamples that use commercial services expect their access-token text files as documented in the example directory.
- Public classes and types are exported from
babylonjs-mapping. - Compatibility subpath exports under
babylonjs-mapping/lib/*remain available for existing consumers. - The package uses ES modules. Include the
.jssuffix when importing Babylon modules directly, as shown above. - Geometry-dependent calls throw descriptive errors when geometry or raster coordinates have not been initialized.
- Dispose application-owned providers, observers, and Babylon resources when their scene is torn down.
npm ci
npm run build
npm testReport bugs and request features through GitHub Issues.
Vic Szabo
Principal Investigator
Research Professor of Art, Art History & Visual Studies, Duke University
Chair of Art, Art History & Visual Studies, Duke University
David J. Zielinski
Senior AR/VR Technology Specialist, Duke University
Developer, 2022–2025
Project Manager, 2026–present
Thomas Hines
Lead Developer, 2026–present
Undergraduate, Computer Science / Electrical & Computer Engineering, Duke University