{"spec_id":"curve-bias-variance-tradeoff","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// curve-bias-variance-tradeoff: Bias-Variance Tradeoff Curve\n// Library: chartjs 4.4.7 | JavaScript 22.23.2\n// Quality: 91/100 | Created: 2026-08-24\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Theoretical curves — bias squared decreases with complexity, variance\n// increases, irreducible error is a flat noise floor, total error is their sum.\nconst N = 70;\nconst X_MIN = 0;\nconst X_MAX = 20;\nconst IRREDUCIBLE_ERROR = 0.2;\n\nconst complexity = Array.from({ length: N }, (_, i) => X_MIN + (i * (X_MAX - X_MIN)) / (N - 1));\nconst biasSquared = complexity.map((x) => 3.2 / (1 + x));\nconst variance = complexity.map((x) => 0.006 * x * x);\nconst irreducible = complexity.map(() => IRREDUCIBLE_ERROR);\nconst totalError = complexity.map((x, i) => biasSquared[i] + variance[i] + irreducible[i]);\n\n// Optimal complexity = argmin(total error)\nlet optimalIndex = 0;\nfor (let i = 1; i < N; i++) {\n  if (totalError[i] < totalError[optimalIndex]) optimalIndex = i;\n}\nconst optimalX = complexity[optimalIndex];\n\nconst yMax = Math.ceil(Math.max(...biasSquared, ...totalError) * 1.15 * 10) / 10;\n\n// Nearest sample index for a given x — used to place direct curve labels.\nconst indexAt = (x) => Math.round(((x - X_MIN) * (N - 1)) / (X_MAX - X_MIN));\n\nconst biasPoints = complexity.map((x, i) => ({ x, y: biasSquared[i] }));\nconst variancePoints = complexity.map((x, i) => ({ x, y: variance[i] }));\nconst irreduciblePoints = complexity.map((x, i) => ({ x, y: irreducible[i] }));\nconst totalPoints = complexity.map((x, i) => ({ x, y: totalError[i] }));\n\n// --- Mount -------------------------------------------------------------------\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// --- Zone shading (underfitting left of optimum, overfitting right) ----------\n// Chrome-colored wash (no new hue introduced) — context, not data.\nconst underfitZone = {\n  label: \"Underfitting Zone\",\n  data: [\n    { x: X_MIN, y: yMax },\n    { x: optimalX, y: yMax },\n  ],\n  fill: \"origin\",\n  backgroundColor: `${t.ink}08`,\n  borderWidth: 0,\n  pointRadius: 0,\n  tension: 0,\n  order: 3,\n};\nconst overfitZone = {\n  label: \"Overfitting Zone\",\n  data: [\n    { x: optimalX, y: yMax },\n    { x: X_MAX, y: yMax },\n  ],\n  fill: \"origin\",\n  backgroundColor: `${t.ink}14`,\n  borderWidth: 0,\n  pointRadius: 0,\n  tension: 0,\n  order: 3,\n};\n\n// --- Optimal-complexity reference line ----------------------------------------\nconst optimalLine = {\n  label: \"Optimal Complexity\",\n  data: [\n    { x: optimalX, y: 0 },\n    { x: optimalX, y: yMax },\n  ],\n  fill: false,\n  borderColor: t.amber,\n  borderWidth: 2.5,\n  borderDash: [10, 6],\n  pointRadius: 0,\n  tension: 0,\n  order: 2,\n};\n\n// --- Curve datasets — Imprint palette in canonical order + neutral anchor ----\nconst biasDataset = {\n  label: \"Bias²\",\n  data: biasPoints,\n  fill: false,\n  borderColor: t.palette[0],\n  borderWidth: 3.5,\n  pointRadius: 0,\n  tension: 0,\n  order: 1,\n};\nconst varianceDataset = {\n  label: \"Variance\",\n  data: variancePoints,\n  fill: false,\n  borderColor: t.palette[1],\n  borderWidth: 3.5,\n  pointRadius: 0,\n  tension: 0,\n  order: 1,\n};\nconst irreducibleDataset = {\n  label: \"Irreducible Error\",\n  data: irreduciblePoints,\n  fill: false,\n  borderColor: t.palette[2],\n  borderWidth: 2.5,\n  borderDash: [10, 6],\n  pointRadius: 0,\n  tension: 0,\n  order: 1,\n};\nconst totalDataset = {\n  label: \"Total Error\",\n  // Total error is the reference sum of the other three — Imprint's\n  // theme-adaptive \"neutral\" anchor (totals / baseline / reference line).\n  data: totalPoints,\n  fill: false,\n  borderColor: t.ink,\n  borderWidth: 4,\n  pointRadius: 0,\n  tension: 0,\n  order: 0,\n};\n\n// Inline plugin: label each curve directly on the plot + the optimal point\nconst curveLabels = {\n  id: \"curveLabels\",\n  afterDraw(chart) {\n    const ctx = chart.ctx;\n    const xsc = chart.scales.x;\n    const ysc = chart.scales.y;\n\n    const label = (text, x, y, color, dx, dy, align) => {\n      ctx.save();\n      ctx.font = \"600 18px sans-serif\";\n      ctx.fillStyle = color;\n      ctx.textAlign = align;\n      ctx.textBaseline = \"middle\";\n      ctx.fillText(text, xsc.getPixelForValue(x) + dx, ysc.getPixelForValue(y) + dy);\n      ctx.restore();\n    };\n\n    label(\"Bias²\", 1, biasSquared[indexAt(1)], t.palette[0], -10, -16, \"right\");\n    label(\"Variance\", 17, variance[indexAt(17)], t.palette[1], 10, -16, \"left\");\n    label(\"Irreducible Error\", 3, IRREDUCIBLE_ERROR, t.palette[2], 0, -24, \"center\");\n    label(\"Total Error\", 9.5, totalError[indexAt(9.5)], t.ink, 0, 34, \"center\");\n    label(\"Optimal\", optimalX, yMax, t.amber, 10, 16, \"left\");\n  },\n};\n\n// --- Chart -------------------------------------------------------------------\nnew Chart(canvas, {\n  type: \"line\",\n  plugins: [curveLabels],\n  data: {\n    datasets: [underfitZone, overfitZone, optimalLine, biasDataset, varianceDataset, irreducibleDataset, totalDataset],\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    plugins: {\n      title: {\n        display: true,\n        text: \"curve-bias-variance-tradeoff · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 20 },\n        padding: { top: 12, bottom: 4 },\n      },\n      subtitle: {\n        display: true,\n        text: \"Total Error = Bias² + Variance + Irreducible Error\",\n        color: t.inkSoft,\n        font: { size: 16, style: \"italic\" },\n        padding: { bottom: 14 },\n      },\n      legend: {\n        labels: {\n          color: t.ink,\n          font: { size: 15 },\n          boxWidth: 30,\n          padding: 18,\n          filter: (item) => ![\"Underfitting Zone\", \"Overfitting Zone\"].includes(item.text),\n        },\n      },\n    },\n    scales: {\n      x: {\n        type: \"linear\",\n        min: X_MIN,\n        max: X_MAX,\n        title: {\n          display: true,\n          text: \"Model Complexity (Polynomial Degree)\",\n          color: t.ink,\n          font: { size: 17 },\n        },\n        ticks: { color: t.inkSoft, font: { size: 14 }, stepSize: 5 },\n        grid: { display: false },\n      },\n      y: {\n        type: \"linear\",\n        min: 0,\n        max: yMax,\n        title: {\n          display: true,\n          text: \"Prediction Error\",\n          color: t.ink,\n          font: { size: 17 },\n        },\n        ticks: { color: t.inkSoft, font: { size: 14 } },\n        grid: { color: t.grid },\n      },\n    },\n  },\n});\n"}