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
The dispose() method performs comprehensive cleanup:
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
Log Initial Memory
Records memory usage before cleanup for comparison
Clear All Objects
Calls clear(null, true) to remove and dispose all 3D objects
Remove Map Instance
Disposes the Mapbox map and clears the reference
Clean Scene
Removes world from scene and clears children
Clear Cache
Empties the objectsCache Map
Dispose Renderer
Frees WebGL context and renderer resources
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.
Navigation Example
// 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:
// 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
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:
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
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
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:
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:
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
Initialize Once
Create Threebox instance once per page load
Track Objects
Maintain references to added objects
Remove Properly
Use tb.remove() instead of just clearing references
Clear Layers
Use tb.clear(layerId) when removing layers
Style Changes
Use tb.setStyle() instead of map.setStyle()
Monitor Usage
Regularly check tb.memory() and tb.programs()
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).