Relationship / Intermediate

Heatmap

Reveal patterns across a grid of color-coded values.

Back to collection

Interactive Chart Preview

JAVASCRIPT

Approximation: Chart.js uses color-mapped scatter points to approximate heatmap cells.

Live, interactive exampleChart.js

Chart.js Code Example

JAVASCRIPT
import Chart from 'chart.js/auto';

// Illustrative data used by the live preview.
const chartData = {
  "categoryLabel": "Hour of Day",
  "matrix": {
    "columns": [
      "Mon",
      "Tue",
      "Wed",
      "Thu",
      "Fri",
      "Sat",
      "Sun"
    ],
    "rows": [
      "00:00",
      "01:00",
      "02:00",
      "03:00",
      "04:00",
      "05:00"
    ]
  },
  "datasets": [
    {
      "label": "Visits",
      "data": [
        {
          "x": 0,
          "y": 0,
          "v": 23
        },
        {
          "x": 1,
          "y": 0,
          "v": 45
        },
        {
          "x": 2,
          "y": 0,
          "v": 67
        },
        {
          "x": 3,
          "y": 0,
          "v": 34
        },
        {
          "x": 4,
          "y": 0,
          "v": 12
        },
        {
          "x": 5,
          "y": 0,
          "v": 78
        },
        {
          "x": 6,
          "y": 0,
          "v": 90
        },
        {
          "x": 0,
          "y": 1,
          "v": 56
        },
        {
          "x": 1,
          "y": 1,
          "v": 78
        },
        {
          "x": 2,
          "y": 1,
          "v": 23
        },
        {
          "x": 3,
          "y": 1,
          "v": 89
        },
        {
          "x": 4,
          "y": 1,
          "v": 45
        },
        {
          "x": 5,
          "y": 1,
          "v": 67
        },
        {
          "x": 6,
          "y": 1,
          "v": 34
        },
        {
          "x": 0,
          "y": 2,
          "v": 89
        },
        {
          "x": 1,
          "y": 2,
          "v": 34
        },
        {
          "x": 2,
          "y": 2,
          "v": 78
        },
        {
          "x": 3,
          "y": 2,
          "v": 23
        },
        {
          "x": 4,
          "y": 2,
          "v": 90
        },
        {
          "x": 5,
          "y": 2,
          "v": 12
        },
        {
          "x": 6,
          "y": 2,
          "v": 56
        },
        {
          "x": 0,
          "y": 3,
          "v": 45
        },
        {
          "x": 1,
          "y": 3,
          "v": 67
        },
        {
          "x": 2,
          "y": 3,
          "v": 89
        },
        {
          "x": 3,
          "y": 3,
          "v": 56
        },
        {
          "x": 4,
          "y": 3,
          "v": 23
        },
        {
          "x": 5,
          "y": 3,
          "v": 78
        },
        {
          "x": 6,
          "y": 3,
          "v": 34
        },
        {
          "x": 0,
          "y": 4,
          "v": 12
        },
        {
          "x": 1,
          "y": 4,
          "v": 90
        },
        {
          "x": 2,
          "y": 4,
          "v": 45
        },
        {
          "x": 3,
          "y": 4,
          "v": 67
        },
        {
          "x": 4,
          "y": 4,
          "v": 78
        },
        {
          "x": 5,
          "y": 4,
          "v": 23
        },
        {
          "x": 6,
          "y": 4,
          "v": 89
        },
        {
          "x": 0,
          "y": 5,
          "v": 78
        },
        {
          "x": 1,
          "y": 5,
          "v": 23
        },
        {
          "x": 2,
          "y": 5,
          "v": 56
        },
        {
          "x": 3,
          "y": 5,
          "v": 90
        },
        {
          "x": 4,
          "y": 5,
          "v": 34
        },
        {
          "x": 5,
          "y": 5,
          "v": 45
        },
        {
          "x": 6,
          "y": 5,
          "v": 67
        }
      ],
      "borderColor": "rgba(59, 130, 246, 0.8)",
      "pointRadius": 18,
      "pointHoverRadius": 20
    }
  ]
};

function createConfig(chartData) {
    const matrix = chartData.matrix;
    const points = chartData.datasets[0].data;
    const min = Math.min(0, ...points.map(p => p.v));
    const max = Math.max(100, ...points.map(p => p.v));
    return {
        type: 'scatter',
        data: {
            datasets: [{
                    label: chartData.datasets[0].label,
                    data: structuredClone(points),
                    pointStyle: 'rect',
                    backgroundColor: points.map(p => {
                        const t = (p.v - min) / (max - min);
                        return `rgb(${Math.round(230 - 171 * t)}, ${Math.round(243 - 113 * t)}, ${Math.round(255 - 9 * t)})`;
                    }),
                    borderColor: '#ffffff',
                    borderWidth: 1,
                    pointRadius: context => {
                        const area = context.chart.chartArea;
                        return area ? Math.max(0.5, Math.min(area.width / matrix.columns.length, area.height / matrix.rows.length) / 2 - 1) : 8;
                    },
                    pointHoverRadius: 5,
                }],
        },
        options: {
            responsive: true,
            maintainAspectRatio: false,
            scales: {
                x: {
                    min: -0.5, max: matrix.columns.length - 0.5,
                    afterBuildTicks: axis => { axis.ticks = matrix.columns.map((_, value) => ({ value })); },
                    ticks: { callback: value => matrix.columns[Number(value)] ?? '' },
                    grid: { display: false },
                },
                y: {
                    min: -0.5, max: matrix.rows.length - 0.5, reverse: true,
                    afterBuildTicks: axis => { axis.ticks = matrix.rows.map((_, value) => ({ value })); },
                    ticks: { callback: value => matrix.rows[Number(value)] ?? '' },
                    title: { display: true, text: chartData.categoryLabel },
                    grid: { display: false },
                },
            },
            plugins: {
                legend: { display: false },
                tooltip: {
                    callbacks: {
                        title: () => '',
                        label: context => {
                            const point = points[context.dataIndex];
                            return `${matrix.rows[point.y]}, ${matrix.columns[point.x]}: ${point.v}`;
                        },
                    },
                },
            },
        },
    };
}

const chart = new Chart(document.getElementById('myChart'), createConfig(chartData));

Preview and code follow the same library selection.

Run This Example

Install the package below in a browser project such as Vite. Put the copied code in a client JavaScript module and add this container to the page before the module runs.

npm install chart.js@4.5.1
<div style="height:320px"><canvas id="myChart"></canvas></div>

The example includes its sample data. When removing the chart, destroy or dispose of its instance and disconnect any resize observer.

Reading This Example

The default Chart.js preview contains 42 illustrative cells: seven day positions and six hourly positions, from 00:00 through 05:00. Monday at 00:00 has value 23 and Sunday at 00:00 has value 90. Darker points indicate higher sample values.

All library tabs use these illustrative values. Rendering styles and library APIs differ, so use the table to compare exact numbers.

About this example & sample data
Sample values used in the chart
Day and HourSample Value
Mon 00:0023
Tue 00:0045
Wed 00:0067
Thu 00:0034
Fri 00:0012
Sat 00:0078
Sun 00:0090
Mon 01:0056
Tue 01:0078
Wed 01:0023
Thu 01:0089
Fri 01:0045
Sat 01:0067
Sun 01:0034
Mon 02:0089
Tue 02:0034
Wed 02:0078
Thu 02:0023
Fri 02:0090
Sat 02:0012
Sun 02:0056
Mon 03:0045
Tue 03:0067
Wed 03:0089
Thu 03:0056
Fri 03:0023
Sat 03:0078
Sun 03:0034
Mon 04:0012
Tue 04:0090
Wed 04:0045
Thu 04:0067
Fri 04:0078
Sat 04:0023
Sun 04:0089
Mon 05:0078
Tue 05:0023
Wed 05:0056
Thu 05:0090
Fri 05:0034
Sat 05:0045
Sun 05:0067

READ THE CHART

Find Patterns Across a Matrix

A heatmap maps a numeric value to color at each position in a grid. It can make repeated patterns, high-value regions, and differences across two dimensions visible without drawing a separate bar for every cell.

01

Define Each Cell

Specify what the row, column, and color represent. A cell might show a count, an average, or a correlation. Those measures need different interpretations, so include units and a clear color key.

02

Choose a Meaningful Scale

Use a sequential scale for low-to-high values and a diverging scale when values vary around a meaningful midpoint. Keep the scale fixed across comparable views. Missing values should look different from measured zeros.

03

Check Exact Values

Color helps locate patterns but is less precise than position. Provide tooltips or a data table for exact readings. Do not rely on a red-green contrast alone to communicate a difference.

04

Understand the Implementations

The Chart.js preview approximates a heatmap with colored scatter points. All tabs use the same 42 day-and-hour values, with a fixed 0 to 100 color range. D3, ECharts, Plotly, and the Python examples draw rectangular cells.

Edit Chart Data