SDK · JavaScript & TypeScript

Bitruvius SDK for JavaScript

High-performance codecs for reality capture and imagery. BVC, our flagship container for quantized gaussian splats and point clouds. RIPT, our flagship codec for scientific rasters. TurboWebP, TurboJXL, TurboLERC, TurboLEPCC and TurboSPZ across images, elevation, point clouds and splats. Every one memory-safe and decoding off the main thread, so scenes that used to need a desktop client hold interactive framerates in a browser tab.

Decode anything, anywhere: gaussian splats, LiDAR point clouds, photogrammetry meshes, city-scale models, elevation, satellite and scientific rasters, with or without a map. When you want one, the MapLibre plugin makes any of it an ordinary custom layer: one import, one addLayer, georeferenced and terrain-aware, no native viewer and no build step. OGC 3D Tiles and Esri I3S stream on top.

v0.3.1 CDN npm MapLibre GL v5 – v6+ No build step required

Plays in place. Nothing downloads until you press play. Open the video directly ↗

Quick start

Three ways in, all on the same v0.3.1 release line. The CDN needs no tooling at all. npm carries the same modules with TypeScript types, tree-shaking and offline builds.

  • Per-codec bundles are self-contained. Pull one decoder, not the whole SDK.
  • The wasm URL is derived from the bundle's own location, so there is nothing to configure.
  • Release directories are immutable and every file is published with an SRI hash.

No install, no bundler. Import a single codec bundle straight from the CDN and decode in the browser.

index.html HTML
<canvas id="preview"></canvas>

<script type="module">
  // One codec, one file — 5 KB of JavaScript. The wasm module is fetched from
  // the same pinned release directory the first time you decode.
  import { JxlDecoder } from 'https://cdn.bitruvius.com/sdk/v0.3.1/bundles/bitruvius-turbo-jxl.esm.min.js';

  const decoder = new JxlDecoder();

  const response = await fetch('/imagery/tile.jxl');
  const bytes = new Uint8Array(await response.arrayBuffer());

  // { width, height, rgba, hasAlpha }
  const image = await decoder.decode(bytes);

  const canvas = document.getElementById('preview');
  canvas.width = image.width;
  canvas.height = image.height;
  canvas.getContext('2d').putImageData(
    new ImageData(
      new Uint8ClampedArray(image.rgba.buffer, image.rgba.byteOffset, image.rgba.length),
      image.width,
      image.height,
    ),
    0,
    0
  );
</script>
Every URL is pinned to v0.3.1. Run this in the playground →

What ships in a release

Eighteen library bundles (plus two I3S decode workers), a manifest.json with an SRI hash per file, and the wasm codecs, under https://cdn.bitruvius.com/sdk/v0.3.1/. The same modules are on npm as @bitruvius/* packages at the matching version.

Umbrella build

everything, lazily loaded

Start here when you do not know which formats you will meet. Codecs load on first use, so the wasm you never touch is never fetched.

  • bitruvius.esm.min.js 345 KB

    Every decoder in one module: BvcDecoder, BvcWorkerDecoder, JxlDecoder, LercDecoder, LepccDecoder, COLORMAPS, BitruviusError.

  • bitruvius.iife.min.js

    The same surface on window.Bitruvius, for classic script tags with no module syntax.

Single-codec bundles

smallest possible payload

Self-contained. If you only ever decode one format, ship one file.

  • bitruvius-turbo-webp.esm.min.js 5 KB

    TurboWebP: our WebP decoder. Reads anything libwebp 1.6 does. Exports WebpDecoder and ensureWasm.

  • bitruvius-turbo-jxl.esm.min.js

    TurboJXL: our JPEG XL decoder. Reads anything libjxl 0.11 does.

  • bitruvius-turbo-lerc.esm.min.js

    TurboLERC: our Esri LERC decoder for elevation and scientific rasters.

  • bitruvius-turbo-lepcc.esm.min.js

    TurboLEPCC: our Esri LEPCC decoder for I3S point-cloud payloads.

  • bitruvius-turbo-spz.esm.min.js

    TurboSPZ: our SPZ v4 decoder for 3D gaussian splats.

MapLibre plugin

layers & widgets

The rendering half of the SDK. Peer-depends on maplibre-gl v5.0.0 through v6.0.0+; the examples pin 5.24.0.

  • bitruvius-sdk-maplibre.esm.min.js 1.9 MB

    Every MapLibre layer and widget, for bundlers and import maps.

  • bitruvius-sdk-maplibre.iife.min.js

    The same plugin for classic script tags.

Composable modules

for bundler builds

The umbrella build is assembled from these. Published individually so a bundler can take exactly the graph your app touches. Most apps never import them directly. Two are worth knowing by name: bitruvius-bvc is our flagship container for quantized gaussian splats and point clouds, and bitruvius-ript is our scientific raster codec (lossless and lossy, 14 data types, for elevation, satellite, SAR and medical imagery).

  • bitruvius-bvc.esm.min.js
  • bitruvius-ript.esm.min.js
  • bitruvius-splats.esm.min.js
  • bitruvius-ptcloud.esm.min.js
  • bitruvius-raster.esm.min.js
  • bitruvius-geo-core.esm.min.js
  • bitruvius-render-runtime.esm.min.js
  • bitruvius-foundation.esm.min.js
  • bitruvius-codec-runtime.esm.min.js

@bitruvius/sdk-maplibre

3D geospatial, inside the map you already have

Every layer type is a genuine MapLibre custom layer: it obeys the camera, the terrain, the layer order and the style. There is no second canvas floating over your map, no separate globe, and no fork of maplibre-gl. You keep your basemap, your controls and your event handlers exactly as they are.

The plugin peer-depends on maplibre-gl v5.0.0 through v6.0.0+. Layers are standard MapLibre custom layers — add and remove them like any other.

package.json JSON
{
  "dependencies": {
    "@bitruvius/sdk-maplibre": "0.3.1",
    "maplibre-gl": "5.24.0"
  }
}
map.ts TypeScript
import maplibregl from 'maplibre-gl';
import { Tiles3DSplatLayer } from '@bitruvius/sdk-maplibre';
import 'maplibre-gl/dist/maplibre-gl.css';

const map = new maplibregl.Map({
  container: 'map',
  style: 'https://tiles.openfreemap.org/styles/bright',
  center: [-122.4013, 37.7893],
  zoom: 16,
  pitch: 65
});

map.on('load', () => {
  // An OGC 3D Tiles gaussian-splat tileset, streamed and sorted on the GPU.
  map.addLayer(
    new Tiles3DSplatLayer({
      id: 'downtown-splats',
      url: 'https://tiles.arcgis.com/tiles/x5wCko8UnSi4h0CB/arcgis/rest/services/CU_Campus_Area3_GaussianSplat/3DTilesServer/tileset.json'
    })
  );
});
Every layer option is fully typed. Open the splats example →

Layer types

  • Tiles3DSplatLayer
  • Tiles3DMeshLayer
  • Tiles3DPointCloudLayer
  • I3sIntegratedMeshLayer
  • I3s3dObjectLayer
  • I3sMeshLayer
  • I3sBuildingLayer
  • I3sPointCloudLayer
  • I3sPointLayer
  • BvcMapLibreLayer
  • SpzMapLibreLayer
  • LepccMapLibreLayer
  • ModelInstancesLayer
  • MultiModelInstancesLayer
  • BillboardInstancesLayer
  • TerrainShadowReceiverLayer

Controllers & widgets

  • BasemapController
  • DrawController
  • MeasurementWidget
  • ElevationProfileWidget
  • FirstPersonNavWidget
  • SliceWidget
  • SunControlWidget

Shipping it

Three things surprise people the first time they put the SDK into a hardened application. All three are one line of configuration.

The short version. Allow 'wasm-unsafe-eval', allow worker-src blob:, and pass workers: false when the SDK itself is loaded cross-origin from the CDN.

Decoding happens in WebAssembly, usually inside a worker. If your app sends a CSP, these directives are the minimum.

response headers HTTP
Content-Security-Policy:
  default-src 'self';
  script-src  'self' 'wasm-unsafe-eval' https://cdn.bitruvius.com;
  worker-src  blob:;
  connect-src 'self' https://cdn.bitruvius.com;

Live examples

The examples are editable, and they run

Every example opens in a real editor next to a live preview. Change the tileset URL, flip a layer option, break something on purpose, then press Run and it executes in your browser, against the same v0.3.1 bundles documented on this page. Nothing is a screenshot.

Open the playground ↗

Edit, then run

A real editor beside a live preview. Change a tileset URL, flip a layer option, break it on purpose, then press Run and it executes in your browser.

The same bundles

Every example loads the v0.3.1 bundles documented on this page from the CDN, not a vendored copy and not a screenshot.

Copy the whole file

Each example ships its full source and a README, in both JavaScript and TypeScript, ready to lift into your own project.