City-scale 3D in MapLibre GL JS, in a few lines of code.
Stream OGC 3D Tiles and OGC I3S scene services, photogrammetric mesh, LiDAR point clouds, Gaussian splats, 3D objects and elevation services directly into the MapLibre map you already have. Planet-wide tilesets included. No second globe engine beside your map, no viewer to migrate onto, no rebuild of your app.
Turbocharged by Bitruvius TurboLEPCC, Bitruvius TurboLERC and Bitruvius TurboSPZ. Powered by Bitruvius BVC, a Bitruvius flagship codec.
The formats that dominate the wire are decoded by Bitruvius codecs written from scratch for the browser. Point clouds, splats and elevation rasters each get a decoder built for that payload, running off the main thread, so the map keeps its frame budget while a city streams in.
import * as maplibregl from 'maplibre-gl';
import { I3sPointCloudLayer, prepareMapLibre } from '@bitruvius/sdk-maplibre';
await prepareMapLibre(maplibregl);
const map = new maplibregl.Map({ container: 'map', style, pitch: 60 });
map.addLayer(new I3sPointCloudLayer({ id: 'lidar', url: SCENE_SERVER_URL }));
That is the whole integration. The layer discovers the service, works out where on Earth it belongs, streams it at the level of detail the current camera needs, and renders it in the same pass as your basemap.
Terrain from an elevation service is one call too:
await addEsriTerrain(map, { url: IMAGE_SERVER_URL, maplibregl });
Rendering the geometry is the straightforward part. Making independently published services agree, with the terrain, with your basemap, and with each other, is where these integrations stall.
| Capability | What it does |
|---|---|
| Registered to the planet | Content is placed by its own earth-centered geometry, not by a hand-tuned offset, so a planet-wide tileset lands correctly anywhere you fly and two services over the same block agree with each other and with your basemap. |
| Lands on the ground | Layers seat themselves on the terrain instead of floating above it or sinking through it. Draping is automatic, and re-seats when terrain or exaggeration changes. |
| Heights that agree | Services publish against different vertical datums. The SDK reads the declared vertical CRS, applies the geoid separation, and puts data at the elevation it actually means, so a mesh and a point cloud of the same street line up. |
| Any grid on Earth | Services published on national grids stream and place correctly, not only Web Mercator ones. |
| Many services, one layer | unifyLayers() drives a set of services as a single layer, with one memory budget and one set of controls. A city published as 57 separate suburb services behaves like one city. |
| One memory ceiling | Streaming stays inside a budget you set, so a phone and a workstation both stay upright instead of the tab dying at altitude. |
| Correct in the map | Layers respect the map's pitch, terrain, label ordering and attribution rules, so 3D content composites with your basemap rather than covering it. |
Both major open standards, across every content type they carry. 3D Tiles and I3S are OGC Community Standards, so this is an implementation of published specifications, not a reverse-engineered reader pointed at one vendor's services:
| Content type | 3D Tiles | I3S |
|---|---|---|
| Integrated mesh and photogrammetry | ● | ● |
| LiDAR point clouds | ● | ● |
| Gaussian splats | ● | |
| 3D objects | ● | ● |
| Buildings and BIM interiors | ● | ● |
| Instanced point features | ● |
That includes planet-wide photorealistic mesh: point the mesh layer at a global tileset, bring your own provider key, and fly. There is no per-city URL and no special code path.
BVC is the Bitruvius flagship volumetric container, and the SDK renders it natively. One format carries both of the things 3D actually ships in, splats and LiDAR point clouds, losslessly, and the decoder is built to feed the GPU rather than fight it.
map.addLayer(new BvcMapLibreLayer({ id: 'scene', url: '/assets/scene.bvc', anchor }));
Standalone SPZ captures and LEPCC point clouds drop in the same way. All of them place at a lng/lat using a friendly placement vocabulary, up axis, heading, pitch, roll, scale and anchor point, rather than raw matrices, and you can re-orient a loaded asset live without re-decoding it.
Elevation. Elevation image services become MapLibre terrain in one call, decoded with TurboLERC.
Drop-in widgets, themeable, working against whatever you have loaded:
Measure distance, area, height and volume, snapping to meshes, point clouds and splats. Elevation profile along a drawn line. Slice a clipping volume through a tileset. Mask a footprint out of a layer. First-person walkthrough, outdoors or inside a building. Sun control for time-of-day relighting and shadows. Layer settings for live symbology and detail tuning.
map.addControl(new MeasurementWidget({ pickLayers: [layer] }), 'top-right');
npm i @bitruvius/sdk-maplibre maplibre-gl
maplibre-gl is a peer dependency (>=5 <7); v5 and v6 are both supported from
one build. Call await prepareMapLibre(maplibregl) once before your first map.
A self-contained ESM bundle is also published on the Bitruvius CDN if you would
rather skip the bundler entirely.
TypeScript and JavaScript both work. Type declarations ship with every
package, so there is no @types install and no configuration to get full
autocomplete.
The SDK is ESM only. import is supported; require() is not, and there is
no CommonJS build. Vite, webpack 5, Rollup, esbuild and modern Next.js handle
this without configuration. In Node, use "type": "module" or an .mjs entry.
MapLibre GL JS v6 is ESM only as well, so a project on the SDK is already in
that world.
The SDK is software, not a data licence. It grants you no right to any dataset, basemap, tileset or service it is able to connect to.
Bring your own credentials. Google Photorealistic 3D Tiles needs a Google Maps Platform API key, Cesium ion assets need an ion token, and secured ArcGIS services need an ArcGIS token or API key. What you may then do with the data those unlock is governed by your agreement with that provider, and nothing here adds to it or substitutes for it.
Attribution is usually a licence condition, not a courtesy. Providers generally require the data credit to remain visible to the end user. The SDK reads each layer's attribution and composes it into the map's attribution control for you, but keeping it displayed is your obligation, not ours.
Credentials are treated as yours. A token is sent only to the origin it was
minted for, never to some other host named by a tileset that happens to
reference it, and is never logged. A tile that fails to load is recoverable in a
way that a leaked key is not. See
@bitruvius/esri-auth for
ArcGIS sign-in.
3D Tiles and I3S are OGC Community Standards. OGC is a trademark of the Open Geospatial Consortium. Esri, ArcGIS, I3S, LERC and LEPCC are trademarks of Environmental Systems Research Institute, Inc. Cesium and 3D Tiles are trademarks of Cesium GS, Inc. Google is a trademark of Google LLC. Niantic and SPZ are trademarks of Niantic, Inc. MapLibre is a trademark of the MapLibre organization. All other marks are the property of their respective owners.
These names are used solely to describe the data formats and services this software interoperates with. Bitruvius is not affiliated with, sponsored by, or endorsed by any of them, and no such relationship is implied. Implementing a published specification is not a claim of certification: Bitruvius has not undergone OGC compliance testing for any standard. Use of any third-party service remains subject to that provider's own terms and licensing.
Proprietary. The full terms ship as LICENSE inside this package, and are readable
before installing at cdn.bitruvius.com/legal/sdk-license-v1.txt.
© Bitruvius, Inc.
@bitruvius/sdk-maplibre — MapLibre GL viewer adapter for BVC scenes.
Drop a BvcMapLibreLayer into a MapLibre map to load and render a
.bvcgaussian-splat or point-cloud asset, georeferenced. The renderer cores are viewer-agnostic; this package is the MapLibre seam (placement, view-matrix recovery, custom-layer lifecycle).maplibre-glis a peer dependency (>=5 <7) — both majors from one build, with v6 as the default target. Import the namespace (v6 removed the default export) and run the one-line pre-flight before constructing the map:Example