Relationship / Intermediate
Heatmap
Reveal patterns across a grid of color-coded values.
Interactive Chart Preview
JAVASCRIPTApproximation: Chart.js uses color-mapped scatter points to approximate heatmap cells.
Edit the column names and values to update your chart. Changes stay in this page until you reload.
| Row# | Row label | Cell value | Cell value | Cell value | Cell value | Cell value | Cell value | Cell value | Remove row |
|---|---|---|---|---|---|---|---|---|---|
| 1 | |||||||||
| 2 | |||||||||
| 3 | |||||||||
| 4 | |||||||||
| 5 | |||||||||
| 6 |
Use distinct row and column labels. Negative values and decimals are supported. Colors share one scale that expands to include all values. Up to 20 value columns.
Chart.js Code Example
JAVASCRIPTimport 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
| Day and Hour | Sample Value |
|---|---|
| Mon 00:00 | 23 |
| Tue 00:00 | 45 |
| Wed 00:00 | 67 |
| Thu 00:00 | 34 |
| Fri 00:00 | 12 |
| Sat 00:00 | 78 |
| Sun 00:00 | 90 |
| Mon 01:00 | 56 |
| Tue 01:00 | 78 |
| Wed 01:00 | 23 |
| Thu 01:00 | 89 |
| Fri 01:00 | 45 |
| Sat 01:00 | 67 |
| Sun 01:00 | 34 |
| Mon 02:00 | 89 |
| Tue 02:00 | 34 |
| Wed 02:00 | 78 |
| Thu 02:00 | 23 |
| Fri 02:00 | 90 |
| Sat 02:00 | 12 |
| Sun 02:00 | 56 |
| Mon 03:00 | 45 |
| Tue 03:00 | 67 |
| Wed 03:00 | 89 |
| Thu 03:00 | 56 |
| Fri 03:00 | 23 |
| Sat 03:00 | 78 |
| Sun 03:00 | 34 |
| Mon 04:00 | 12 |
| Tue 04:00 | 90 |
| Wed 04:00 | 45 |
| Thu 04:00 | 67 |
| Fri 04:00 | 78 |
| Sat 04:00 | 23 |
| Sun 04:00 | 89 |
| Mon 05:00 | 78 |
| Tue 05:00 | 23 |
| Wed 05:00 | 56 |
| Thu 05:00 | 90 |
| Fri 05:00 | 34 |
| Sat 05:00 | 45 |
| Sun 05:00 | 67 |
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.
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.
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.
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.
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.