Bland-Altman Agreement Plot — Highcharts

A Bland-Altman plot (also known as a difference plot or Tukey mean-difference plot) visualizes the agreement between two measurement methods by plotting the difference against the average of paired observations. It displays the mean difference (bias) as a horizontal line and limits of agreement (mean ± 1.96 SD) to assess whether the methods are interchangeable within acceptable tolerances.

Bland-Altman Agreement Plot rendered with Highcharts

Renders

JavaScript source (Highcharts)

// anyplot.ai
// bland-altman-basic: Bland-Altman Agreement Plot
// Library: highcharts 12.6.0 | JavaScript 22.23.1
// Quality: 93/100 | Created: 2026-08-11

const t = window.ANYPLOT_TOKENS;

// --- Data (in-memory, deterministic LCG) ------------------------------------
let seed = 42;
function lcg() {
  seed = (seed * 1103515245 + 12345) % 2147483648;
  return seed / 2147483648;
}
function randNormal() {
  const u1 = lcg() || 1e-9;
  const u2 = lcg();
  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
}

// Paired systolic blood pressure readings from two sphygmomanometers
const pairCount = 90;
const points = [];
for (let i = 0; i < pairCount; i++) {
  const trueSystolic = 100 + lcg() * 80;
  const deviceA = trueSystolic + randNormal() * 2.5;
  const deviceB = trueSystolic + 1.5 + randNormal() * 3;
  const mean = (deviceA + deviceB) / 2;
  const diff = deviceA - deviceB;
  points.push({ x: mean, y: diff, deviceA, deviceB });
}

const diffs = points.map((p) => p.y);
const bias = diffs.reduce((a, b) => a + b, 0) / diffs.length;
const variance =
  diffs.reduce((a, b) => a + (b - bias) ** 2, 0) / (diffs.length - 1);
const sd = Math.sqrt(variance);
const upperLoA = bias + 1.96 * sd;
const lowerLoA = bias - 1.96 * sd;

// --- Chart -------------------------------------------------------------------
Highcharts.chart("container", {
  chart: {
    type: "scatter",
    backgroundColor: "transparent",
    animation: false,
    marginRight: 150,
    style: { fontFamily: "inherit" },
  },
  credits: { enabled: false },
  colors: t.palette,
  title: {
    text: "bland-altman-basic · javascript · highcharts · anyplot.ai",
    style: { color: t.ink, fontSize: "22px", fontWeight: "600" },
  },
  xAxis: {
    title: {
      text: "Mean of Two Devices (mmHg)",
      style: { color: t.inkSoft, fontSize: "16px" },
    },
    lineColor: t.inkSoft,
    tickColor: t.inkSoft,
    gridLineWidth: 0,
    labels: { style: { color: t.inkSoft, fontSize: "14px" } },
  },
  yAxis: {
    title: {
      text: "Difference: Device A − Device B (mmHg)",
      style: { color: t.inkSoft, fontSize: "16px" },
    },
    lineColor: t.inkSoft,
    tickColor: t.inkSoft,
    gridLineColor: t.grid,
    gridLineWidth: 1,
    labels: { style: { color: t.inkSoft, fontSize: "14px" } },
    plotBands: [
      {
        from: lowerLoA,
        to: upperLoA,
        color: Highcharts.color(t.palette[0]).setOpacity(0.07).get(),
        zIndex: 0,
      },
    ],
    plotLines: [
      {
        value: bias,
        color: t.ink,
        width: 2,
        dashStyle: "Solid",
        zIndex: 5,
        label: {
          text: `Bias: ${bias.toFixed(1)}`,
          align: "right",
          textAlign: "left",
          x: 14,
          y: 4,
          style: { color: t.ink, fontSize: "14px", fontWeight: "600" },
        },
      },
      {
        value: upperLoA,
        color: t.inkSoft,
        width: 1.5,
        dashStyle: "Dash",
        zIndex: 5,
        label: {
          text: `+1.96 SD: ${upperLoA.toFixed(1)}`,
          align: "right",
          textAlign: "left",
          x: 14,
          y: 4,
          style: { color: t.inkSoft, fontSize: "14px" },
        },
      },
      {
        value: lowerLoA,
        color: t.inkSoft,
        width: 1.5,
        dashStyle: "Dash",
        zIndex: 5,
        label: {
          text: `−1.96 SD: ${lowerLoA.toFixed(1)}`,
          align: "right",
          textAlign: "left",
          x: 14,
          y: 4,
          style: { color: t.inkSoft, fontSize: "14px" },
        },
      },
    ],
  },
  legend: { enabled: false },
  tooltip: {
    backgroundColor: t.elevatedBg,
    borderColor: t.grid,
    style: { color: t.ink, fontSize: "13px" },
    formatter: function () {
      return (
        `Device A: ${this.point.deviceA.toFixed(1)} mmHg<br>` +
        `Device B: ${this.point.deviceB.toFixed(1)} mmHg<br>` +
        `<b>Difference: ${this.point.y.toFixed(1)} mmHg</b>`
      );
    },
  },
  plotOptions: {
    series: { animation: false },
    scatter: {
      marker: {
        radius: 5,
        fillOpacity: 0.55,
        lineWidth: 1,
        lineColor: t.pageBg,
      },
    },
  },
  series: [
    {
      name: "Paired Measurements",
      data: points,
      color: t.palette[0],
    },
  ],
});

Part of Bland-Altman Agreement Plot on anyplot.ai.

Other implementations