Basic Heatmap — MUI X Charts

A heatmap displaying values in a matrix format using color intensity. Each cell's color represents the magnitude of the value, making it easy to identify patterns, clusters, and outliers in two-dimensional data. Essential for visualizing correlations, frequencies, and relationships between variables.

Basic Heatmap rendered with MUI X Charts

Renders

JavaScript source (MUI X Charts)

// anyplot.ai
// heatmap-basic: Basic Heatmap
// Library: muix 7.29.1 | JavaScript 22.23.2
// Quality: 93/100 | Created: 2026-08-20
//# anyplot-orientation: square
// anyplot.ai
// heatmap-basic: Basic 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-20
import { ChartContainer } from "@mui/x-charts/ChartContainer";
import { ChartsXAxis } from "@mui/x-charts/ChartsXAxis";
import { ChartsYAxis } from "@mui/x-charts/ChartsYAxis";
import { ChartsText } from "@mui/x-charts/ChartsText";
import { ContinuousColorLegend } from "@mui/x-charts/ChartsLegend";
import { useXScale, useYScale, useZColorScale } from "@mui/x-charts/hooks";

const t = window.ANYPLOT_TOKENS;
const SIZE = window.ANYPLOT_SIZE;

// --- Data: correlation matrix between business metrics (in-memory, deterministic) ---
const metrics = [
  "Revenue",
  "Marketing",
  "CSAT",
  "Headcount",
  "R&D",
  "Mkt Share",
  "OpEx",
  "Churn",
];

// Upper-triangular correlation coefficients (row i vs. columns i+1..n-1); mirrored below.
const upperTriangle = [
  [0.72, 0.45, 0.68, 0.55, 0.81, 0.6, -0.52],
  [0.3, 0.4, 0.25, 0.66, 0.58, -0.2],
  [0.35, 0.28, 0.5, -0.1, -0.78],
  [0.62, 0.47, 0.71, -0.18],
  [0.58, 0.33, -0.25],
  [0.4, -0.44],
  [0.15],
];

const n = metrics.length;
const cells = [];
for (let row = 0; row < n; row += 1) {
  for (let col = 0; col < n; col += 1) {
    const value =
      row === col
        ? 1
        : upperTriangle[Math.min(row, col)][
            Math.max(row, col) - Math.min(row, col) - 1
          ];
    cells.push({
      id: `${row}-${col}`,
      x: metrics[col],
      y: metrics[row],
      value,
    });
  }
}

// --- Colour: diverging Imprint colormap (imprint_div) driven by the z-axis colorMap ---
function hexToRgb(hex) {
  const int = parseInt(hex.slice(1), 16);
  return [(int >> 16) & 255, (int >> 8) & 255, int & 255];
}

function lerp(a, b, ratio) {
  return Math.round(a + (b - a) * ratio);
}

function imprintDivInterpolator(stops) {
  const [low, mid, high] = stops.map(hexToRgb);
  return (position) => {
    const [start, end, localRatio] =
      position < 0.5
        ? [low, mid, position / 0.5]
        : [mid, high, (position - 0.5) / 0.5];
    const [r, g, b] = [0, 1, 2].map((channel) =>
      lerp(start[channel], end[channel], localRatio),
    );
    return `rgb(${r}, ${g}, ${b})`;
  };
}

// Cell text must stay legible against that cell's own fill luminance regardless
// of the current site theme, so pick between the two fixed Imprint ink literals
// (light-mode ink / dark-mode ink) rather than the theme-dependent `t.ink`.
const CELL_TEXT_ON_LIGHT_FILL = "#1A1A17";
const CELL_TEXT_ON_DARK_FILL = "#F0EFE8";

function textColorFor(fill) {
  const [r, g, b] = fill.match(/[\d.]+/g).map(Number);
  const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
  return luminance > 140 ? CELL_TEXT_ON_LIGHT_FILL : CELL_TEXT_ON_DARK_FILL;
}

// --- Cells (custom composition: MUI X band scales + z-axis colour scale) --------------
function HeatmapCells() {
  const xScale = useXScale();
  const yScale = useYScale();
  const colorScale = useZColorScale();

  return (
    <g>
      {cells.map((cell) => {
        const x = xScale(cell.x) ?? 0;
        const y = yScale(cell.y) ?? 0;
        const width = xScale.bandwidth();
        const height = yScale.bandwidth();
        const fill = colorScale(cell.value);
        return (
          <g key={cell.id}>
            <rect
              x={x}
              y={y}
              width={width}
              height={height}
              fill={fill}
              rx={4}
            />
            <ChartsText
              text={cell.value.toFixed(2)}
              x={x + width / 2}
              y={y + height / 2}
              style={{
                fontSize: 15,
                fill: textColorFor(fill),
                textAnchor: "middle",
                dominantBaseline: "central",
              }}
            />
          </g>
        );
      })}
    </g>
  );
}

// --- Chart (default-exported component — the harness mounts it) ----------------------
const TITLE = "heatmap-basic · javascript · muix · anyplot.ai";
const MARGIN = { top: 150, right: 200, bottom: 150, left: 170 };
const LEGEND_CAPTION_X = SIZE.width - 26;
const LEGEND_CAPTION_Y = SIZE.height / 2;

export default function Chart() {
  return (
    <ChartContainer
      width={SIZE.width}
      height={SIZE.height}
      series={[]}
      margin={MARGIN}
      skipAnimation
      xAxis={[
        {
          scaleType: "band",
          data: metrics,
          categoryGapRatio: 0.08,
          disableLine: true,
          disableTicks: true,
          tickLabelStyle: { fontSize: 14, fill: t.inkSoft },
        },
      ]}
      yAxis={[
        {
          scaleType: "band",
          data: metrics,
          categoryGapRatio: 0.08,
          disableLine: true,
          disableTicks: true,
          tickLabelStyle: { fontSize: 14, fill: t.inkSoft },
        },
      ]}
      zAxis={[
        {
          colorMap: {
            type: "continuous",
            min: -1,
            max: 1,
            color: imprintDivInterpolator(t.div),
          },
        },
      ]}
    >
      <HeatmapCells />
      <ChartsXAxis />
      <ChartsYAxis />
      <ContinuousColorLegend
        position={{ horizontal: "right", vertical: "middle" }}
        direction="column"
        length="55%"
        thickness={18}
        labelStyle={{ fontSize: 13, fill: t.inkSoft }}
      />
      <ChartsText
        text="Correlation coefficient"
        x={LEGEND_CAPTION_X}
        y={LEGEND_CAPTION_Y}
        style={{
          fontSize: 12,
          fill: t.inkSoft,
          textAnchor: "middle",
          angle: -90,
        }}
      />
      <ChartsText
        text={TITLE}
        x={SIZE.width / 2}
        y={50}
        style={{
          fontSize: 26,
          fontWeight: 600,
          fill: t.ink,
          textAnchor: "middle",
        }}
      />
    </ChartContainer>
  );
}

Part of Basic Heatmap on anyplot.ai.

Other implementations