{"spec_id":"spectrum-nmr","library":"muix","language":"javascript","code":"// anyplot.ai\n// spectrum-nmr: NMR Spectrum (Nuclear Magnetic Resonance)\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 85/100 | Created: 2026-08-24\n//# anyplot-orientation: landscape\n// anyplot.ai\n// spectrum-nmr: NMR Spectrum (Nuclear Magnetic Resonance)\n// Library: MUI X Charts | React | Node 22\n// License: @mui/x-charts — MIT (community). Pro/Premium are out of scope.\n// Quality: pending | Created: 2026-08-24\nimport { LineChart } from \"@mui/x-charts/LineChart\";\nimport { ChartsReferenceLine } from \"@mui/x-charts/ChartsReferenceLine\";\nimport Box from \"@mui/material/Box\";\nimport Typography from \"@mui/material/Typography\";\n\nconst t = window.ANYPLOT_TOKENS;\n\nconst TITLE_HEIGHT = 60;\n\n// Deterministic LCG (seed 42) — no Math.random() in the browser harness\nlet seed = 42;\nfunction rng() {\n  seed = (1664525 * seed + 1013904223) >>> 0;\n  return seed / 4294967296;\n}\n\n// --- Data: synthetic 1H NMR spectrum of ethanol (CH3-CH2-OH) ---------------\nconst PPM_MIN = -0.5;\nconst PPM_MAX = 4.6;\nconst POINT_COUNT = 6000;\nconst step = (PPM_MAX - PPM_MIN) / (POINT_COUNT - 1);\nconst chemicalShift = Array.from({ length: POINT_COUNT }, (_, i) => PPM_MIN + i * step);\nconst intensity = chemicalShift.map(() => rng() * 0.6); // clean baseline, minimal noise\n\nfunction addLorentzian(center, halfWidth, peakHeight) {\n  chemicalShift.forEach((shift, i) => {\n    const z = (shift - center) / halfWidth;\n    intensity[i] += peakHeight / (1 + z * z);\n  });\n}\n\nfunction addMultiplet(center, splitting, relativeWeights, halfWidth, unitHeight) {\n  const n = relativeWeights.length;\n  relativeWeights.forEach((weight, i) => {\n    const lineCenter = center + (i - (n - 1) / 2) * splitting;\n    addLorentzian(lineCenter, halfWidth, unitHeight * weight);\n  });\n}\n\n// TMS internal standard — singlet reference at 0 ppm\naddMultiplet(0.0, 0, [1], 0.006, 24);\n// CH3 — triplet (coupled to adjacent CH2); narrow lines with wide spacing so\n// the 1:2:1 pattern resolves into three distinct peaks, not one blob\naddMultiplet(1.2, 0.024, [1, 2, 1], 0.004, 34);\n// OH — singlet, slightly broadened by proton exchange\naddMultiplet(2.6, 0, [1], 0.012, 52);\n// CH2 — quartet (coupled to adjacent CH3); narrow lines with wide spacing so\n// the 1:3:3:1 pattern resolves into four distinct peaks, not one blob\naddMultiplet(3.7, 0.024, [1, 3, 3, 1], 0.004, 20);\n\nconst PEAK_LABELS = [\n  { shift: 0.0, label: \"0.00 (TMS)\" },\n  { shift: 1.2, label: \"1.20 (CH₃)\" },\n  { shift: 2.6, label: \"2.60 (OH)\" },\n  { shift: 3.7, label: \"3.70 (CH₂)\" },\n];\n\n// --- Chart (default-exported component — the harness mounts it) -----------\nexport default function Chart() {\n  const { width, height } = window.ANYPLOT_SIZE;\n\n  return (\n    <Box sx={{ width, height, display: \"flex\", flexDirection: \"column\", paddingTop: \"20px\" }}>\n      <Typography\n        sx={{ color: t.ink, fontSize: 22, fontWeight: 500, textAlign: \"center\", lineHeight: 1.2 }}\n      >\n        spectrum-nmr · javascript · muix · anyplot.ai\n      </Typography>\n      <LineChart\n        width={width}\n        height={height - TITLE_HEIGHT}\n        margin={{ top: 70, right: 48, bottom: 72, left: 88 }}\n        xAxis={[\n          {\n            data: chemicalShift,\n            scaleType: \"linear\",\n            reverse: true,\n            min: PPM_MIN,\n            max: PPM_MAX,\n            label: \"Chemical Shift (δ, ppm)\",\n            tickLabelStyle: { fontSize: 14 },\n            labelStyle: { fontSize: 16 },\n          },\n        ]}\n        yAxis={[\n          {\n            min: -5,\n            max: 90,\n            label: \"Intensity (a.u.)\",\n            tickLabelStyle: { fontSize: 14 },\n            labelStyle: { fontSize: 16 },\n          },\n        ]}\n        series={[\n          {\n            data: intensity,\n            curve: \"linear\",\n            showMark: false,\n            color: t.palette[0],\n            label: \"1H NMR\",\n          },\n        ]}\n        grid={{ horizontal: true, vertical: false }}\n        slotProps={{ legend: { hidden: true } }}\n        skipAnimation\n      >\n        {PEAK_LABELS.map(({ shift, label }) => (\n          <ChartsReferenceLine\n            key={label}\n            x={shift}\n            label={label}\n            labelStyle={{ fontSize: 13, fill: t.inkSoft }}\n            lineStyle={{ stroke: t.inkSoft, strokeDasharray: \"6 4\", strokeWidth: 1.5 }}\n          />\n        ))}\n      </LineChart>\n    </Box>\n  );\n}\n"}