{"spec_id":"density-rug","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// density-rug: Density Plot with Rug Marks\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 90/100 | Created: 2026-09-02\n\n//# anyplot-orientation: landscape\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic, LCG-seeded Box-Muller) ----------------\nconst makeLcg = (seed) => {\n  let state = seed >>> 0;\n  return () => {\n    state = (Math.imul(state, 1664525) + 1013904223) >>> 0;\n    return state / 4294967296;\n  };\n};\nconst rand = makeLcg(42);\nconst gaussian = () => {\n  const u1 = Math.max(rand(), 1e-12);\n  const u2 = rand();\n  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n};\n\n// Reaction times (ms) from a lexical-decision task: a fast majority group\n// plus a slower, more variable tail group — a realistic bimodal shape.\nconst reactionTimes = [];\nfor (let i = 0; i < 150; i++) {\n  const isSlowGroup = rand() < 0.3;\n  const mean = isSlowGroup ? 485 : 320;\n  const spread = isSlowGroup ? 55 : 38;\n  reactionTimes.push(Math.round(mean + gaussian() * spread));\n}\nreactionTimes.sort((a, b) => a - b);\n\n// --- Kernel density estimation (Gaussian kernel, Silverman bandwidth) ------\nconst n = reactionTimes.length;\nconst mean = reactionTimes.reduce((s, v) => s + v, 0) / n;\nconst variance = reactionTimes.reduce((s, v) => s + (v - mean) ** 2, 0) / (n - 1);\nconst std = Math.sqrt(variance);\nconst bandwidth = 1.06 * std * n ** (-1 / 5);\n\nconst gaussianKernel = (u) => Math.exp(-0.5 * u * u) / Math.sqrt(2 * Math.PI);\nconst densityAt = (x) =>\n  reactionTimes.reduce((s, v) => s + gaussianKernel((x - v) / bandwidth), 0) / (n * bandwidth);\n\nconst gridMin = reactionTimes[0] - 3 * bandwidth;\nconst gridMax = reactionTimes[n - 1] + 3 * bandwidth;\nconst gridSteps = 200;\nconst densityCurve = [];\nfor (let i = 0; i <= gridSteps; i++) {\n  const x = gridMin + ((gridMax - gridMin) * i) / gridSteps;\n  densityCurve.push([x, densityAt(x)]);\n}\nconst maxDensity = Math.max(...densityCurve.map((p) => p[1]));\n\n// Rug ticks sit below the zero line as a thin band, separated from the fill.\nconst tickHeight = maxDensity * 0.12;\nconst rugColor = `${t.palette[0]}73`; // brand green, ~45% opacity (overlap-friendly)\n\n// Nice round y-axis tick interval (1/2/5 * 10^k) so labeled ticks are spaced\n// far enough apart to stay visually distinct after rounding.\nconst niceStep = (rough) => {\n  const exp = Math.floor(Math.log10(rough));\n  const base = rough / 10 ** exp;\n  const niceBase = base < 1.5 ? 1 : base < 3 ? 2 : base < 7 ? 5 : 10;\n  return niceBase * 10 ** exp;\n};\nconst yTickInterval = niceStep(maxDensity / 5);\n\n// --- Chart -------------------------------------------------------------\nHighcharts.chart(\"container\", {\n  chart: {\n    backgroundColor: \"transparent\",\n    animation: false,\n    style: { fontFamily: \"inherit\" },\n  },\n  credits: { enabled: false },\n  colors: t.palette,\n  title: {\n    text: \"density-rug · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" },\n  },\n  xAxis: {\n    title: { text: \"Reaction Time (ms)\", 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: gridMin,\n    max: gridMax,\n  },\n  yAxis: {\n    title: { text: \"Density\", style: { color: t.inkSoft, fontSize: \"16px\" } },\n    gridLineColor: t.grid,\n    labels: {\n      style: { color: t.inkSoft, fontSize: \"14px\" },\n      formatter() {\n        return this.value >= 0 ? this.value.toFixed(4) : \"\";\n      },\n    },\n    tickInterval: yTickInterval,\n    min: -tickHeight * 1.4,\n    max: maxDensity * 1.15,\n    plotLines: [{ value: 0, color: t.inkSoft, width: 1, zIndex: 3 }],\n  },\n  legend: { enabled: false },\n  tooltip: { enabled: false },\n  plotOptions: {\n    series: { animation: false, enableMouseTracking: false },\n  },\n  series: [\n    {\n      type: \"area\",\n      name: \"Estimated density\",\n      data: densityCurve,\n      color: t.palette[0],\n      lineWidth: 2.5,\n      fillOpacity: 0.25,\n      threshold: 0,\n      marker: { enabled: false },\n    },\n    {\n      type: \"column\",\n      name: \"Observations\",\n      data: reactionTimes.map((v) => [v, -tickHeight]),\n      color: rugColor,\n      borderWidth: 0,\n      pointWidth: 2,\n      threshold: 0,\n      pointPadding: 0,\n      groupPadding: 0,\n    },\n  ],\n});\n"}