{"spec_id":"calibration-beer-lambert","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// calibration-beer-lambert: Beer-Lambert Calibration Curve\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 92/100 | Created: 2026-08-20\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data: nitrate colorimetric assay standards, 540 nm ---------------------\n// concentration in mg/L NO3-N, absorbance dimensionless (incl. reagent blank)\nconst standards = [\n  [0, 0.004],\n  [1, 0.089],\n  [2, 0.163],\n  [4, 0.335],\n  [6, 0.492],\n  [8, 0.667],\n  [10, 0.821],\n  [12, 0.991],\n];\nconst n = standards.length;\n\n// --- Ordinary least-squares fit ---------------------------------------------\nconst xs = standards.map((p) => p[0]);\nconst ys = standards.map((p) => p[1]);\nconst xBar = xs.reduce((a, b) => a + b, 0) / n;\nconst yBar = ys.reduce((a, b) => a + b, 0) / n;\nlet sxx = 0;\nlet sxy = 0;\nlet syy = 0;\nfor (let i = 0; i < n; i++) {\n  const dx = xs[i] - xBar;\n  const dy = ys[i] - yBar;\n  sxx += dx * dx;\n  sxy += dx * dy;\n  syy += dy * dy;\n}\nconst slope = sxy / sxx;\nconst intercept = yBar - slope * xBar;\nconst r2 = (sxy * sxy) / (sxx * syy);\nconst sse = ys.reduce((acc, y, i) => acc + (y - (slope * xs[i] + intercept)) ** 2, 0);\nconst residualStdErr = Math.sqrt(sse / (n - 2));\nconst tCrit = 2.447; // 95% two-tail critical value at df=6\n\n// Regression line + 95% prediction interval band across the standards' range.\nconst xMin = Math.min(...xs);\nconst xMax = Math.max(...xs);\nconst steps = 40;\nconst lineData = [];\nconst piUpper = [];\nconst piLower = [];\nfor (let i = 0; i <= steps; i++) {\n  const x0 = xMin + ((xMax - xMin) * i) / steps;\n  const yHat = slope * x0 + intercept;\n  const se = residualStdErr * Math.sqrt(1 + 1 / n + (x0 - xBar) ** 2 / sxx);\n  lineData.push([x0, yHat]);\n  piUpper.push([x0, yHat + tCrit * se]);\n  piLower.push([x0, yHat - tCrit * se]);\n}\n\n// Example unknown sample: measured absorbance, back-calculated concentration.\nconst unknownAbsorbance = 0.45;\nconst unknownConcentration = (unknownAbsorbance - intercept) / slope;\n\n// --- Chart -------------------------------------------------------------------\n// arearange lives in highcharts-more (not vendored), so the prediction band is\n// drawn as a plain SVG path in the core renderer, redrawn on every chart render.\nlet bandPath;\nHighcharts.chart(\"container\", {\n  chart: {\n    type: \"scatter\",\n    backgroundColor: \"transparent\",\n    animation: false,\n    style: { fontFamily: \"inherit\" },\n    events: {\n      render: function () {\n        const xAxis = this.xAxis[0];\n        const yAxis = this.yAxis[0];\n        const upper = piUpper.map(\n          (p, i) => `${i === 0 ? \"M\" : \"L\"} ${xAxis.toPixels(p[0], false)} ${yAxis.toPixels(p[1], false)}`,\n        );\n        const lower = piLower\n          .slice()\n          .reverse()\n          .map((p) => `L ${xAxis.toPixels(p[0], false)} ${yAxis.toPixels(p[1], false)}`);\n        const d = `${upper.join(\" \")} ${lower.join(\" \")} Z`;\n        if (bandPath) {\n          bandPath.attr({ d });\n        } else {\n          bandPath = this.renderer\n            .path()\n            .attr({ d, fill: Highcharts.color(t.palette[2]).setOpacity(0.15).get(), zIndex: 2 })\n            .add();\n        }\n      },\n    },\n  },\n  credits: { enabled: false },\n  colors: t.palette,\n  title: {\n    text: \"calibration-beer-lambert · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" },\n  },\n  subtitle: {\n    text: `R² = ${r2.toFixed(4)}  ·  A = ${slope.toFixed(4)}·c + ${intercept.toFixed(4)}`,\n    style: { color: t.inkSoft, fontSize: \"15px\" },\n  },\n  xAxis: {\n    title: { text: \"Concentration (mg/L NO₃-N)\", style: { color: t.inkSoft, fontSize: \"16px\" } },\n    lineColor: t.inkSoft,\n    tickColor: t.inkSoft,\n    gridLineColor: t.grid,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n    min: -0.5,\n    max: xMax + 1,\n    plotLines: [\n      {\n        value: unknownConcentration,\n        color: t.inkSoft,\n        dashStyle: \"Dash\",\n        width: 1.5,\n        zIndex: 3,\n      },\n    ],\n  },\n  yAxis: {\n    title: { text: \"Absorbance\", style: { color: t.inkSoft, fontSize: \"16px\" } },\n    lineColor: t.inkSoft,\n    tickColor: t.inkSoft,\n    gridLineColor: t.grid,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n    min: 0,\n    plotLines: [\n      {\n        value: unknownAbsorbance,\n        color: t.inkSoft,\n        dashStyle: \"Dash\",\n        width: 1.5,\n        zIndex: 3,\n      },\n    ],\n  },\n  legend: {\n    align: \"left\",\n    verticalAlign: \"top\",\n    x: 60,\n    y: 10,\n    itemStyle: { color: t.inkSoft, fontSize: \"14px\" },\n    itemHoverStyle: { color: t.ink },\n  },\n  tooltip: {\n    pointFormat: \"Concentration: {point.x:.2f} mg/L<br/>Absorbance: {point.y:.3f}\",\n  },\n  plotOptions: {\n    series: { animation: false },\n  },\n  series: [\n    {\n      type: \"scatter\",\n      name: \"Calibration Standards\",\n      data: standards,\n      zIndex: 5,\n      marker: {\n        radius: 6,\n        fillColor: t.palette[0],\n        lineColor: t.pageBg,\n        lineWidth: 1,\n      },\n    },\n    {\n      type: \"line\",\n      name: \"Regression Fit\",\n      data: lineData,\n      color: t.palette[2],\n      lineWidth: 2.5,\n      zIndex: 4,\n      marker: { enabled: false },\n      enableMouseTracking: false,\n    },\n    {\n      type: \"column\",\n      name: \"95% Prediction Interval\",\n      data: [],\n      color: Highcharts.color(t.palette[2]).setOpacity(0.15).get(),\n      legendSymbol: \"rectangle\",\n      showInLegend: true,\n      enableMouseTracking: false,\n    },\n    {\n      type: \"scatter\",\n      name: \"Unknown Sample\",\n      data: [{ x: unknownConcentration, y: unknownAbsorbance }],\n      zIndex: 6,\n      marker: {\n        symbol: \"diamond\",\n        radius: 8,\n        fillColor: t.amber,\n        lineColor: t.pageBg,\n        lineWidth: 1,\n      },\n      dataLabels: {\n        enabled: true,\n        format: `c ≈ ${unknownConcentration.toFixed(2)} mg/L`,\n        style: { color: t.ink, fontSize: \"14px\", fontWeight: \"500\", textOutline: \"none\" },\n        align: \"right\",\n        x: -16,\n        y: 20,\n      },\n    },\n  ],\n});\n"}