The same plot in 14 other libraries — JavaScript: Chart.js, D3.js, Apache ECharts, MUI X Charts; Python: Altair, Bokeh, lets-plot, Matplotlib, Plotly, plotnine, Pygal, Seaborn; R: ggplot2; Julia: Makie.jl. Compare all 15 side by side: Bland-Altman Agreement Plot in Python, R, Julia and JavaScript.
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],
},
],
});
Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/bland-altman-basic/highcharts/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.
{
"spec_id": "bland-altman-basic",
"language": "javascript",
"library": "highcharts",
"page": "https://anyplot.ai/bland-altman-basic/javascript/highcharts",
"hub": "https://anyplot.ai/bland-altman-basic",
"code_json": "https://api.anyplot.ai/specs/bland-altman-basic/highcharts/code",
"spec_json": "https://api.anyplot.ai/specs/bland-altman-basic",
"render_light_png": "https://storage.googleapis.com/anyplot-images/plots/bland-altman-basic/javascript/highcharts/plot-light.png",
"render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/bland-altman-basic/javascript/highcharts/plot-dark.png",
"interactive_light_html": "https://storage.googleapis.com/anyplot-images/plots/bland-altman-basic/javascript/highcharts/plot-light.html",
"interactive_dark_html": "https://storage.googleapis.com/anyplot-images/plots/bland-altman-basic/javascript/highcharts/plot-dark.html",
"quality_score": 93.0,
"license": "MIT",
"guide": "https://anyplot.ai/llms.txt"
}Part of Bland-Altman Agreement Plot on anyplot.ai.