MapSnippets logoMapSnippets logo

Heatmap Layer

Visualize point density as a continuous color gradient Each library has its own approach: here's how they compare.

Quick Recommendations

Leaflet — Ideal for quick heatmap overlays using the lightweight leaflet.heat plugin with simple [lat, lng, intensity] data arrays.

MapLibre GL JS — Best when the heatmap is part of a larger vector tile application and you need GPU-rendered density with zoom-dependent radius expressions.

OpenLayers — Ideal when heatmap data comes from existing vector features and you need built-in weight and gradient customization via ol.layer.Heatmap.

MapTiler SDK — For MapTiler SDK users, this means MapLibre's native heatmap layer plus automatic MapTiler basemap integration.

At a Glance

LeafletMapLibreOpenLayersSDK
Coordinates[lat, lng][lng, lat]fromLonLat([lng, lat])[lng, lat]
RenderingDOM + Canvas (2D)WebGL (GPU, 60fps)Canvas + WebGLWebGL (GPU, 60fps)
Bundle size~42 KB gzipped~250 KB gzipped~290 KB gzipped (tree-shakeable)~280 KB gzipped (includes MapLibre)
Learning curveEasiest (simple API, great tutorials)Moderate (source/layer model, expressions)Steepest (projections, OGC standards, verbose API)Easy (MapLibre API with convenience helpers)
3D supportNo native 3D (2D only)Full: terrain, fill-extrusion, globe, skyLimited: WebGL layers, no native globeFull (same as MapLibre)
Code lines10222223

Code Snippets

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

Leaflet10 linesShow code
arrow_forwardFull example
const map = L.map('map').setView([48.8566, 2.3522], 13);
L.tileLayer('https://api.maptiler.com/maps/streets-v4/256/{z}/{x}/{y}.png?key=YOUR_MAPTILER_KEY', {
  tileSize: 256,
  attribution: '&copy; <a href="https://www.maptiler.com/">MapTiler</a> &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>'
}).addTo(map);
const points = [];
for (let i = 0; i < 200; i++) {
  points.push([48.83 + Math.random() * 0.06, 2.28 + Math.random() * 0.15, Math.random()]);
}
L.heatLayer(points, { radius: 20, blur: 15, maxZoom: 17, max: 1.0, gradient: { 0.2: '#0000ff', 0.4: '#00ffff', 0.6: '#00ff00', 0.8: '#ffff00', 1.0: '#ff0000' } }).addTo(map);
MapLibre GL JS22 linesShow code
arrow_forwardFull example
const map = new maplibregl.Map({
  container: 'map',
  style: 'https://api.maptiler.com/maps/dataviz-dark/style.json?key=YOUR_MAPTILER_KEY',
  center: [14.42, 50.08],
  zoom: 10
});
const pts = [];
for (let i = 0; i < 500; i++) {
  const a = Math.random() * Math.PI * 2, r = Math.random() * 0.1;
  pts.push({ type: 'Feature', geometry: { type: 'Point', coordinates: [14.42 + Math.cos(a) * r, 50.08 + Math.sin(a) * r] },
    properties: { weight: Math.random() } });
}
map.on('load', () => {
  map.addSource('heat', { type: 'geojson', data: { type: 'FeatureCollection', features: pts } });
  map.addLayer({ id: 'heatmap', type: 'heatmap', source: 'heat', paint: {
    'heatmap-weight': ['get', 'weight'],
    'heatmap-intensity': ['interpolate', ['linear'], ['zoom'], 8, 1, 13, 3],
    'heatmap-color': ['interpolate', ['linear'], ['heatmap-density'], 0, 'rgba(33,102,172,0)', 0.2, '#4393c3', 0.4, '#d6604d', 0.6, '#f4a582', 0.8, '#fddbc7', 1, '#fff'],
    'heatmap-radius': ['interpolate', ['linear'], ['zoom'], 8, 10, 13, 25],
    'heatmap-opacity': 0.8
  } });
});
OpenLayers22 linesShow code
arrow_forwardFull example
var features = [];
for (var i = 0; i < 500; i++) {
  var feature = new ol.Feature({
    geometry: new ol.geom.Point(ol.proj.fromLonLat([2.3 + Math.random() * 0.15, 48.83 + Math.random() * 0.06])),
    weight: Math.random()
  });
  features.push(feature);
}
var heatmap = new ol.layer.Heatmap({
  source: new ol.source.Vector({ features: features }),
  blur: 15,
  radius: 10,
  weight: function(feature) { return feature.get('weight'); }
});
var map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({ source: new ol.source.XYZ({ url: 'https://api.maptiler.com/maps/streets-v4/256/{z}/{x}/{y}.png?key=YOUR_MAPTILER_KEY', attributions: '&copy; <a href="https://www.maptiler.com/copyright/">MapTiler</a>' }) }),
    heatmap
  ],
  view: new ol.View({ center: ol.proj.fromLonLat([2.37, 48.86]), zoom: 13 })
});
MapTiler SDK23 linesShow code
arrow_forwardFull example
maptilersdk.config.apiKey = 'YOUR_MAPTILER_KEY';
const map = new maptilersdk.Map({
  container: 'map',
  style: maptilersdk.MapStyle.STREETS,
  center: [-98, 39],
  zoom: 3
});
map.on('load', () => {
  map.addSource('quakes', {
    type: 'geojson',
    data: 'https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.geojson'
  });
  map.addLayer({
    id: 'quake-heat', type: 'heatmap', source: 'quakes',
    paint: {
      'heatmap-weight': ['interpolate', ['linear'], ['get', 'mag'], 0, 0, 6, 1],
      'heatmap-radius': ['interpolate', ['linear'], ['zoom'], 0, 2, 9, 20],
      'heatmap-color': ['interpolate', ['linear'], ['heatmap-density'],
        0, 'rgba(0,0,0,0)', 0.2, '#e8d5f5', 0.4, '#ce93d8',
        0.6, '#9C27B0', 0.8, '#7b1fa2', 1, '#4a148c']
    }
  });
});

Things to Know

Implementation-specific details that matter for heatmap layer.

Leaflet

Requires the external leaflet.heat plugin; input is a simple array of [lat, lng, intensity] values, not GeoJSON.

MapLibre GL JS

Native heatmap layer type with paint properties for radius, weight, color ramp, and intensity: no plugins needed.

OpenLayers

ol.layer.Heatmap is built-in; weight is set per-feature via a property name, and gradient is an array of CSS colors.

MapTiler SDK

Identical heatmap layer configuration to MapLibre using the same paint properties and GeoJSON source.

Which Library to Use?

auto_awesome

Build Maps with AI

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