Bias-Variance Tradeoff Curve — Highcharts

A theoretical visualization of the bias-variance tradeoff showing how total prediction error decomposes into bias squared, variance, and irreducible noise as a function of model complexity. The plot displays multiple curves: bias squared (decreasing with complexity), variance (increasing with complexity), irreducible error (constant), and total error (U-shaped). This is one of the most fundamental conceptual plots in machine learning for understanding model selection, overfitting, and underfitting.

Bias-Variance Tradeoff Curve rendered with Highcharts

Renders

JavaScript source (Highcharts)

// anyplot.ai
// curve-bias-variance-tradeoff: Bias-Variance Tradeoff Curve
// Library: highcharts 12.6.0 | JavaScript 22.23.2
// Quality: 91/100 | Created: 2026-08-24

const t = window.ANYPLOT_TOKENS;
const hexToRgba = (hex, alpha) => {
  const n = parseInt(hex.slice(1), 16);
  return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${alpha})`;
};

// --- Data (theoretical curves, deterministic) -------------------------------
// Model complexity stands in for e.g. polynomial degree or tree depth.
const N = 60;
const complexity = Array.from({ length: N }, (_, i) => 1 + (i * 14) / (N - 1));

const biasSquared = complexity.map((c) => 6 / (1 + c));
const variance = complexity.map((c) => 0.018 * c * c);
const irreducibleValue = 0.5;
const irreducibleError = complexity.map(() => irreducibleValue);
const totalError = complexity.map(
  (_, i) => biasSquared[i] + variance[i] + irreducibleValue,
);

let optimalIdx = 0;
totalError.forEach((v, i) => {
  if (v < totalError[optimalIdx]) optimalIdx = i;
});
const optimalComplexity = complexity[optimalIdx];
const lastIdx = N - 1;

// [x, y] pairs so each value lands at its real complexity coordinate.
const asPairs = (ys) => complexity.map((x, i) => [x, ys[i]]);

// --- Chart -------------------------------------------------------------------
Highcharts.chart("container", {
  chart: {
    type: "spline",
    backgroundColor: "transparent",
    animation: false,
    spacingRight: 110,
    style: { fontFamily: "inherit" },
  },
  credits: { enabled: false },
  colors: t.palette,
  title: {
    text: "curve-bias-variance-tradeoff · javascript · highcharts · anyplot.ai",
    style: { color: t.ink, fontSize: "22px", fontWeight: "600" },
  },
  subtitle: {
    text: "Total Error = Bias² + Variance + Irreducible Error",
    style: { color: t.inkSoft, fontSize: "14px" },
  },
  xAxis: {
    min: 1,
    max: 15,
    lineColor: t.inkSoft,
    tickColor: t.inkSoft,
    gridLineColor: t.grid,
    title: {
      text: "Model Complexity",
      style: { color: t.inkSoft, fontSize: "16px" },
    },
    plotBands: [
      {
        from: 1,
        to: optimalComplexity,
        color: t.grid,
        label: {
          text: "Underfitting zone",
          align: "center",
          verticalAlign: "bottom",
          y: -12,
          style: { color: t.inkSoft, fontSize: "13px" },
        },
      },
      {
        from: optimalComplexity,
        to: 15,
        color: hexToRgba(t.amber, 0.1),
        label: {
          text: "Overfitting zone",
          align: "center",
          verticalAlign: "bottom",
          y: -12,
          style: { color: t.inkSoft, fontSize: "13px" },
        },
      },
    ],
    plotLines: [
      {
        value: optimalComplexity,
        color: t.inkSoft,
        width: 1.5,
        dashStyle: "ShortDash",
        zIndex: 4,
        label: {
          text: `Optimal complexity ≈ ${optimalComplexity.toFixed(1)}`,
          rotation: 0,
          align: "center",
          y: 20,
          style: { color: t.inkSoft, fontSize: "13px" },
        },
      },
    ],
  },
  yAxis: {
    min: 0,
    title: {
      text: "Prediction Error",
      style: { color: t.inkSoft, fontSize: "16px" },
    },
    gridLineColor: t.grid,
    labels: { style: { color: t.inkSoft, fontSize: "14px" } },
  },
  legend: { enabled: false },
  tooltip: { enabled: false },
  plotOptions: {
    series: {
      animation: false,
      marker: { enabled: false },
      lineWidth: 3,
      enableMouseTracking: false,
      dataLabels: {
        enabled: true,
        crop: false,
        overflow: "allow",
        align: "left",
        x: 8,
        y: 2,
        style: { fontSize: "14px", fontWeight: "600", textOutline: "none" },
        formatter: function () {
          return this.point.index === lastIdx ? this.series.name : null;
        },
      },
    },
  },
  series: [
    {
      name: "Bias²",
      data: asPairs(biasSquared),
      color: t.palette[0],
      dataLabels: { style: { color: t.palette[0] } },
    },
    {
      name: "Variance",
      data: asPairs(variance),
      color: t.palette[1],
      dataLabels: { style: { color: t.palette[1] } },
    },
    {
      name: "Irreducible Error",
      data: asPairs(irreducibleError),
      color: t.inkSoft,
      dashStyle: "Dash",
      lineWidth: 2,
      dataLabels: {
        style: { color: t.inkSoft },
        align: "left",
        x: 10,
        y: -10,
        formatter: function () {
          return this.point.index === 0 ? this.series.name : null;
        },
      },
    },
    {
      name: "Total Error",
      data: asPairs(totalError),
      color: t.ink,
      lineWidth: 4,
      zIndex: 3,
      dataLabels: { style: { color: t.ink } },
    },
    {
      type: "scatter",
      name: "Optimal complexity",
      data: [[optimalComplexity, totalError[optimalIdx]]],
      color: t.ink,
      marker: {
        enabled: true,
        symbol: "circle",
        radius: 7,
        fillColor: t.ink,
        lineColor: t.pageBg,
        lineWidth: 2,
      },
      dataLabels: { enabled: false },
      zIndex: 5,
    },
  ],
});

Part of Bias-Variance Tradeoff Curve on anyplot.ai.

Other implementations