Heatmap chart
A heatmap is a grid where color intensity represents a number. Each cell is a combination of two categories (like "Monday" × "9am"), and its shade tells you how high or low the value is. Your eyes pick up patterns — hot spots, cold spots, diagonals — far faster than they would from the same numbers in a spreadsheet.
When to use it
Use a heatmap when all three are true:
- Your data has two categorical dimensions. Every value sits at the intersection of two things: day-of-week × hour, product × region, customer × month. If you only have one dimension, use a bar chart.
- There are too many cells for a table to be readable. A 7×24 grid (168 cells) of pure numbers is unreadable. The same grid as colors is instantly scannable. Below about 20 cells, a table with bold formatting is often better.
- The reader cares about patterns, not exact values. Heatmaps are for spotting "Tuesday afternoons are always busy" or "this region underperforms." If the reader needs to know exact numbers, pair the heatmap with a downloadable table.
Common uses: website traffic by day/hour, correlation matrices, calendar views (GitHub-style contribution graphs), cohort retention charts, sales by product/region.
Example data
This is where heatmaps trip people up. Your data needs to be in matrix form — one row per row-label, one column per column-label, and the values in the cells. Not a long list.
Here's a week of customer support tickets by day and hour:
| 9am | 10am | 11am | 12pm | 1pm | 2pm | 3pm | 4pm | 5pm | |
|---|---|---|---|---|---|---|---|---|---|
| Monday | 12 | 18 | 22 | 15 | 10 | 14 | 20 | 25 | 19 |
| Tuesday | 8 | 14 | 19 | 11 | 7 | 12 | 18 | 22 | 16 |
| Wednesday | 10 | 16 | 24 | 18 | 12 | 15 | 21 | 28 | 22 |
| Thursday | 11 | 17 | 20 | 14 | 9 | 13 | 19 | 24 | 18 |
| Friday | 14 | 20 | 26 | 20 | 15 | 18 | 23 | 30 | 25 |
That's the shape your chart tool needs. The hot spots (higher numbers) will naturally darken into a visible pattern — you'll immediately see that late afternoons on Fridays are peak ticket hours.
The "wrong shape" problem
Most non-technical users actually have their data in long form, like this:
| Day | Hour | Tickets |
|---|---|---|
| Monday | 9am | 12 |
| Monday | 10am | 18 |
| Monday | 11am | 22 |
| ... | ... | ... |
Both shapes represent the same information. Most libraries and spreadsheet tools can convert between them, but you have to know what they expect:
- Excel and Google Sheets want the matrix shape (wide).
- Python (seaborn, matplotlib), R (ggplot2), and most JS libraries want the long shape and pivot internally.
If your chart comes out looking wrong — a single row, a single column, or nothing at all — this is almost always the reason. Check which shape your tool expects before blaming the chart.
How to read it
A heatmap should be scanned, not studied. The three things a reader's eyes do automatically:
- Find the hottest cell. That's the peak — the single most extreme value. Always check whether this makes sense (sometimes it's a data error, like a duplicate entry).
- Look for stripes. A dark row means that whole row-label is elevated (every Friday is busy). A dark column means the column-label is elevated (every day at 4pm is busy). Stripes are the most actionable pattern.
- Look for clusters. A dense block of warm cells in one corner points to an interaction effect — "Friday afternoons are extra busy, more than you'd expect from Friday + afternoon alone."
Always print a color legend showing what the scale means. Without it, readers can't tell if the darkest cell is 30 or 3,000.
Sequential vs diverging colors
This is the single most important choice in a heatmap, and non-technical users usually get it wrong:
- Sequential scale (light → dark in one color, like white → blue) for data that only goes one direction: counts, amounts, percentages of a whole. The ticket data above is sequential.
- Diverging scale (one color → neutral → another color, like red → white → blue) for data that has a meaningful midpoint: correlations (−1 to +1), % change vs. a baseline, performance vs. target.
Using a diverging scale for sequential data makes the chart lie — readers interpret the neutral color as "zero / neutral" when it's actually just "the middle of your range." Pick sequential unless your data crosses a meaningful zero.
How to build one
In a spreadsheet (Excel or Google Sheets)
Heatmaps are built with conditional formatting, not the chart menu:
- Arrange your data in matrix form (rows and columns with values in the cells, like the first table above).
- Select the value cells only (not the labels).
- Excel: Home → Conditional Formatting → Color Scales → pick a 2-color or 3-color scale. Google Sheets: Format → Conditional formatting → Color scale.
- Optionally hide the numbers so only the colors remain — but keeping the numbers visible is usually better for small grids.
This is genuinely one of the most useful features in spreadsheet software, and it produces a real, presentation-ready heatmap.
With code (for a dashboard or report)
Most charting libraries have a heatmap type. The data pattern depends on the library:
// Long form — what most libraries expect (Plotly, Observable Plot, D3)
const data = [
{ day: "Monday", hour: "9am", tickets: 12 },
{ day: "Monday", hour: "10am", tickets: 18 },
{ day: "Tuesday", hour: "9am", tickets: 8 },
// ... one row per cell
];
// Matrix form — what some libraries expect (Chart.js matrix plugin, raw D3)
const matrix = [
[12, 18, 22, 15, 10, 14, 20, 25, 19], // Monday
[ 8, 14, 19, 11, 7, 12, 18, 22, 16], // Tuesday
// ... one array per row
];
Check your library's docs for which shape it wants. If you're writing code that pulls from a database, the query will almost always return long form — so libraries that accept it directly save you a conversion step.
Tips
- Keep grids under ~500 cells. Beyond that, individual cells become too small to see and the chart turns into wallpaper. Aggregate to fewer rows or columns if needed.
- Sort rows and columns meaningfully. Alphabetical order rarely reveals anything. Sort by row totals, by similarity, or by a natural order (days of week, months) to let patterns emerge.
- Use the same scale across multiple heatmaps when comparing. If each small-multiple heatmap has its own color scale, you can't compare them — the darkest cell in each just means "the max of that one chart."
- Pick colorblind-safe palettes. Red-green diverging scales are the worst offenders. Blue-orange or blue-red work better. Most modern libraries default to colorblind-safe palettes (viridis, cividis) — stick with them unless you have a reason to change.
- Don't use a heatmap for two variables you can show in a simpler chart. If one dimension has only 2–3 categories, a grouped bar chart is easier to read. Heatmaps earn their keep when both dimensions have lots of categories.