{"spec_id":"scatter-color-mapped","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// scatter-color-mapped: Color-Mapped Scatter Plot\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 87/100 | Created: 2026-09-05\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic fixed-seed LCG) -------------------------\nlet seed = 42;\nfunction rand() {\n  seed = (1664525 * seed + 1013904223) % 4294967296;\n  return seed / 4294967296;\n}\n\nconst pointCount = 150;\nconst readings = [];\nfor (let i = 0; i < pointCount; i++) {\n  const temperature = 150 + rand() * 100; // reaction temperature, deg C\n  const duration = 10 + rand() * 80; // reaction duration, minutes\n  const noise = (rand() - 0.5) * 15;\n  let yieldPct =\n    95 -\n    0.012 * (temperature - 200) ** 2 -\n    0.02 * (duration - 50) ** 2 +\n    noise;\n  yieldPct = Math.max(5, Math.min(98, yieldPct));\n  readings.push({ x: temperature, y: duration, yieldPct });\n}\n\nconst yieldValues = readings.map((r) => r.yieldPct);\nconst yieldMin = Math.min(...yieldValues);\nconst yieldMax = Math.max(...yieldValues);\n\n// --- Color mapping ------------------------------------------------------------\n// The `coloraxis` module (colorAxis + automatic legend gradient) lives in\n// modules/coloraxis.js, which isn't loaded — only the core bundle is. Interpolate\n// each point's fill from the Imprint imprint_seq gradient by hand instead, and\n// draw a matching colorbar with the core SVG renderer.\nfunction hexToRgb(hex) {\n  const v = parseInt(hex.slice(1), 16);\n  return [(v >> 16) & 255, (v >> 8) & 255, v & 255];\n}\nconst seqLow = hexToRgb(t.seq[0]);\nconst seqHigh = hexToRgb(t.seq[1]);\nfunction yieldColor(value) {\n  const ratio = (value - yieldMin) / (yieldMax - yieldMin);\n  const [r, g, b] = seqLow.map((c, i) => Math.round(c + (seqHigh[i] - c) * ratio));\n  return `rgb(${r}, ${g}, ${b})`;\n}\n\nconst points = readings.map((r) => ({\n  x: r.x,\n  y: r.y,\n  color: yieldColor(r.yieldPct),\n  custom: { yieldPct: r.yieldPct },\n}));\n\nconst peak = readings.reduce((best, r) => (r.yieldPct > best.yieldPct ? r : best));\n\n// --- Chart -------------------------------------------------------------------\nHighcharts.chart(\"container\", {\n  chart: {\n    type: \"scatter\",\n    backgroundColor: \"transparent\",\n    animation: false,\n    marginRight: 190,\n    style: { fontFamily: \"inherit\" },\n    events: {\n      load: function () {\n        // Manual colorbar (see \"Color mapping\" note above) mirroring the\n        // imprint_seq gradient used for the point fills.\n        const chart = this;\n        const barWidth = 26;\n        const barX = chart.plotLeft + chart.plotWidth + 46;\n        const barY = chart.plotTop;\n        const barHeight = chart.plotHeight;\n\n        // Fragment-url paint servers (linearGradient defs) don't resolve on the\n        // harness's about:blank document, so the bar is built from many thin\n        // interpolated bands instead of a single gradient fill.\n        const bandCount = 60;\n        const bandHeight = barHeight / bandCount;\n        for (let i = 0; i < bandCount; i++) {\n          const frac = (i + 0.5) / bandCount;\n          const value = yieldMin + frac * (yieldMax - yieldMin);\n          chart.renderer\n            .rect(barX, barY + barHeight - (i + 1) * bandHeight, barWidth, bandHeight + 0.5)\n            .attr({ fill: yieldColor(value) })\n            .add();\n        }\n        chart.renderer\n          .rect(barX, barY, barWidth, barHeight)\n          .attr({ fill: \"none\", stroke: t.inkSoft, \"stroke-width\": 1 })\n          .add();\n\n        const tickCount = 5;\n        for (let i = 0; i < tickCount; i++) {\n          const frac = i / (tickCount - 1);\n          const value = yieldMin + frac * (yieldMax - yieldMin);\n          const tickY = barY + barHeight * (1 - frac);\n          chart.renderer\n            .path([\"M\", barX + barWidth, tickY, \"L\", barX + barWidth + 6, tickY])\n            .attr({ stroke: t.inkSoft, \"stroke-width\": 1 })\n            .add();\n          chart.renderer\n            .text(value.toFixed(0), barX + barWidth + 12, tickY + 5)\n            .css({ color: t.inkSoft, fontSize: \"14px\" })\n            .add();\n        }\n\n        chart.renderer\n          .text(\"Reaction Yield (%)\", barX + barWidth + 60, barY + barHeight / 2)\n          .attr({ rotation: 90, align: \"center\" })\n          .css({ color: t.inkSoft, fontSize: \"16px\" })\n          .add();\n\n        // Storytelling touch: ring the highest-yield point and label it, so the\n        // chart guides the viewer to the reaction's sweet spot instead of leaving\n        // an undifferentiated point cloud.\n        const peakX = chart.xAxis[0].toPixels(peak.x, false);\n        const peakY = chart.yAxis[0].toPixels(peak.y, false);\n        chart.renderer\n          .circle(peakX, peakY, 13)\n          .attr({ fill: \"none\", stroke: t.ink, \"stroke-width\": 1.5, dashstyle: \"Dash\" })\n          .add();\n        chart.renderer\n          .label(`Peak yield ${peak.yieldPct.toFixed(0)}%`, peakX + 16, peakY - 26)\n          .css({ color: t.ink, fontSize: \"13px\", fontWeight: \"600\" })\n          .attr({ padding: 4, zIndex: 6 })\n          .add();\n      },\n    },\n  },\n  credits: { enabled: false },\n  accessibility: { enabled: false },\n  title: {\n    text: \"scatter-color-mapped · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" },\n  },\n  legend: { enabled: false },\n  tooltip: {\n    pointFormat:\n      \"Temperature: {point.x:.0f}°C<br/>Duration: {point.y:.0f} min<br/>Yield: {point.custom.yieldPct:.1f}%\",\n  },\n  xAxis: {\n    title: {\n      text: \"Reaction Temperature (°C)\",\n      style: { color: t.inkSoft, fontSize: \"16px\" },\n    },\n    lineColor: t.inkSoft,\n    tickColor: t.inkSoft,\n    gridLineColor: t.grid,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n  },\n  yAxis: {\n    title: {\n      text: \"Reaction Duration (min)\",\n      style: { color: t.inkSoft, fontSize: \"16px\" },\n    },\n    lineColor: t.inkSoft,\n    tickColor: t.inkSoft,\n    gridLineColor: t.grid,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n  },\n  plotOptions: {\n    series: { animation: false },\n    scatter: {\n      marker: {\n        radius: 7,\n        lineWidth: 1,\n        lineColor: t.pageBg,\n        fillOpacity: 0.88,\n      },\n    },\n  },\n  series: [\n    {\n      name: \"Yield\",\n      showInLegend: false,\n      data: points,\n    },\n  ],\n});\n"}