---
title: "Create a heatmap layer | JavaScript maps SDK"
description: "Visualize point data frequency using a custom heatmap layer."
---

> For AI agents: a documentation index is available at [/llms.txt](https://docs.maptiler.com/llms.txt). Markdown versions of all pages are available by appending .md to any URL.

# Create a heatmap layer

This example uses SDK JS to visualize the frequency of US schools by location using a heatmap layer.

### NPM module setup

```bash
npm install --save @maptiler/sdk
```

#### main.js

```js
import { Map, MapStyle, config } from '@maptiler/sdk';
import '@maptiler/sdk/dist/maptiler-sdk.css';

config.apiKey = 'YOUR_MAPTILER_API_KEY_HERE';
const map = new Map({
    container: 'map',
    zoom: 0.3,
    center: [0, 20],
    style: MapStyle.DATAVIZ.DARK
});

map.on('load', function () {
  // add a clustered GeoJSON source for a sample set of earthquakes
  map.addSource('school_source', {
    'type': 'geojson',
    'data': 'https://docs.maptiler.com/sdk-js/assets/Public_School_Characteristics_2020-21_no_prop.geojson',
  });

  map.addLayer({
    id: 'school_heat',
    type: 'heatmap',
    source: 'school_source',
    maxzoom: 14,
    paint: {

      // Increase the heatmap weight based on frequency and property magnitude
      'heatmap-weight': [
        'interpolate',
        ['linear'],
        ['get', 'students'],
        0,
        0,
        20000, // 20k students to reach the max of colormap
        1
      ],

      // Increase the heatmap color weight weight by zoom level
      // heatmap-intensity is a multiplier on top of heatmap-weight
      'heatmap-intensity': [
        'interpolate',
        ['linear'],
        ['zoom'],
        0,
        1,
        12,
        3
      ],

      // Color ramp for heatmap.  Domain is 0 (low) to 1 (high).
      // Begin color ramp at 0-stop with a 0-transparancy color
      // to create a blur-like effect.
      'heatmap-color': [
        'interpolate',
        ['linear'],
        ['heatmap-density'],
        0, "rgba(68, 1, 84, 0)",
        0.01, "rgba(68, 1, 84, 0.2)",
        0.13, "rgba(71, 44, 122, 1)",
        0.25, "rgba(59, 81, 139, 1)",
        0.38, "rgba(44, 113, 142, 1)",
        0.5, "rgba(33, 144, 141, 1)",
        0.63, "rgba(39, 173, 129, 1)",
        0.75, "rgba(92, 200, 99, 1)",
        0.88, "rgba(170, 220, 50, 1)",
        1, "rgba(253, 231, 37, 1)",
      ],

      // Adjust the heatmap radius by zoom level
      'heatmap-radius': [
        'interpolate',
        ['linear'],
        ['zoom'],
        0,
        2,
        9,
        20
      ],
      // Transition from heatmap to circle layer by zoom level
      'heatmap-opacity': [
        'interpolate',
        ['linear'],
        ['zoom'],
        7,
        1,
        18,
        0
      ]
    }
  });

  map.addLayer({
    'id': 'school_point',
    'type': 'circle',
    'source': 'school_source',
    'minzoom': 8,
    'paint': {
      'circle-pitch-alignment': "map",
      // Size circle radius by earthquake magnitude and zoom level
      'circle-radius': [
        'interpolate',
        ['linear'],
        ['zoom'],
        9,
        ['interpolate', ['linear'], ['get', 'students'], 10, 0.1 * 5, 4000, 2 * 2.5],
        16,
        ['interpolate', ['linear'], ['get', 'students'], 10, 1 * 5, 4000, 20 * 2.5]
      ],
      // Color circle by earthquake magnitude
      'circle-color': [
        'interpolate',
        ['linear'],
        ['get', 'students'],
        2000 * 0, "rgba(68, 1, 84, 0)",
        2000 * 0.01, "rgba(68, 1, 84, 20)",
        2000 * 0.13, "rgba(71, 44, 122, 100)",
        2000 * 0.25, "rgba(59, 81, 139, 100)",
        2000 * 0.38, "rgba(44, 113, 142, 100)",
        2000 * 0.5, "rgba(33, 144, 141, 100)",
        2000 * 0.63, "rgba(39, 173, 129, 100)",
        2000 * 0.75, "rgba(92, 200, 99, 100)",
        2000 * 0.88, "rgba(170, 220, 50, 100)",
        2000 * 1, "rgba(253, 231, 37, 100)",
      ],
      // 'circle-stroke-color': 'white',
      'circle-stroke-width': 0,
      // Transition from heatmap to circle layer by zoom level
      'circle-opacity': [
        'interpolate',
        ['linear'],
        ['zoom'],
        8,
        0,
        12,
        0.8
      ]
    }
  });
});
```

#### index.html

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="robots" content="noindex">
  <title>Create a heatmap layer | NPM example</title>
  <link rel="stylesheet" href="./style.css">
</head>
<body>
  <div id="map"></div>
  <script type="module" src="./main.js"></script>
</body>
</html>
```

#### style.css

```css
body {margin: 0; padding: 0;}
#map {position: absolute; top: 0; bottom: 0; width: 100%;}
```

### Basic JavaScript setup

```html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Create and style clusters</title>
<script src="https://cdn.maptiler.com/maptiler-sdk-js/v4.2.0/maptiler-sdk.umd.min.js"></script>
<link href="https://cdn.maptiler.com/maptiler-sdk-js/v4.2.0/maptiler-sdk.css" rel="stylesheet" />
<style>
  body {margin: 0; padding: 0;}
  #map {position: absolute; top: 0; bottom: 0; width: 100%;}
</style>
</head>
<body>
<div id="map"></div>

<script>
    maptilersdk.config.apiKey = 'YOUR_MAPTILER_API_KEY_HERE';
    const map = new maptilersdk.Map({
        container: 'map',
        zoom: 0.3,
        center: [0, 20],
        style: maptilersdk.MapStyle.DATAVIZ.DARK
    });

    map.on('load', function () {
      // add a clustered GeoJSON source for a sample set of earthquakes
      map.addSource('school_source', {
        'type': 'geojson',
        'data': 'https://docs.maptiler.com/sdk-js/assets/Public_School_Characteristics_2020-21_no_prop.geojson',
      });

      map.addLayer({
        id: 'school_heat',
        type: 'heatmap',
        source: 'school_source',
        maxzoom: 14,
        paint: {

          // Increase the heatmap weight based on frequency and property magnitude
          'heatmap-weight': [
            'interpolate',
            ['linear'],
            ['get', 'students'],
            0,
            0,
            20000, // 20k students to reach the max of colormap
            1
          ],

          // Increase the heatmap color weight weight by zoom level
          // heatmap-intensity is a multiplier on top of heatmap-weight
          'heatmap-intensity': [
            'interpolate',
            ['linear'],
            ['zoom'],
            0,
            1,
            12,
            3
          ],

          // Color ramp for heatmap.  Domain is 0 (low) to 1 (high).
          // Begin color ramp at 0-stop with a 0-transparancy color
          // to create a blur-like effect.
          'heatmap-color': [
            'interpolate',
            ['linear'],
            ['heatmap-density'],
            0, "rgba(68, 1, 84, 0)",
            0.01, "rgba(68, 1, 84, 0.2)",
            0.13, "rgba(71, 44, 122, 1)",
            0.25, "rgba(59, 81, 139, 1)",
            0.38, "rgba(44, 113, 142, 1)",
            0.5, "rgba(33, 144, 141, 1)",
            0.63, "rgba(39, 173, 129, 1)",
            0.75, "rgba(92, 200, 99, 1)",
            0.88, "rgba(170, 220, 50, 1)",
            1, "rgba(253, 231, 37, 1)",
          ],

          // Adjust the heatmap radius by zoom level
          'heatmap-radius': [
            'interpolate',
            ['linear'],
            ['zoom'],
            0,
            2,
            9,
            20
          ],
          // Transition from heatmap to circle layer by zoom level
          'heatmap-opacity': [
            'interpolate',
            ['linear'],
            ['zoom'],
            7,
            1,
            18,
            0
          ]
        }
      });

      map.addLayer({
        'id': 'school_point',
        'type': 'circle',
        'source': 'school_source',
        'minzoom': 8,
        'paint': {
          'circle-pitch-alignment': "map",
          // Size circle radius by earthquake magnitude and zoom level
          'circle-radius': [
            'interpolate',
            ['linear'],
            ['zoom'],
            9,
            ['interpolate', ['linear'], ['get', 'students'], 10, 0.1 * 5, 4000, 2 * 2.5],
            16,
            ['interpolate', ['linear'], ['get', 'students'], 10, 1 * 5, 4000, 20 * 2.5]
          ],
          // Color circle by earthquake magnitude
          'circle-color': [
            'interpolate',
            ['linear'],
            ['get', 'students'],
            2000 * 0, "rgba(68, 1, 84, 0)",
            2000 * 0.01, "rgba(68, 1, 84, 20)",
            2000 * 0.13, "rgba(71, 44, 122, 100)",
            2000 * 0.25, "rgba(59, 81, 139, 100)",
            2000 * 0.38, "rgba(44, 113, 142, 100)",
            2000 * 0.5, "rgba(33, 144, 141, 100)",
            2000 * 0.63, "rgba(39, 173, 129, 100)",
            2000 * 0.75, "rgba(92, 200, 99, 100)",
            2000 * 0.88, "rgba(170, 220, 50, 100)",
            2000 * 1, "rgba(253, 231, 37, 100)",
          ],
          // 'circle-stroke-color': 'white',
          'circle-stroke-width': 0,
          // Transition from heatmap to circle layer by zoom level
          'circle-opacity': [
            'interpolate',
            ['linear'],
            ['zoom'],
            8,
            0,
            12,
            0.8
          ]
        }
      });
    });

</script>
</body>
</html>
```

## Related examples

### [HTML clusters with custom properties](https://docs.maptiler.com/sdk-js/examples/cluster-html/)

Examples

Enhance map clustering using HTML markers and custom expressions.

### [Point layer cluster (point helper)](https://docs.maptiler.com/sdk-js/examples/helper-point-cluster/)

Examples

Add clustered point layer using MapTiler point helper.

### [Heatmap layer (heatmap helper)](https://docs.maptiler.com/sdk-js/examples/helper-heatmap-minimal/)

Examples

Quickly add a heatmap layer using the heatmap helper.

### [Point filtering by property](https://docs.maptiler.com/sdk-js/examples/point-filtering/)

Examples

Filter features in real time by property.
