Skip to main content
This guide shows you how to create your first Threebox scene with a simple 3D object.

Prerequisites

  • Mapbox GL JS v2.2.0 or higher (or Azure Maps SDK)
  • Threebox library
  • Mapbox access token (or Azure subscription key)

Installation

Include Threebox from CDN:
<!-- Mapbox GL JS -->
<link href="https://api.mapbox.com/mapbox-gl-js/v2.2.0/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v2.2.0/mapbox-gl.js"></script>

<!-- Threebox -->
<script src="https://cdn.jsdelivr.net/gh/jscastro76/threebox/dist/threebox.js"></script>
<link href="https://cdn.jsdelivr.net/gh/jscastro76/threebox/dist/threebox.css" rel="stylesheet" />

Configuration

Create a configuration file with your access token:
config.js
var config = {
  accessToken: 'pk.your_mapbox_token_here'
}
For Azure Maps, add subscriptionKey: 'your_azure_key' to the config object.

Basic Example: Sphere

Here’s a complete working example that renders a red sphere on the map:
<!doctype html>
<head>
  <title>Threebox Basic Example</title>
  <link href="https://api.mapbox.com/mapbox-gl-js/v2.2.0/mapbox-gl.css" rel="stylesheet">
  <script src="https://api.mapbox.com/mapbox-gl-js/v2.2.0/mapbox-gl.js"></script>
  <script src="../dist/threebox.js"></script>
  <link href="../dist/threebox.css" rel="stylesheet" />
  <script src="config.js"></script>
  <style>
    body, html {
      width: 100%;
      height: 100%;
      margin: 0;
    }
    #map {
      width: 100%;
      height: 100%;
    }
  </style>
</head>
<body>
  <div id='map'></div>

  <script type="module">
    mapboxgl.accessToken = config.accessToken;

    // Starting location for the map and sphere
    var origin = [-122.4340, 37.7353, 1];

    // Initialize the map
    var map = new mapboxgl.Map({
      container: 'map',
      style: 'mapbox://styles/mapbox/light-v9',
      center: origin,
      zoom: 17,
      pitch: 60,
      antialias: true
    });

    // Wait for map style to load
    map.on('style.load', function() {
      // Add a custom 3D layer
      map.addLayer({
        id: 'custom_layer',
        type: 'custom',
        renderingMode: '3d',
        onAdd: function(map, mbxContext) {
          // Initialize Threebox
          window.tb = new Threebox(
            map,
            mbxContext,
            {
              defaultLights: true,
              enableSelectingObjects: true
            }
          );

          // Create a red sphere
          var sphere = tb.sphere({
            color: 'red',
            material: 'MeshToonMaterial'
          }).setCoords(origin);

          // Add event listeners
          sphere.addEventListener('ObjectMouseOver', onObjectMouseOver, false);
          sphere.addEventListener('ObjectMouseOut', onObjectMouseOut, false);

          // Add sphere to the scene
          tb.add(sphere);
        },

        render: function(gl, matrix) {
          tb.update();
        }
      });
    });

    // Event handlers
    function onObjectMouseOver(e) {
      console.log("Mouse over: " + e.detail.name);
    }

    function onObjectMouseOut(e) {
      console.log("Mouse out: " + e.detail.name);
    }
  </script>
</body>

Code Breakdown

1. Initialize the Map

var map = new mapboxgl.Map({
  container: 'map',           // DOM element ID
  style: 'mapbox://styles/mapbox/light-v9',
  center: origin,             // [lng, lat]
  zoom: 17,
  pitch: 60,                  // 3D perspective angle
  antialias: true             // Smooth 3D rendering
});
Set pitch to a value greater than 0 to enable 3D perspective. Values typically range from 0-60 degrees.

2. Create a Custom Layer

Threebox works within a Mapbox custom layer:
map.addLayer({
  id: 'custom_layer',
  type: 'custom',
  renderingMode: '3d',
  onAdd: function(map, mbxContext) {
    // Initialize Threebox here
  },
  render: function(gl, matrix) {
    // Update Threebox on each frame
    tb.update();
  }
});

3. Initialize Threebox

window.tb = new Threebox(
  map,                        // Mapbox map instance
  mbxContext,                 // Mapbox GL context
  {
    defaultLights: true,      // Add default lighting
    enableSelectingObjects: true  // Enable object selection
  }
);

4. Create and Add Objects

var sphere = tb.sphere({
  color: 'red',
  material: 'MeshToonMaterial'
}).setCoords(origin);

tb.add(sphere);

Threebox Initialization Options

Here are the key configuration options:
OptionTypeDefaultDescription
defaultLightsbooleanfalseAdd default scene lighting
enableSelectingObjectsbooleanfalseEnable 3D object selection
enableSelectingFeaturesbooleanfalseEnable fill-extrusion selection
enableDraggingObjectsbooleanfalseEnable dragging with [Shift]
enableRotatingObjectsbooleanfalseEnable rotation with [Alt]
enableTooltipsbooleanfalseShow default tooltips
realSunlightbooleanfalseUse real sun position
skybooleanfalseAdd atmospheric sky layer
terrainbooleanfalseEnable terrain integration

Common Geometries

Threebox provides built-in geometry helpers:
var sphere = tb.sphere({
  radius: 50,
  color: 'blue',
  material: 'MeshStandardMaterial',
  units: 'meters'
}).setCoords([lng, lat, altitude]);

Coordinate System

Threebox uses [longitude, latitude, altitude] coordinates:
// San Francisco coordinates
var origin = [-122.4340, 37.7353, 1];

// Set object position
sphere.setCoords(origin);

// Get object position
var coords = sphere.coordinates;

Updating the Scene

Call tb.update() in the render function to update the scene:
render: function(gl, matrix) {
  tb.update();  // Required on every frame
}
Without tb.update(), objects won’t render or update properly.

Performance Monitoring

Add Stats.js to monitor performance:
import Stats from 'https://threejs.org/examples/jsm/libs/stats.module.js';

let stats = new Stats();
map.getContainer().appendChild(stats.dom);

function animate() {
  requestAnimationFrame(animate);
  stats.update();
}
animate();

Complete Example with Multiple Objects

Here’s an example with multiple object types:
map.on('style.load', function() {
  map.addLayer({
    id: 'custom_layer',
    type: 'custom',
    renderingMode: '3d',
    onAdd: function(map, mbxContext) {
      window.tb = new Threebox(
        map,
        mbxContext,
        {
          defaultLights: true,
          enableSelectingObjects: true,
          enableTooltips: true
        }
      );

      // Add a sphere
      var sphere = tb.sphere({
        radius: 30,
        color: 'green',
        material: 'MeshPhysicalMaterial',
        units: 'meters'
      }).setCoords([-122.3512, 47.6202, 0]);
      tb.add(sphere);

      // Add a box
      var geometry = new THREE.BoxGeometry(30, 60, 120);
      var redMaterial = new THREE.MeshPhongMaterial({
        color: 0x660000,
        side: THREE.DoubleSide
      });
      var cube = tb.Object3D({
        obj: new THREE.Mesh(geometry, redMaterial),
        units: 'meters'
      }).setCoords([-122.34548, 47.617538, 0]);
      cube.addTooltip("This is a red cube", true);
      tb.add(cube);

      // Add a line
      var line = tb.line({
        geometry: [
          [-122.3512, 47.6202, 0],
          [-122.34548, 47.617538, 50],
          [-122.3491, 47.6207, 0]
        ],
        width: 3,
        color: 'yellow'
      });
      tb.add(line);
    },

    render: function(gl, matrix) {
      tb.update();
    }
  });
});

Azure Maps Setup

To use Threebox with Azure Maps:
<!DOCTYPE html>
<html>
<head>
  <!-- Azure Maps SDK -->
  <link rel="stylesheet" href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.css" type="text/css">
  <script src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.js"></script>
  
  <!-- Threebox -->
  <script src="../dist/threebox.js"></script>
  <link href="../dist/threebox.css" rel="stylesheet" />
</head>
<body>
  <div id="myMap" style="width: 100%; height: 100%;"></div>

  <script>
    // Initialize Azure Map
    var map = new atlas.Map("myMap", {
      center: [-122.3491, 47.6207],
      subscriptionKey: config.subscriptionKey,
      zoom: 16,
      pitch: 60,
      style: "satellite",
      antialias: true
    });

    map.events.add("style.load", function() {
      let intMap = map.map;  // Get internal Mapbox GL map
      
      window.tb = new Threebox(
        intMap,
        intMap.getCanvas().getContext('webgl'),
        { defaultLights: true }
      );

      intMap.addLayer({
        id: 'custom_layer',
        type: 'custom',
        renderingMode: '3d',
        onAdd: function(map, mbxContext) {
          // Add 3D objects here
        },
        render: function(gl, matrix) {
          tb.update();
        }
      });
    });
  </script>
</body>
</html>
Access the internal Mapbox GL map via map.map when using Azure Maps.

Troubleshooting

Objects not appearing?
  • Ensure tb.update() is called in the render function
  • Check that coordinates are valid [lng, lat, alt]
  • Verify the map has loaded (style.load event)
  • Confirm renderingMode: '3d' is set
Performance issues?
  • Reduce the number of objects
  • Use lower-poly models
  • Disable shadows if not needed
  • Check Stats.js for FPS monitoring
GLB models not loading?
  • Add .glb MIME type to your web server config
  • Ensure the file path is correct
  • Check browser console for errors

Next Steps

Load 3D Models

Learn how to load and configure GLB/GLTF models

Add Animations

Animate objects along paths

Enable Interactions

Add selection, dragging, and rotation

API Reference

Complete API documentation

Build docs developers (and LLMs) love