A heatmap specifically designed to display correlation coefficients between variables, using a diverging color scheme centered at zero. The symmetric matrix visualization makes it easy to identify positive correlations, negative correlations, and independent variables at a glance. Essential for exploratory data analysis, feature engineering, and multicollinearity detection in statistical and machine learning workflows.

// anyplot.ai
// heatmap-correlation: Correlation Matrix Heatmap
// Library: muix 7.29.1 | JavaScript 22.23.2
// Quality: 91/100 | Created: 2026-08-18
//# anyplot-orientation: square
// anyplot.ai
// heatmap-correlation: Correlation Matrix Heatmap
// Library: MUI X Charts | React | Node 22
// License: @mui/x-charts — MIT (community). Pro/Premium are out of scope.
// Quality: pending | Created: 2026-08-18
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import { ScatterChart } from "@mui/x-charts/ScatterChart";
import { ContinuousColorLegend } from "@mui/x-charts/ChartsLegend";
const tokens = window.ANYPLOT_TOKENS;
// --- Data (in-memory, deterministic) — portfolio diversification example ---
const variables = ["Equities", "Bonds", "Real Estate", "Commodities", "Gold", "Cash"];
const correlationMatrix = [
[1.0, -0.35, 0.55, 0.2, -0.1, -0.05],
[-0.35, 1.0, 0.15, -0.2, 0.3, 0.1],
[0.55, 0.15, 1.0, 0.25, 0.05, -0.15],
[0.2, -0.2, 0.25, 1.0, 0.65, -0.1],
[-0.1, 0.3, 0.05, 0.65, 1.0, 0.2],
[-0.05, 0.1, -0.15, -0.1, 0.2, 1.0],
];
// Mask the strict upper triangle — a symmetric matrix repeats every
// off-diagonal value, so only the diagonal and lower triangle carry ink.
const points = [];
for (let row = 0; row < variables.length; row += 1) {
for (let col = 0; col <= row; col += 1) {
points.push({
id: `${row}-${col}`,
x: variables[col],
y: variables[row],
z: correlationMatrix[row][col],
});
}
}
// --- Diverging Imprint color scale (imprint_div, theme-adaptive midpoint) ---
function mixHex(hexA, hexB, ratio) {
const a = parseInt(hexA.slice(1), 16);
const b = parseInt(hexB.slice(1), 16);
const channel = (shift) => {
const av = (a >> shift) & 255;
const bv = (b >> shift) & 255;
return Math.round(av + (bv - av) * ratio);
};
return `#${[16, 8, 0].map((shift) => channel(shift).toString(16).padStart(2, "0")).join("")}`;
}
// `t` arrives pre-normalized to [0, 1] by the zAxis colorMap's scaleSequential
// (from the -1..1 domain below) — 0 is the red end, 0.5 the neutral midpoint.
function divergingColor(t) {
const [low, mid, high] = tokens.div;
return t <= 0.5 ? mixHex(low, mid, t / 0.5) : mixHex(mid, high, (t - 0.5) / 0.5);
}
function relativeLuminance(hex) {
const n = parseInt(hex.slice(1), 16);
const r = ((n >> 16) & 255) / 255;
const g = ((n >> 8) & 255) / 255;
const b = (n & 255) / 255;
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
// Custom marker: filled square cells (matrix entries) instead of the default
// circles, each annotated with its correlation value to 2 decimal places.
function CorrelationCell(props) {
const { series, xScale, yScale, colorGetter, color } = props;
const cellWidth = xScale.bandwidth();
const cellHeight = yScale.bandwidth();
const fontSize = Math.round(Math.min(cellWidth, cellHeight) * 0.22);
return (
<g>
{series.data.map((point, i) => {
const x0 = xScale(point.x) ?? 0;
const y0 = yScale(point.y) ?? 0;
const fill = colorGetter ? colorGetter(i) : color;
const textFill = relativeLuminance(fill) > 0.5 ? "#1A1A17" : "#F0EFE8";
return (
<g key={point.id}>
<rect x={x0} y={y0} width={cellWidth} height={cellHeight} fill={fill} />
<text
x={x0 + cellWidth / 2}
y={y0 + cellHeight / 2}
textAnchor="middle"
dominantBaseline="central"
fontSize={fontSize}
fontFamily="inherit"
fill={textFill}
>
{point.z.toFixed(2)}
</text>
</g>
);
})}
</g>
);
}
export default function Chart() {
const { width, height } = window.ANYPLOT_SIZE;
const TITLE_HEIGHT = 80;
const RIGHT_BUFFER = 40; // room for the legend's max-value label at the true edge
const TOP_MARGIN = 70;
const LEFT_MARGIN = 160;
const RIGHT_MARGIN = 20;
const LEGEND_SPACE = 70;
const chartWidth = width - RIGHT_BUFFER;
const chartHeight = height - TITLE_HEIGHT;
return (
<Box sx={{ width, height, bgcolor: tokens.pageBg, display: "flex", flexDirection: "column" }}>
<Typography
sx={{
color: tokens.ink,
fontSize: 22,
fontWeight: 500,
textAlign: "center",
lineHeight: 1.2,
pt: "16px",
height: TITLE_HEIGHT,
fontFamily: "inherit",
}}
>
heatmap-correlation · javascript · muix · anyplot.ai
</Typography>
<Box sx={{ flex: 1, display: "flex", alignItems: "flex-start", justifyContent: "flex-start" }}>
<ScatterChart
width={chartWidth}
height={chartHeight}
skipAnimation
disableVoronoi
series={[
{
id: "correlation",
type: "scatter",
data: points,
label: "Correlation",
xAxisId: "col",
yAxisId: "row",
zAxisId: "corr",
},
]}
xAxis={[
{
id: "col",
scaleType: "band",
data: variables,
categoryGapRatio: 0.08,
tickLabelStyle: { fontSize: 16, fill: tokens.inkSoft },
disableTicks: true,
disableLine: true,
},
]}
yAxis={[
{
id: "row",
scaleType: "band",
data: variables,
categoryGapRatio: 0.08,
tickLabelStyle: { fontSize: 16, fill: tokens.inkSoft },
disableTicks: true,
disableLine: true,
},
]}
zAxis={[
{
id: "corr",
min: -1,
max: 1,
colorMap: { type: "continuous", min: -1, max: 1, color: divergingColor },
},
]}
topAxis="col"
bottomAxis={null}
leftAxis="row"
rightAxis={null}
margin={{ top: TOP_MARGIN, right: RIGHT_MARGIN, bottom: LEGEND_SPACE, left: LEFT_MARGIN }}
slots={{ scatter: CorrelationCell }}
slotProps={{ legend: { hidden: true } }}
>
<ContinuousColorLegend
axisId="corr"
axisDirection="z"
position={{ horizontal: "right", vertical: "bottom" }}
direction="row"
length="50%"
thickness={14}
labelStyle={{ fontSize: 14, fill: tokens.inkSoft, fontFamily: "inherit" }}
/>
</ScatterChart>
</Box>
</Box>
);
}
Part of Correlation Matrix Heatmap on anyplot.ai.