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.

// 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.