Occluding Mapbox GL markers behind 3D buildings
If you’ve ever built a 3D application with Mapbox GL, you’ve inevitably hit this wall: HTML markers float above the WebGL canvas, ignoring the depth buffer entirely. Rotate the camera, and suddenly your ground-floor markers are punching straight through the roof of your 3D buildings.
While building our multi-tenant indoor 3D mapping and asset tracking tools at Spatial Map, this became a major roadblock. We needed rich DOM elements for hover cards and drag handles, so falling back to native symbol layers wasn’t an option. And since Mapbox’s occludedOpacity only covers 3D terrain (not custom fill-extrusions or floor slabs), we had to solve it ourselves.
The fix? An analytic ray-vs-prism cast. No shaders. No heavy queryRenderedFeatures. Just a single camera read per frame.
Get SOFTWARE ENGINEER | CTO | MULTIWARE SOLUTION’s stories in your inbox
Join Medium for free to get updates from this writer.
Here is how we made DOM markers behave like they actually belong in the scene:
- One Ray, Many Prisms: We cast a single ray from the camera to each marker’s world position.
- Altitude Slabs: For every shape that can cover a marker, we build a 2D footprint ring plus an altitude band
[baseM, topM]. - Lightweight Math: The ray is clipped against each shape’s altitude slab. If the clipped segment crosses the footprint, the marker is occluded.
- Highly Performant: It costs just a handful of multiply-adds per marker per frame — with zero GPU roundtrips.
We just published a complete technical deep-dive on the Spatial Map blog detailing the exact math, the edge cases we learned the hard way (like handling doors and ghost geometry), and how we tied the same prism list to native text-label occlusion.
If you are building facility maps, indoor editors, or digital twins on Mapbox GL, this is the depth-buffer trick you’ve been looking for.
Read the full deep-dive here: https://spatialmap.io/blog/en/mapbox-gl-marker-occlusion-3d-buildings









