{"spec_id":"titration-curve","library":"muix","language":"javascript","code":"// anyplot.ai\n// titration-curve: Acid-Base Titration Curve\n// Library: muix 7.29.1 | JavaScript 22.22.3\n// Quality: 84/100 | Created: 2026-06-24\n\nimport { LineChart } from \"@mui/x-charts/LineChart\";\nimport { ChartsReferenceLine } from \"@mui/x-charts/ChartsReferenceLine\";\nimport { useDrawingArea, useXScale } from \"@mui/x-charts/hooks\";\nimport Box from \"@mui/material/Box\";\nimport Typography from \"@mui/material/Typography\";\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data: 25 mL of 0.1 M HCl titrated with 0.1 M NaOH -----------------\nconst N = 201;\nconst VOL_MAX = 50;\nconst EQ_VOL = 25;\n// Transition zone: ±4 mL around equivalence — the steep-jump buffer region\nconst BUFFER_LO = EQ_VOL - 4;\nconst BUFFER_HI = EQ_VOL + 4;\n\nfunction phAt(V) {\n  if (Math.abs(V - EQ_VOL) < 1e-4) return 7.0;\n  if (V < EQ_VOL) {\n    const excessH = 0.0025 - V * 1e-4;\n    return Math.max(0, -Math.log10(excessH / ((25 + V) / 1000)));\n  }\n  const excessOH = (V - EQ_VOL) * 1e-4;\n  const pOH = -Math.log10(excessOH / ((25 + V) / 1000));\n  return Math.min(14, 14 - pOH);\n}\n\n// 201 evenly-spaced volume points (0–50 mL); index 100 = 25.00 mL exactly\nconst volumes = Array.from({ length: N }, (_, i) =>\n  parseFloat(((i / (N - 1)) * VOL_MAX).toFixed(2))\n);\nconst phData = volumes.map((v) => parseFloat(phAt(v).toFixed(3)));\n\n// Central-difference derivative dpH/dV — spikes sharply at the equivalence point\nconst derivData = volumes.map((_, i) => {\n  if (i === 0 || i === N - 1) return 0;\n  const dv = volumes[i + 1] - volumes[i - 1];\n  const dph = phData[i + 1] - phData[i - 1];\n  return parseFloat((dph / dv).toFixed(3));\n});\n\nconst maxDeriv = Math.max(...derivData); // ≈ 14.8 at equivalence\n\n// --- Buffer zone shading component (uses chart context hooks) -------------\nfunction BufferZone() {\n  const { top, height } = useDrawingArea();\n  const xScale = useXScale(\"vol\");\n  if (!xScale) return null;\n  const x1 = xScale(BUFFER_LO);\n  const x2 = xScale(BUFFER_HI);\n  return (\n    <rect\n      x={x1}\n      y={top}\n      width={x2 - x1}\n      height={height}\n      fill={t.palette[2]}\n      fillOpacity={0.1}\n    />\n  );\n}\n\n// --- Chart ---------------------------------------------------------------\nexport default function Chart() {\n  const W = window.ANYPLOT_SIZE.width;\n  const H = window.ANYPLOT_SIZE.height;\n  const TITLE_H = 60;\n\n  return (\n    <Box\n      sx={{\n        width: W,\n        height: H,\n        bgcolor: t.pageBg,\n        display: \"flex\",\n        flexDirection: \"column\",\n        alignItems: \"center\",\n      }}\n    >\n      {/* Chart title */}\n      <Box sx={{ height: TITLE_H, display: \"flex\", alignItems: \"center\" }}>\n        <Typography\n          sx={{\n            fontSize: 22,\n            fontWeight: 500,\n            color: t.ink,\n            letterSpacing: 0.3,\n          }}\n        >\n          titration-curve · javascript · muix · anyplot.ai\n        </Typography>\n      </Box>\n\n      <LineChart\n        width={W}\n        height={H - TITLE_H}\n        skipAnimation\n        colors={[t.palette[0], t.palette[1]]}\n        grid={{ horizontal: true }}\n        xAxis={[\n          {\n            id: \"vol\",\n            data: volumes,\n            scaleType: \"linear\",\n            label: \"Volume of NaOH added (mL)\",\n            min: 0,\n            max: VOL_MAX,\n            tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n            labelStyle: { fontSize: 16, fill: t.ink },\n          },\n        ]}\n        yAxis={[\n          {\n            id: \"ph\",\n            min: 0,\n            max: 14,\n            label: \"pH\",\n            tickNumber: 8,\n            tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n            labelStyle: { fontSize: 16, fill: t.ink },\n          },\n          {\n            id: \"deriv\",\n            position: \"right\",\n            min: 0,\n            max: Math.ceil(maxDeriv * 1.2),\n            label: \"dpH/dV (mL⁻¹)\",\n            tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n            labelStyle: { fontSize: 16, fill: t.ink },\n          },\n        ]}\n        series={[\n          {\n            data: phData,\n            label: \"pH\",\n            yAxisId: \"ph\",\n            showMark: false,\n            curve: \"monotoneX\",\n            strokeWidth: 3,\n          },\n          {\n            data: derivData,\n            label: \"dpH/dV\",\n            yAxisId: \"deriv\",\n            showMark: false,\n            curve: \"monotoneX\",\n            strokeWidth: 2.5,\n            area: true,\n          },\n        ]}\n        margin={{ top: 30, bottom: 90, left: 90, right: 120 }}\n        sx={{\n          // Explicit grid line color and opacity (not relying solely on ThemeProvider)\n          \"& .MuiChartsGrid-line\": {\n            stroke: t.grid,\n            strokeWidth: 0.8,\n          },\n          // Explicit axis line and tick styling\n          \"& .MuiChartsAxis-line\": {\n            stroke: t.inkSoft,\n            strokeWidth: 0.5,\n          },\n          \"& .MuiChartsAxis-tick\": {\n            stroke: t.inkSoft,\n          },\n          // Subtle area fill under the dpH/dV spike\n          \"& .MuiAreaElement-root\": {\n            fillOpacity: 0.15,\n          },\n        }}\n        slotProps={{\n          legend: {\n            position: { vertical: \"top\", horizontal: \"right\" },\n            itemMarkWidth: 18,\n            itemMarkHeight: 3,\n            labelStyle: { fontSize: 14, fill: t.ink },\n          },\n        }}\n      >\n        {/* Buffer / transition zone: semi-transparent shading around equivalence region */}\n        <BufferZone />\n\n        {/* Equivalence point dashed marker with annotation */}\n        <ChartsReferenceLine\n          x={EQ_VOL}\n          lineStyle={{\n            strokeDasharray: \"8 5\",\n            stroke: t.inkSoft,\n            strokeWidth: 1.5,\n          }}\n          label=\"Equivalence Point — 25 mL, pH 7\"\n          labelAlign=\"start\"\n          labelStyle={{ fontSize: 13, fill: t.inkSoft }}\n        />\n      </LineChart>\n    </Box>\n  );\n}\n"}