Documentation Index Fetch the complete documentation index at: https://mintlify.com/jscastro76/threebox/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Threadbox uses CSS2DRenderer to display HTML labels and tooltips that stay anchored to 3D objects. These labels are rendered as HTML/CSS, allowing for rich styling and interactivity while automatically handling occlusion and positioning.
Label Renderer
Threadbox automatically creates a LabelRenderer that overlays the map canvas:
const tb = new Threebox ( map , gl , {
defaultLights: true ,
enableTooltips: true // Enable default tooltips
});
// Label renderer is automatically available
console . log ( tb . labelRenderer );
Labels
Creating Labels
Add persistent labels to objects:
// Create a label object
const label = tb . label ({
htmlElement: '<div>City Hall</div>' ,
cssClass: 'custom-label' ,
alwaysVisible: true ,
feature: geoJsonFeature
});
label . setCoords ([ - 122.4194 , 37.7749 , 50 ]);
tb . add ( label );
Label Options
Option Type Default Description htmlElementstring or HTMLElementRequired HTML content for the label cssClassstring'label3D'CSS class for styling alwaysVisiblebooleanfalseWhether label is always visible featureobjectnullGeoJSON feature data
Adding Labels to Models
Add labels to 3D models:
tb . loadObj ({
obj: '/models/building.glb' ,
type: 'gltf' ,
scale: 1 ,
units: 'meters'
}, ( model ) => {
model . setCoords ([ - 122.4194 , 37.7749 ]);
tb . add ( model );
// Add label to model
const labelHTML = '<div class="building-label">Office Building</div>' ;
model . addLabel (
labelHTML ,
true , // visible
model . anchor , // center position
0.5 // height multiplier
);
});
Removing Labels
// Remove label from object
model . removeLabel ();
// Access label before removing
if ( model . label ) {
console . log ( 'Label exists' );
model . removeLabel ();
}
Enable automatic tooltips for all objects:
const tb = new Threebox ( map , gl , {
enableTooltips: true ,
enableSelectingObjects: true
});
// Tooltips will show automatically on hover/select
Create standalone tooltip objects:
const tooltip = tb . tooltip ({
text: 'Click for more info' ,
mapboxStyle: true , // Use Mapbox popup style
feature: geoJsonFeature
});
tooltip . setCoords ([ - 122.4194 , 37.7749 , 25 ]);
tb . add ( tooltip );
// Initially hidden
tooltip . tooltip . visible = false ;
Option Type Default Description textstringRequired Tooltip text content mapboxStylebooleanfalseUse Mapbox GL popup styling featureobjectnullGeoJSON feature data
model . addTooltip (
'Interactive Model' , // tooltip text
true , // use Mapbox style
model . anchor , // center position
true , // custom tooltip
1.0 // height multiplier
);
// Show/hide tooltip
if ( model . tooltip ) {
model . tooltip . visible = true ;
}
// Remove tooltip
model . removeTooltip ();
Dynamic Tooltip Content
model . addEventListener ( 'ObjectMouseOver' , ( e ) => {
const object = e . detail ;
// Create dynamic tooltip
const tooltipText = `
<strong> ${ object . userData . name } </strong><br>
Height: ${ object . coordinates [ 2 ] } m<br>
Type: ${ object . userData . type }
` ;
object . addTooltip ( tooltipText , true );
object . tooltip . visible = true ;
});
model . addEventListener ( 'ObjectMouseOut' , ( e ) => {
if ( e . detail . tooltip ) {
e . detail . tooltip . visible = false ;
}
});
Show contextual help during interactions:
const tb = new Threebox ( map , gl , {
enableHelpTooltips: true // Enable help tooltips
});
// Add help tooltip to object
model . addHelp (
'Press Shift+Drag to move' ,
'help' , // object name
false , // not Mapbox style
model . anchor , // position
0.5 // height
);
// Remove help tooltip
model . removeHelp ();
Custom Styling
CSS Classes
Default CSS classes:
/* Default label style */
.label3D {
background-color : rgba ( 255 , 255 , 255 , 0.9 );
border-radius : 4 px ;
padding : 4 px 8 px ;
font-size : 12 px ;
font-weight : bold ;
pointer-events : none ;
}
/* Default tooltip style */
.tooltip3D {
background-color : rgba ( 0 , 0 , 0 , 0.8 );
color : white ;
border-radius : 3 px ;
padding : 5 px 10 px ;
font-size : 11 px ;
white-space : nowrap ;
pointer-events : none ;
}
Custom Label Styles
// Create label with custom class
const label = tb . label ({
htmlElement: '<div>Custom Styled Label</div>' ,
cssClass: 'my-custom-label'
});
/* Custom label styling */
.my-custom-label {
background : linear-gradient ( 135 deg , #667eea 0 % , #764ba2 100 % );
color : white ;
padding : 8 px 16 px ;
border-radius : 20 px ;
font-weight : bold ;
box-shadow : 0 4 px 6 px rgba ( 0 , 0 , 0 , 0.3 );
pointer-events : auto ; /* Make it clickable */
cursor : pointer ;
}
.my-custom-label:hover {
transform : scale ( 1.05 );
box-shadow : 0 6 px 12 px rgba ( 0 , 0 , 0 , 0.4 );
}
Use Mapbox GL JS popup styling:
const tooltip = tb . tooltip ({
text: 'Building Information' ,
mapboxStyle: true // Uses Mapbox popup classes
});
This generates:
< div class = "label3D" >
< div class = "marker mapboxgl-popup-anchor-bottom" >
< div class = "mapboxgl-popup-tip" ></ div >
< div class = "mapboxgl-popup-content" >
< strong > Building Information </ strong >
</ div >
</ div >
</ div >
Label Positioning
Anchor Points
Position labels relative to object anchor:
model . addLabel (
'<div>Label Text</div>' ,
true ,
model . center , // Use center anchor
1.0 // At top of object
);
// Available anchors:
// model.center, model.top, model.bottom
// model.topLeft, model.topRight
// model.bottomLeft, model.bottomRight
// model.left, model.right
Height Adjustment
// Label at 50% of object height
model . addLabel ( html , true , model . anchor , 0.5 );
// Label at top of object
model . addLabel ( html , true , model . anchor , 1.0 );
// Label above object
model . addLabel ( html , true , model . anchor , 1.5 );
Direct CSS2D Objects
Create CSS2D objects directly:
const div = document . createElement ( 'div' );
div . className = 'custom-label' ;
div . textContent = 'Advanced Label' ;
const css2DLabel = model . addCSS2D (
div ,
'customLabel' , // object name
model . anchor , // center
1.0 // height
);
// Control visibility
css2DLabel . visible = true ;
// Remove
model . removeCSS2D ( 'customLabel' );
Visibility Control
Always Visible Labels
const label = tb . label ({
htmlElement: '<div>Permanent Label</div>' ,
alwaysVisible: true // Always shown
});
label . setCoords ( coordinates );
tb . add ( label );
Conditional Visibility
// Show label only when selected
model . addEventListener ( 'SelectedChange' , ( e ) => {
if ( model . label ) {
model . label . visible = e . detail . selected ;
}
});
// Show label on hover
model . addEventListener ( 'ObjectMouseOver' , () => {
if ( model . label ) model . label . visible = true ;
});
model . addEventListener ( 'ObjectMouseOut' , () => {
if ( model . label && ! model . label . alwaysVisible ) {
model . label . visible = false ;
}
});
Layer Visibility
Toggle labels by layer:
// Hide all labels in a layer
tb . labelRenderer . toggleLabels ( 'buildings-layer' , false );
// Show all labels in a layer
tb . labelRenderer . toggleLabels ( 'buildings-layer' , true );
Automatic tooltips for fill-extrusion features:
const tb = new Threebox ( map , gl , {
enableSelectingFeatures: true ,
enableTooltips: true
});
// Tooltips automatically appear on hover/select
// Content from feature.properties.name or feature.id
Complete Example
map . on ( 'load' , () => {
const tb = new Threebox ( map , gl , {
defaultLights: true ,
enableSelectingObjects: true ,
enableTooltips: true ,
enableHelpTooltips: true
});
map . addLayer ({
id: 'custom-layer' ,
type: 'custom' ,
renderingMode: '3d' ,
onAdd : function ( map , gl ) {
// Create permanent label
const cityLabel = tb . label ({
htmlElement: '<div class="city-label">San Francisco</div>' ,
cssClass: 'city-label' ,
alwaysVisible: true
});
cityLabel . setCoords ([ - 122.4194 , 37.7749 , 100 ]);
tb . add ( cityLabel );
// Load model with dynamic tooltip
tb . loadObj ({
obj: '/models/building.glb' ,
type: 'gltf' ,
scale: 1 ,
units: 'meters'
}, ( building ) => {
building . setCoords ([ - 122.4194 , 37.7749 , 0 ]);
tb . add ( building );
// Add label
building . addLabel (
'<div class="building-name">Office Building</div>' ,
false , // not always visible
building . center ,
1.0
);
// Show label and tooltip on hover
building . addEventListener ( 'ObjectMouseOver' , () => {
if ( building . label ) {
building . label . visible = true ;
}
// Add dynamic tooltip
const info = `
<strong>Office Building</strong><br>
Height: ${ building . modelHeight . toFixed ( 1 ) } m<br>
<em>Click for details</em>
` ;
building . addTooltip ( info , true );
building . tooltip . visible = true ;
});
building . addEventListener ( 'ObjectMouseOut' , () => {
if ( building . label && ! building . selected ) {
building . label . visible = false ;
}
if ( building . tooltip && ! building . selected ) {
building . tooltip . visible = false ;
}
});
// Keep label visible when selected
building . addEventListener ( 'SelectedChange' , ( e ) => {
if ( building . label ) {
building . label . visible = e . detail . selected ;
}
});
});
},
render : function ( gl , matrix ) {
tb . update ();
}
});
});
Optimize Label Performance
Limit the number of always-visible labels
Use alwaysVisible: false and show on hover/select
Disable tooltips globally if not needed: enableTooltips: false
Remove labels from off-screen objects
Use simple HTML/CSS (avoid complex animations)
See Also