A MapLibre GL JS custom layer for rendering interpolated heatmaps (extracting average values) with WebGL shaders. Works with MapLibre GL JS v5+ (WebGL2).
This library was greatly inspired by temperature-map-gl and depends on Earcut.
MapLibre provides a built-in heatmap layer that represents the density of points:
This library provides an interpolated heatmap that calculates colors based on the weighted average of surrounding point values:
# npm
npm install maplibre-gl-interpolate-heatmap maplibre-gl
# bun
bun add maplibre-gl-interpolate-heatmap maplibre-gl
# JSR
bunx jsr add @geoql/maplibre-gl-interpolate-heatmapimport maplibregl from 'maplibre-gl';
import { MaplibreInterpolateHeatmapLayer } from 'maplibre-gl-interpolate-heatmap';
import 'maplibre-gl/dist/maplibre-gl.css';
const map = new maplibregl.Map({
container: 'map',
style: 'https://demotiles.maplibre.org/style.json',
center: [0, 20],
zoom: 2,
});
map.on('load', () => {
const layer = new MaplibreInterpolateHeatmapLayer({
id: 'temperature',
data: [
{ lat: 62.47, lon: 6.18, val: 16 },
{ lat: 48.09, lon: -1.37, val: 20 },
{ lat: 35.68, lon: 139.69, val: 28 },
],
opacity: 0.5,
minValue: 10,
maxValue: 35,
});
map.addLayer(layer);
});| Option | Type | Default | Description |
|---|---|---|---|
id |
string |
'' |
Unique layer ID |
data |
Array<{lat, lon, val}> |
[] |
Data points (latitude must be within -85° to 85°) |
opacity |
number |
0.5 |
Layer opacity (0-1) |
minValue |
number |
Infinity |
Value mapped to blue color |
maxValue |
number |
-Infinity |
Value mapped to red color |
p |
number |
3 |
IDW power parameter (higher = more uniform colors around points) |
framebufferFactor |
number |
0.3 |
Computation resolution (0-1, lower = faster but less accurate) |
aoi |
Array<{lat, lon}> |
[] |
Area of interest polygon (empty = entire map) |
valueToColor |
string |
See below | GLSL function mapping value (0-1) to vec3 color |
valueToColor4 |
string |
See below | GLSL function mapping value to vec4 with alpha |
Default valueToColor (blue → green → red gradient):
vec3 valueToColor(float value) {
return vec3(
max((value - 0.5) * 2.0, 0.0),
1.0 - 2.0 * abs(value - 0.5),
max((0.5 - value) * 2.0, 0.0)
);
}Default valueToColor4:
vec4 valueToColor4(float value, float defaultOpacity) {
return vec4(valueToColor(value), defaultOpacity);
}Colors are computed using Inverse Distance Weighting (IDW):
- Render N textures, each containing
wi * ui(red) andwi(green) per fragment - Blend textures with accumulator to sum all contributions
- Divide red by green channel to get interpolated value
u(x) - Map value to color using the GLSL color function
Where wi(x) = 1 / d(x, xi)^p is the weight based on distance.
- Node.js >= 24.0.0
- MapLibre GL JS >= 3.0.0 (v5+ recommended for WebGL2)
- Fork and create a feature branch from
main - Make changes following conventional commits
- Ensure commits are signed (why?)
- Submit a PR
bun install
bun run build
bun run lint
bun run formatMIT © Vinayak Kulkarni

