Create a Heatmap Layer in Maptoolkit Maps JS
A heatmap layer in Maptoolkit Maps JS aggregates the density of nearby point features and renders it as a smooth color gradient from cool to warm. The intensity, radius, and color ramp are all configurable with expressions that respond to zoom level and feature properties. Use heatmaps to visualize earthquake locations, accident density, user activity, or any dataset where the concentration of points is more meaningful than individual locations.
const API_KEY = 'YOUR_API_KEY';
const map = new maptoolkit.Map({
container: 'map',
apiKey: API_KEY,
style: `https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=${API_KEY}`,
center: [-119.5, 36.0],
zoom: 6,
attributionControl: { compact: false }
});
map.addControl(new maptoolkit.NavigationControl(), 'top-right');
map.on('load', () => {
map.addSource('earthquakes', {
type: 'geojson',
data: 'https://maplibre.org/maplibre-gl-js/docs/assets/earthquakes.geojson'
});
map.addLayer({
id: 'earthquakes-heatmap',
type: 'heatmap',
source: 'earthquakes',
maxzoom: 9,
paint: {
'heatmap-weight': ['interpolate', ['linear'], ['get', 'mag'], 0, 0, 6, 1],
'heatmap-intensity': ['interpolate', ['linear'], ['zoom'], 0, 1, 9, 3],
'heatmap-color': [
'interpolate', ['linear'], ['heatmap-density'],
0, 'rgba(33,102,172,0)',
0.2, 'rgb(103,169,207)',
0.4, 'rgb(209,229,240)',
0.6, 'rgb(253,219,199)',
0.8, 'rgb(239,138,98)',
1, 'rgb(178,24,43)'
],
'heatmap-radius': ['interpolate', ['linear'], ['zoom'], 0, 2, 9, 20],
'heatmap-opacity': ['interpolate', ['linear'], ['zoom'], 7, 1, 9, 0]
}
});
map.addLayer({
id: 'earthquakes-point',
type: 'circle',
source: 'earthquakes',
minzoom: 7,
paint: {
'circle-radius': ['interpolate', ['linear'], ['zoom'],
7, ['interpolate', ['linear'], ['get', 'mag'], 1, 1, 6, 4],
16, ['interpolate', ['linear'], ['get', 'mag'], 1, 5, 6, 50]
],
'circle-color': ['interpolate', ['linear'], ['get', 'mag'],
1, 'rgba(33,102,172,0)',
2, 'rgb(103,169,207)',
3, 'rgb(209,229,240)',
4, 'rgb(253,219,199)',
5, 'rgb(239,138,98)',
6, 'rgb(178,24,43)'
],
'circle-stroke-color': 'white',
'circle-stroke-width': 1,
'circle-opacity': ['interpolate', ['linear'], ['zoom'], 7, 0, 8, 1]
}
});
});<!DOCTYPE html>
<html lang="en">
<head>
<title>Create a Heatmap Layer - Maptoolkit Maps JS</title>
<meta property="og:description" content="Visualize point density using a heatmap layer." />
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://unpkg.com/@maptoolkit/maps@11.0.0-beta.3/dist/maptoolkit.js"></script>
<link rel="stylesheet" href="https://unpkg.com/@maptoolkit/maps@11.0.0-beta.3/dist/maptoolkit.css" />
<style>
html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
#map { width: 100%; height: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
const API_KEY = 'YOUR_API_KEY';
const map = new maptoolkit.Map({
container: 'map',
apiKey: API_KEY,
style: `https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=${API_KEY}`,
center: [-119.5, 36.0],
zoom: 6,
attributionControl: { compact: false }
});
map.addControl(new maptoolkit.NavigationControl(), 'top-right');
map.on('load', () => {
map.addSource('earthquakes', {
type: 'geojson',
data: 'https://maplibre.org/maplibre-gl-js/docs/assets/earthquakes.geojson'
});
map.addLayer({
id: 'earthquakes-heatmap',
type: 'heatmap',
source: 'earthquakes',
maxzoom: 9,
paint: {
'heatmap-weight': ['interpolate', ['linear'], ['get', 'mag'], 0, 0, 6, 1],
'heatmap-intensity': ['interpolate', ['linear'], ['zoom'], 0, 1, 9, 3],
'heatmap-color': [
'interpolate', ['linear'], ['heatmap-density'],
0, 'rgba(33,102,172,0)',
0.2, 'rgb(103,169,207)',
0.4, 'rgb(209,229,240)',
0.6, 'rgb(253,219,199)',
0.8, 'rgb(239,138,98)',
1, 'rgb(178,24,43)'
],
'heatmap-radius': ['interpolate', ['linear'], ['zoom'], 0, 2, 9, 20],
'heatmap-opacity': ['interpolate', ['linear'], ['zoom'], 7, 1, 9, 0]
}
});
map.addLayer({
id: 'earthquakes-point',
type: 'circle',
source: 'earthquakes',
minzoom: 7,
paint: {
'circle-radius': ['interpolate', ['linear'], ['zoom'],
7, ['interpolate', ['linear'], ['get', 'mag'], 1, 1, 6, 4],
16, ['interpolate', ['linear'], ['get', 'mag'], 1, 5, 6, 50]
],
'circle-color': ['interpolate', ['linear'], ['get', 'mag'],
1, 'rgba(33,102,172,0)',
2, 'rgb(103,169,207)',
3, 'rgb(209,229,240)',
4, 'rgb(253,219,199)',
5, 'rgb(239,138,98)',
6, 'rgb(178,24,43)'
],
'circle-stroke-color': 'white',
'circle-stroke-width': 1,
'circle-opacity': ['interpolate', ['linear'], ['zoom'], 7, 0, 8, 1]
}
});
});
</script>
</body>
</html>Use the prompt below with any LLM to get the same result. Make sure the Maptoolkit MCP server is connected first — check out AI Integration & MCP to get started.
How it works
A heatmap layer aggregates point density in the renderer. You give it points and it produces
a continuous surface, so there is no binning or gridding step on your side.
Four paint properties do the work, and they interact. heatmap-weight is how much one point
contributes, usually driven from a property such as magnitude. heatmap-intensity multiplies
the whole surface. heatmap-color maps density to colour and must start at 0 with a
transparent value, or the entire map is tinted rather than just the hot areas.
heatmap-radius sets the influence of each point in pixels, so it should usually grow with
zoom.
maxzoom: 9 on the layer is a deliberate hand-off: a heatmap stops being meaningful when you
are zoomed in far enough to see individual points, so the usual pattern is a heatmap at low
zoom and a circle layer above that.
Heatmaps show density, not value. A hundred small events look hotter than one large one unless weight says otherwise.
Next steps
Weighting is where a heatmap becomes honest. Unweighted, it shows where records are dense, which is often a map of where people report rather than where things happen; weighting by magnitude or value fixes that.
The standard pairing is a heatmap at low zoom handing over to individual points as the user zooms in, since a heatmap says nothing useful once you can see the features themselves. A legend matters too, because heat colours look quantitative and are not, and clustering is the alternative when the individual features still matter.