A heatmap-style visualization showing the energy distribution across the 12 pitch classes (C, C#, D, D#, E, F, F#, G, G#, A, A#, B) over time. Each column represents a time frame and each row a pitch class, with color intensity indicating the energy or magnitude at that pitch-time point. Widely used in music information retrieval to analyze harmonic content, detect chords, estimate musical key, and study tonal progressions in audio signals.

// anyplot.ai
// heatmap-chromagram: Music Chromagram (Pitch Class Distribution over Time)
// Library: echarts 5.5.1 | JavaScript 22.22.3
// Quality: 89/100 | Created: 2026-06-24
//# anyplot-orientation: square
const t = window.ANYPLOT_TOKENS;
// Pitch classes ordered C to B
const pitchClasses = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
const numFrames = 80;
const framesPerChord = 20;
// C–G–Am–F progression (indices into pitchClasses)
// C major: C(0), E(4), G(7)
// G major: D(2), G(7), B(11)
// A minor: C(0), E(4), A(9)
// F major: C(0), F(5), A(9)
const progression = [
[0, 4, 7],
[2, 7, 11],
[0, 4, 9],
[0, 5, 9],
];
// Deterministic LCG for reproducible noise (seed 42)
let seed = 42;
const lcg = () => {
seed = (seed * 1664525 + 1013904223) >>> 0;
return seed / 4294967296;
};
// Build chromagram: [frameIdx, pitchClassIdx, energy]
const chromaData = [];
for (let f = 0; f < numFrames; f++) {
const chordTones = progression[Math.floor(f / framesPerChord)];
for (let p = 0; p < 12; p++) {
const isChordTone = chordTones.includes(p);
const energy = isChordTone ? 0.55 + lcg() * 0.45 : lcg() * 0.18;
chromaData.push([f, p, parseFloat(energy.toFixed(3))]);
}
}
// X-axis labels: show seconds only at chord boundaries (every 20 frames = 1 second)
const timeLabels = Array.from({ length: numFrames }, (_, i) =>
i % 20 === 0 ? `${(i * 0.05).toFixed(1)}s` : ""
);
// Chord section annotations — graphic positions in 1200×1200 CSS-px space
// Grid: left 82, right 118 (from right edge), top 112, bottom 80
// Grid area: x [82, 1082], y [112, 1120], width 1000, height 1008
const gLeft = 82;
const gTop = 112;
const gBottom = 1120;
const sectionW = 1000 / 4; // 250px per chord section
const chordNames = ["C", "G", "Am", "F"];
const labelGraphics = chordNames.map((name, i) => ({
type: "text",
style: {
text: name,
fill: t.ink,
fontSize: 12,
fontWeight: "bold",
textAlign: "center",
textVerticalAlign: "bottom",
},
x: gLeft + sectionW * i + sectionW / 2,
y: gTop - 5,
}));
// Vertical dashed lines at chord boundaries (between sections)
const boundaryGraphics = [1, 2, 3].map((i) => ({
type: "line",
shape: {
x1: gLeft + sectionW * i,
y1: gTop,
x2: gLeft + sectionW * i,
y2: gBottom,
},
style: { stroke: t.inkSoft, lineWidth: 1, lineDash: [4, 3], opacity: 0.55 },
}));
const chart = echarts.init(document.getElementById("container"));
chart.setOption({
animation: false,
backgroundColor: "transparent",
title: {
text: "heatmap-chromagram · javascript · echarts · anyplot.ai",
subtext: "C–G–Am–F chord progression | 4 seconds, 80 analysis frames",
left: "center",
top: 20,
textStyle: { color: t.ink, fontSize: 22, fontWeight: "bold" },
subtextStyle: { color: t.inkSoft, fontSize: 14 },
},
grid: { left: 82, right: 118, top: 112, bottom: 80 },
xAxis: {
type: "category",
data: timeLabels,
name: "Time (seconds)",
nameLocation: "middle",
nameGap: 44,
nameTextStyle: { color: t.inkSoft, fontSize: 16 },
axisLabel: { color: t.inkSoft, fontSize: 13, interval: 0 },
axisLine: { lineStyle: { color: t.inkSoft } },
axisTick: { show: false },
splitLine: { show: false },
},
yAxis: {
type: "category",
data: pitchClasses,
name: "Pitch Class",
nameLocation: "middle",
nameGap: 58,
nameTextStyle: { color: t.inkSoft, fontSize: 16 },
axisLabel: { color: t.inkSoft, fontSize: 14 },
axisLine: { lineStyle: { color: t.inkSoft } },
axisTick: { show: false },
splitLine: { show: false },
},
visualMap: {
min: 0,
max: 1,
calculable: false,
orient: "vertical",
right: 18,
top: "center",
itemWidth: 22,
itemHeight: 220,
inRange: { color: t.seq },
text: ["High", "Low"],
textGap: 8,
textStyle: { color: t.inkSoft, fontSize: 14 },
outOfRange: { color: t.pageBg },
},
graphic: [...labelGraphics, ...boundaryGraphics],
series: [
{
type: "heatmap",
data: chromaData,
itemStyle: {
borderWidth: 0.5,
borderColor: t.pageBg,
},
emphasis: { disabled: true },
},
],
});
Part of Music Chromagram (Pitch Class Distribution over Time) on anyplot.ai.