{"spec_id":"line-reaction-coordinate","library":"d3","language":"javascript","code":"// anyplot.ai\n// line-reaction-coordinate: Reaction Coordinate Energy Diagram\n// Library: d3 7.9.0 | JavaScript 22.22.3\n// Quality: 91/100 | Created: 2026-06-24\n\nconst t = window.ANYPLOT_TOKENS;\nconst { width, height } = window.ANYPLOT_SIZE;\n\nconst margin = { top: 90, right: 160, bottom: 90, left: 110 };\nconst iw = width - margin.left - margin.right;\nconst ih = height - margin.top - margin.bottom;\n\n// Data: single-step exothermic reaction\n// Reactants: 50 kJ/mol, Transition State: 120 kJ/mol, Products: 20 kJ/mol\nconst E_r = 50, E_ts = 120, E_p = 20;\nconst sigma = 0.15;\nconst A = E_ts - (E_r + (E_p - E_r) * 0.5); // Gaussian amplitude = 85\n\nconst N = 300;\nconst curveData = Array.from({ length: N }, (_, i) => {\n  const x = i / (N - 1);\n  const energy = E_r + A * Math.exp(-Math.pow(x - 0.5, 2) / (2 * sigma * sigma)) + (E_p - E_r) * x;\n  return { x, energy };\n});\n\n// --- SVG mount\nconst svg = d3.select(\"#container\").append(\"svg\").attr(\"width\", width).attr(\"height\", height);\nconst g = svg.append(\"g\").attr(\"transform\", `translate(${margin.left},${margin.top})`);\n\n// --- Scales\nconst xScale = d3.scaleLinear().domain([0, 1]).range([0, iw]);\nconst yScale = d3.scaleLinear().domain([0, 140]).range([ih, 0]);\n\n// --- Defs: arrowhead markers for double-headed arrows\nconst defs = svg.append(\"defs\");\n\ndefs.append(\"marker\")\n  .attr(\"id\", \"arrow-end\")\n  .attr(\"viewBox\", \"0 -5 10 10\")\n  .attr(\"refX\", 8).attr(\"refY\", 0)\n  .attr(\"markerWidth\", 5).attr(\"markerHeight\", 5)\n  .attr(\"orient\", \"auto\")\n  .append(\"path\").attr(\"d\", \"M0,-5L10,0L0,5\").attr(\"fill\", t.inkSoft);\n\ndefs.append(\"marker\")\n  .attr(\"id\", \"arrow-start\")\n  .attr(\"viewBox\", \"0 -5 10 10\")\n  .attr(\"refX\", 2).attr(\"refY\", 0)\n  .attr(\"markerWidth\", 5).attr(\"markerHeight\", 5)\n  .attr(\"orient\", \"auto-start-reverse\")\n  .append(\"path\").attr(\"d\", \"M0,-5L10,0L0,5\").attr(\"fill\", t.inkSoft);\n\n// --- Y-axis gridlines\ng.append(\"g\")\n  .selectAll(\"line.grid\")\n  .data(yScale.ticks(7))\n  .join(\"line\")\n  .attr(\"class\", \"grid\")\n  .attr(\"x1\", 0).attr(\"x2\", iw)\n  .attr(\"y1\", d => yScale(d)).attr(\"y2\", d => yScale(d))\n  .attr(\"stroke\", t.grid)\n  .attr(\"stroke-width\", 1);\n\n// --- Subtle shaded ΔH region between reactant and product energy levels\ng.append(\"rect\")\n  .attr(\"x\", 0)\n  .attr(\"y\", yScale(E_r))\n  .attr(\"width\", iw)\n  .attr(\"height\", yScale(E_p) - yScale(E_r))\n  .attr(\"fill\", t.palette[0])\n  .attr(\"opacity\", 0.08);\n\n// --- Horizontal dashed reference lines at reactant and product energy levels\ng.append(\"line\")\n  .attr(\"x1\", 0).attr(\"x2\", xScale(0.38))\n  .attr(\"y1\", yScale(E_r)).attr(\"y2\", yScale(E_r))\n  .attr(\"stroke\", t.inkSoft)\n  .attr(\"stroke-width\", 1.5)\n  .attr(\"stroke-dasharray\", \"8,5\")\n  .attr(\"opacity\", 0.6);\n\ng.append(\"line\")\n  .attr(\"x1\", xScale(0.62)).attr(\"x2\", iw)\n  .attr(\"y1\", yScale(E_p)).attr(\"y2\", yScale(E_p))\n  .attr(\"stroke\", t.inkSoft)\n  .attr(\"stroke-width\", 1.5)\n  .attr(\"stroke-dasharray\", \"8,5\")\n  .attr(\"opacity\", 0.6);\n\n// --- Ea double-headed arrow (reactant level → transition state peak)\nconst eaX = xScale(0.22);\ng.append(\"line\")\n  .attr(\"x1\", eaX).attr(\"y1\", yScale(E_r) - 2)\n  .attr(\"x2\", eaX).attr(\"y2\", yScale(E_ts) + 2)\n  .attr(\"stroke\", t.inkSoft)\n  .attr(\"stroke-width\", 2)\n  .attr(\"marker-start\", \"url(#arrow-start)\")\n  .attr(\"marker-end\", \"url(#arrow-end)\");\n\ng.append(\"text\")\n  .attr(\"x\", eaX + 10)\n  .attr(\"y\", yScale((E_r + E_ts) / 2))\n  .attr(\"fill\", t.ink)\n  .attr(\"dominant-baseline\", \"middle\")\n  .style(\"font-size\", \"15px\")\n  .text(`Eₐ = ${E_ts - E_r} kJ/mol`);\n\n// --- ΔH double-headed arrow (reactant level → product level)\nconst dhX = xScale(0.78);\ng.append(\"line\")\n  .attr(\"x1\", dhX).attr(\"y1\", yScale(E_r) + 2)\n  .attr(\"x2\", dhX).attr(\"y2\", yScale(E_p) - 2)\n  .attr(\"stroke\", t.inkSoft)\n  .attr(\"stroke-width\", 2)\n  .attr(\"marker-start\", \"url(#arrow-start)\")\n  .attr(\"marker-end\", \"url(#arrow-end)\");\n\ng.append(\"text\")\n  .attr(\"x\", dhX + 16)\n  .attr(\"y\", yScale((E_r + E_p) / 2))\n  .attr(\"fill\", t.ink)\n  .attr(\"dominant-baseline\", \"middle\")\n  .style(\"font-size\", \"15px\")\n  .text(`ΔH = ${E_p - E_r} kJ/mol`);\n\n// --- Main reaction coordinate curve\nconst line = d3.line()\n  .x(d => xScale(d.x))\n  .y(d => yScale(d.energy))\n  .curve(d3.curveCatmullRom.alpha(0.5));\n\ng.append(\"path\")\n  .datum(curveData)\n  .attr(\"fill\", \"none\")\n  .attr(\"stroke\", t.palette[0])\n  .attr(\"stroke-width\", 4)\n  .attr(\"d\", line);\n\n// --- Direct labels for Reactants, Transition State, Products\n// Reactants\ng.append(\"text\")\n  .attr(\"x\", xScale(0.07))\n  .attr(\"y\", yScale(E_r) - 20)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.ink)\n  .style(\"font-size\", \"16px\")\n  .style(\"font-weight\", \"600\")\n  .text(\"Reactants\");\n\ng.append(\"text\")\n  .attr(\"x\", xScale(0.07))\n  .attr(\"y\", yScale(E_r) - 4)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.inkSoft)\n  .style(\"font-size\", \"14px\")\n  .text(`${E_r} kJ/mol`);\n\n// Transition State\ng.append(\"text\")\n  .attr(\"x\", xScale(0.5))\n  .attr(\"y\", yScale(E_ts) - 20)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.ink)\n  .style(\"font-size\", \"16px\")\n  .style(\"font-weight\", \"600\")\n  .text(\"Transition State ‡\");\n\ng.append(\"text\")\n  .attr(\"x\", xScale(0.5))\n  .attr(\"y\", yScale(E_ts) - 4)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.inkSoft)\n  .style(\"font-size\", \"14px\")\n  .text(`${E_ts} kJ/mol`);\n\n// Products\ng.append(\"text\")\n  .attr(\"x\", xScale(0.93))\n  .attr(\"y\", yScale(E_p) - 20)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.ink)\n  .style(\"font-size\", \"16px\")\n  .style(\"font-weight\", \"600\")\n  .text(\"Products\");\n\ng.append(\"text\")\n  .attr(\"x\", xScale(0.93))\n  .attr(\"y\", yScale(E_p) - 4)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.inkSoft)\n  .style(\"font-size\", \"14px\")\n  .text(`${E_p} kJ/mol`);\n\n// --- Axes\nconst xAxis = g.append(\"g\")\n  .attr(\"transform\", `translate(0,${ih})`)\n  .call(d3.axisBottom(xScale).ticks(0).tickSize(0));\n\nxAxis.select(\".domain\").attr(\"stroke\", t.inkSoft).attr(\"stroke-width\", 1.5);\nxAxis.selectAll(\"text\").remove();\n\nconst yAxis = g.append(\"g\")\n  .call(d3.axisLeft(yScale).ticks(7).tickSize(5));\n\nyAxis.selectAll(\"text\").attr(\"fill\", t.inkSoft).style(\"font-size\", \"14px\");\nyAxis.selectAll(\".tick line\").attr(\"stroke\", t.grid);\nyAxis.select(\".domain\").attr(\"stroke\", \"none\");\n\n// --- Axis labels\nsvg.append(\"text\")\n  .attr(\"transform\", `translate(${margin.left - 72},${margin.top + ih / 2}) rotate(-90)`)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.ink)\n  .style(\"font-size\", \"18px\")\n  .text(\"Potential Energy (kJ/mol)\");\n\nsvg.append(\"text\")\n  .attr(\"x\", margin.left + iw / 2)\n  .attr(\"y\", margin.top + ih + 62)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.ink)\n  .style(\"font-size\", \"18px\")\n  .text(\"Reaction Coordinate →\");\n\n// --- Title\nsvg.append(\"text\")\n  .attr(\"x\", width / 2).attr(\"y\", 50)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.ink)\n  .style(\"font-size\", \"22px\")\n  .style(\"font-weight\", \"600\")\n  .text(\"line-reaction-coordinate · javascript · d3 · anyplot.ai\");\n"}