Skip to main content

Overview

Proper memory management is critical when using Threebox in web applications. Without cleanup, 3D objects, textures, and WebGL resources can accumulate, causing memory leaks and performance degradation.

The dispose() Method

The most important method for resource cleanup is dispose(), which completely frees all Threebox, Three.js, and Mapbox resources.

Complete Disposal

Threebox.js:1092-1115
await tb.dispose();
The dispose() method performs comprehensive cleanup:
Threebox.js:1092-1114
async dispose() {
  console.log(this.memory());
  
  return new Promise((resolve) => {
    resolve(
      this.clear(null, true).then((resolve) => {
        this.map.remove();
        this.map = {};
        this.scene.remove(this.world);
        this.world.children = [];
        this.world = null;
        this.objectsCache.clear();
        this.labelRenderer.dispose();
        console.log(this.memory());
        this.renderer.dispose();
        return resolve;
      })
    );
  });
}

What dispose() Does

1

Log Initial Memory

Records memory usage before cleanup for comparison
2

Clear All Objects

Calls clear(null, true) to remove and dispose all 3D objects
3

Remove Map Instance

Disposes the Mapbox map and clears the reference
4

Clean Scene

Removes world from scene and clears children
5

Clear Cache

Empties the objectsCache Map
6

Dispose Renderer

Frees WebGL context and renderer resources
7

Log Final Memory

Shows memory usage after cleanup

When to Use dispose()

Critical: Always call dispose() before navigating away from a page that uses Threebox, or when completely removing Threebox from your application.
// Single-page application cleanup
function navigateAway() {
  if (window.tb) {
    tb.dispose().then(() => {
      // Navigate to new page
      window.location.href = '/other-page';
    });
  }
}

// React/Vue component unmount
componentWillUnmount() {
  if (this.tb) {
    this.tb.dispose();
  }
}

// Before page unload
window.addEventListener('beforeunload', () => {
  if (window.tb) {
    tb.dispose();
  }
});

The clear() Method

For partial cleanup without removing the entire Threebox instance:
Threebox.js:947-971
// Clear all objects
await tb.clear();

// Clear objects from specific layer
await tb.clear('custom_layer');

// Clear and dispose (used by dispose method)
await tb.clear(null, true);

Clear Implementation

Threebox.js:947-971
async clear(layerId = null, dispose = false) {
  return new Promise((resolve, reject) => {
    let objects = [];
    this.world.children.forEach(function (object) {
      objects.push(object);
    });
    
    for (let i = 0; i < objects.length; i++) {
      let obj = objects[i];
      // If layerId, check the layer to remove, otherwise always remove
      if (obj.layer === layerId || !layerId) {
        this.remove(obj);
      }
    }
    
    if (dispose) {
      this.objectsCache.forEach((value) => {
        value.promise.then(obj => {
          obj.dispose();
          obj = null;
        })
      })
    }
    
    resolve("clear");
  });
}

Object Removal

Individual object removal with proper cleanup:
Threebox.js:938-944
tb.remove(obj) {
  if (this.map.selectedObject && obj.uuid == this.map.selectedObject.uuid) {
    this.map.unselectObject();
  }
  if (this.map.draggedObject && obj.uuid == this.map.draggedObject.uuid) {
    this.map.draggedObject = null;
  }
  if (obj.dispose) obj.dispose();
  this.world.remove(obj);
  obj = null;
}

Safe Object Removal

// Remove by reference
let model = tb.loadObj(options, callback);
tb.remove(model);

// Remove by name
tb.removeByName('my-object-name');

// Remove all objects from a layer
tb.clear('custom-layer-id');

Memory Monitoring

Threebox provides methods to track memory usage:

Memory Statistics

Threebox.js:1173
let memory = tb.memory();
console.log('Geometries:', memory.geometries);
console.log('Textures:', memory.textures);
Returns the memory object from THREE.WebGLRenderer.info:
{
  geometries: 45,  // Number of geometries in memory
  textures: 12     // Number of textures in memory
}

Program Count

Threebox.js:1175
let programCount = tb.programs();
console.log('WebGL Programs:', programCount);

Monitoring Example

function logMemoryUsage() {
  const mem = tb.memory();
  const progs = tb.programs();
  
  console.table({
    'Geometries': mem.geometries,
    'Textures': mem.textures,
    'Programs': progs,
    'Objects': tb.world.children.length,
    'Cached': tb.objectsCache.size
  });
}

// Log periodically
setInterval(logMemoryUsage, 5000);

// Log before/after operations
console.log('Before loading:');
logMemoryUsage();

tb.loadObj(options, (model) => {
  tb.add(model);
  console.log('After loading:');
  logMemoryUsage();
});

ObjectsCache Management

The objectsCache Map stores loaded models for reuse:
Threebox.js:76
this.objectsCache = new Map();

Cache Lifecycle

// Cached automatically when clone: true (default)
tb.loadObj({ obj: 'model.glb', type: 'gltf' }, callback);

// Check cache size
console.log('Cached models:', tb.objectsCache.size);

// Clear cache (called by dispose)
tb.objectsCache.clear();

Layer Cleanup

When removing layers, clean up associated objects:
Threebox.js:974-978
tb.removeLayer(layerId) {
  this.clear(layerId, true).then(() => {
    this.map.removeLayer(layerId);
  });
}

Best Practices

Dispose Before Navigate

Always call dispose() before leaving the page

Clear Unused Objects

Regularly remove objects no longer needed

Monitor Memory

Use memory() and programs() to track usage

Use Object Caching

Keep clone: true for better memory efficiency

Complete Memory Management Example

class ThreeboxManager {
  constructor(mapContainer) {
    this.map = null;
    this.tb = null;
    this.objects = new Map();
  }
  
  initialize() {
    this.map = new mapboxgl.Map({
      container: 'map',
      style: 'mapbox://styles/mapbox/streets-v11',
      center: [-122.4194, 37.7749],
      zoom: 15
    });
    
    this.tb = new Threebox(
      this.map,
      this.map.getCanvas().getContext('webgl'),
      { defaultLights: true }
    );
    
    this.setupLayers();
  }
  
  setupLayers() {
    this.map.on('style.load', () => {
      this.map.addLayer({
        id: 'custom-layer',
        type: 'custom',
        renderingMode: '3d',
        onAdd: (map, gl) => this.onAdd(map, gl),
        render: (gl, matrix) => this.tb.update()
      });
    });
  }
  
  onAdd(map, gl) {
    // Add objects and track them
    this.loadModel('building', {
      type: 'gltf',
      obj: './building.glb',
      coords: [-122.4194, 37.7749]
    });
  }
  
  loadModel(id, config) {
    this.tb.loadObj(config, (model) => {
      model.setCoords(config.coords);
      this.tb.add(model);
      this.objects.set(id, model);
    });
  }
  
  removeModel(id) {
    const model = this.objects.get(id);
    if (model) {
      this.tb.remove(model);
      this.objects.delete(id);
    }
  }
  
  clearAll() {
    this.objects.clear();
    return this.tb.clear(null, true);
  }
  
  getMemoryStats() {
    return {
      memory: this.tb.memory(),
      programs: this.tb.programs(),
      objects: this.objects.size,
      cached: this.tb.objectsCache.size
    };
  }
  
  async dispose() {
    console.log('Before dispose:', this.getMemoryStats());
    
    this.objects.clear();
    
    if (this.tb) {
      await this.tb.dispose();
      this.tb = null;
    }
    
    this.map = null;
    
    console.log('Disposal complete');
  }
}

// Usage
const manager = new ThreeboxManager();
manager.initialize();

// Monitor memory
setInterval(() => {
  console.log('Memory:', manager.getMemoryStats());
}, 10000);

// Clean up before navigation
window.addEventListener('beforeunload', async () => {
  await manager.dispose();
});

Memory Leak Prevention Checklist

1

Initialize Once

Create Threebox instance once per page load
2

Track Objects

Maintain references to added objects
3

Remove Properly

Use tb.remove() instead of just clearing references
4

Clear Layers

Use tb.clear(layerId) when removing layers
5

Style Changes

Use tb.setStyle() instead of map.setStyle()
6

Monitor Usage

Regularly check tb.memory() and tb.programs()
7

Dispose on Exit

Call tb.dispose() before navigation or unmount
Framework Integration: In React, Vue, or Angular, always dispose Threebox in the component’s cleanup lifecycle method (componentWillUnmount, beforeDestroy, ngOnDestroy).

Build docs developers (and LLMs) love