MapSnippets logoMapSnippets logo

3D Building Extrusions

Add 3D extruded buildings from vector tile data using fill-extrusion layers Each library has its own approach: here's how they compare.

Quick Recommendations

MapLibre GL JS — Best when you need full control over fill-extrusion paint properties, light direction, and data-driven height expressions.

MapTiler SDK — For MapTiler SDK users, this means MapLibre's fill-extrusion layers plus automatic access to MapTiler building data.

At a Glance

MapLibreSDK
Coordinates[lng, lat][lng, lat]
RenderingWebGL (GPU, 60fps)WebGL (GPU, 60fps)
Bundle size~250 KB gzipped~280 KB gzipped (includes MapLibre)
Learning curveModerate (source/layer model, expressions)Easy (MapLibre API with convenience helpers)
3D supportFull: terrain, fill-extrusion, globe, skyFull (same as MapLibre)
Code lines3535

Code Snippets

Click a library to expand its code. Each snippet shows only the core logic: the scaffold handles HTML boilerplate.

MapLibre GL JS35 linesShow code
arrow_forwardFull example
const map = new maplibregl.Map({
  container: 'map',
  style: 'https://api.maptiler.com/maps/streets-v4/style.json?key=YOUR_MAPTILER_KEY',
  center: [14.4205, 50.0875],
  zoom: 16,
  pitch: 55,
  bearing: -20,
  canvasContextAttributes: { antialias: true }
});
map.addControl(new maplibregl.NavigationControl());
map.on('load', () => {
  const style = map.getStyle();
  const vectorSrc = Object.keys(style.sources).find(k => style.sources[k].type === 'vector');
  if (!vectorSrc) return;
  // Insert the 3D layer BEFORE the first label layer so labels stay on top.
  const labelLayerId = style.layers.find(l => l.type === 'symbol' && l.layout && l.layout['text-field'])?.id;
  map.addLayer({
    id: '3d-buildings',
    source: vectorSrc,
    'source-layer': 'building',
    type: 'fill-extrusion',
    minzoom: 14,
    filter: ['!=', ['get', 'hide_3d'], true],
    paint: {
      'fill-extrusion-color': '#aaa',
      'fill-extrusion-height': [
        'interpolate', ['linear'], ['zoom'],
        14, 0,
        16, ['coalesce', ['get', 'render_height'], 0]
      ],
      'fill-extrusion-base': ['coalesce', ['get', 'render_min_height'], 0],
      'fill-extrusion-opacity': 0.7
    }
  }, labelLayerId);
});
MapTiler SDK35 linesShow code
arrow_forwardFull example
maptilersdk.config.apiKey = 'YOUR_MAPTILER_KEY';
const map = new maptilersdk.Map({
  container: 'map',
  style: maptilersdk.MapStyle.STREETS,
  center: [2.3488, 48.8534],
  zoom: 15.5,
  pitch: 60,
  bearing: -17,
  canvasContextAttributes: { antialias: true }
});
map.on('load', () => {
  const style = map.getStyle();
  const src = Object.keys(style.sources).find(s => map.getSource(s).type === 'vector');
  if (!src) return;
  // Insert above basemap but below labels so labels stay on top.
  const labelLayerId = style.layers.find(l => l.type === 'symbol' && l.layout && l.layout['text-field'])?.id;
  map.addLayer({
    id: 'buildings-3d',
    type: 'fill-extrusion',
    source: src,
    'source-layer': 'building',
    minzoom: 14,
    filter: ['!=', ['get', 'hide_3d'], true],
    paint: {
      'fill-extrusion-color': '#9C27B0',
      'fill-extrusion-height': [
        'interpolate', ['linear'], ['zoom'],
        14, 0,
        16, ['coalesce', ['get', 'render_height'], 0]
      ],
      'fill-extrusion-base': ['coalesce', ['get', 'render_min_height'], 0],
      'fill-extrusion-opacity': 0.8
    }
  }, labelLayerId);
});

Things to Know

Implementation-specific details that matter for 3d building extrusions.

MapLibre GL JS

Fill-extrusion layers read height from vector tile properties; light settings (anchor, color, intensity) control the 3D appearance.

MapTiler SDK

Identical fill-extrusion configuration to MapLibre; the SDK provides no extra 3D building helpers.

Which Library to Use?

auto_awesome

Build Maps with AI

Generate code like these examples using mapping library skills in your IDE.