{"spec_id":"histogram-overlapping","library":"muix","language":"javascript","code":"// anyplot.ai\n// histogram-overlapping: Overlapping Histograms\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 93/100 | Created: 2026-08-18\nimport { BarChart } from \"@mui/x-charts/BarChart\";\nimport { ChartsReferenceLine } from \"@mui/x-charts/ChartsReferenceLine\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst TITLE = \"histogram-overlapping · javascript · muix · anyplot.ai\";\nconst TITLE_HEIGHT = 56;\n\n// --- Data (in-memory, deterministic): exam scores, control vs treatment -----\n// Deterministic LCG so the sampled distributions are stable across renders —\n// the browser has no seeded Math.random().\nfunction makeLcg(seed) {\n  let state = seed >>> 0;\n  return function next() {\n    state = (state * 1664525 + 1013904223) >>> 0;\n    return state / 4294967296;\n  };\n}\nconst rand = makeLcg(42);\nfunction gaussianSample() {\n  const u1 = Math.max(rand(), 1e-9);\n  const u2 = rand();\n  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n}\n\nconst SAMPLE_COUNT = 240;\nconst controlScores = Array.from({ length: SAMPLE_COUNT }, () =>\n  Math.min(100, Math.max(0, 68 + 11 * gaussianSample())),\n);\nconst treatmentScores = Array.from({ length: SAMPLE_COUNT }, () =>\n  Math.min(100, Math.max(0, 76 + 9 * gaussianSample())),\n);\n\n// Shared bin edges across both groups so the comparison is apples-to-apples.\nconst BIN_WIDTH = 5;\nconst allScores = controlScores.concat(treatmentScores);\nconst binStart = Math.floor(Math.min(...allScores) / BIN_WIDTH) * BIN_WIDTH;\nconst binEnd = Math.ceil(Math.max(...allScores) / BIN_WIDTH) * BIN_WIDTH;\nconst binCount = (binEnd - binStart) / BIN_WIDTH;\nconst binLabels = Array.from(\n  { length: binCount },\n  (_, i) => `${binStart + i * BIN_WIDTH}–${binStart + (i + 1) * BIN_WIDTH}`,\n);\n\nfunction countPerBin(samples) {\n  const counts = new Array(binCount).fill(0);\n  samples.forEach((value) => {\n    const idx = Math.min(binCount - 1, Math.floor((value - binStart) / BIN_WIDTH));\n    counts[idx] += 1;\n  });\n  return counts;\n}\nconst controlCounts = countPerBin(controlScores);\nconst treatmentCounts = countPerBin(treatmentScores);\n\n// Bake 55% opacity into the fill so overlapping regions stay legible.\nfunction withAlpha(hex, alpha) {\n  return hex + Math.round(alpha * 255).toString(16).padStart(2, \"0\");\n}\n\n// Storytelling touch: a reference line at each group's mean, snapped to its\n// containing bin label (the x axis is a band scale, so ChartsReferenceLine's\n// `x` must match one of `binLabels` exactly rather than a raw numeric mean).\nfunction meanBinLabel(samples) {\n  const mean = samples.reduce((sum, v) => sum + v, 0) / samples.length;\n  const idx = Math.min(binCount - 1, Math.max(0, Math.floor((mean - binStart) / BIN_WIDTH)));\n  return binLabels[idx];\n}\nconst controlMeanBin = meanBinLabel(controlScores);\nconst treatmentMeanBin = meanBinLabel(treatmentScores);\n\n// --- Chart (default-exported component — the harness mounts it) -------------\nexport default function Chart() {\n  const chartHeight = window.ANYPLOT_SIZE.height - TITLE_HEIGHT;\n\n  return (\n    <div\n      style={{\n        width: window.ANYPLOT_SIZE.width,\n        height: window.ANYPLOT_SIZE.height,\n      }}\n    >\n      <div\n        style={{\n          height: TITLE_HEIGHT,\n          lineHeight: `${TITLE_HEIGHT}px`,\n          paddingLeft: 24,\n          fontSize: 22,\n          fontWeight: 600,\n          letterSpacing: -0.2,\n          color: t.ink,\n        }}\n      >\n        {TITLE}\n      </div>\n      <BarChart\n        width={window.ANYPLOT_SIZE.width}\n        height={chartHeight}\n        skipAnimation\n        borderRadius={3}\n        series={[\n          {\n            data: controlCounts,\n            label: \"Control group\",\n            color: withAlpha(t.palette[0], 0.55),\n            valueFormatter: (v) => `${v} students`,\n            highlightScope: { highlight: \"series\", fade: \"global\" },\n          },\n          {\n            data: treatmentCounts,\n            label: \"Treatment group\",\n            color: withAlpha(t.palette[1], 0.55),\n            valueFormatter: (v) => `${v} students`,\n            highlightScope: { highlight: \"series\", fade: \"global\" },\n          },\n        ]}\n        xAxis={[\n          {\n            scaleType: \"band\",\n            data: binLabels,\n            label: \"Exam Score (0–100)\",\n            labelStyle: { fontSize: 16, fontWeight: 500 },\n            tickLabelStyle: { fontSize: 13 },\n            categoryGapRatio: 0.05,\n            // Overlap the two series' bars instead of placing them side by\n            // side: barGapRatio=-1 solves barWidth=bandWidth, offset=0 for\n            // every series (see getBandSize in @mui/x-charts/BarChart/BarPlot),\n            // so both groups' bars share the same position and width.\n            barGapRatio: -1,\n          },\n        ]}\n        yAxis={[\n          {\n            label: \"Count\",\n            labelStyle: { fontSize: 16, fontWeight: 500 },\n            tickLabelStyle: { fontSize: 14 },\n          },\n        ]}\n        grid={{ horizontal: true }}\n        margin={{ left: 80, right: 24, top: 24, bottom: 70 }}\n        slotProps={{\n          legend: {\n            direction: \"row\",\n            position: { vertical: \"top\", horizontal: \"right\" },\n            labelStyle: { fontSize: 14 },\n            itemMarkWidth: 14,\n            itemMarkHeight: 14,\n            markGap: 6,\n            itemGap: 18,\n            padding: 6,\n          },\n        }}\n      >\n        {/* Storytelling touch: a light dashed reference line at each group's\n            mean, in the theme-adaptive ink color so it reads as structure\n            rather than a third data series. */}\n        <ChartsReferenceLine\n          x={controlMeanBin}\n          label=\"Control mean\"\n          labelAlign=\"start\"\n          lineStyle={{ stroke: withAlpha(t.ink, 0.35), strokeDasharray: \"4 4\" }}\n          labelStyle={{ fontSize: 11, fill: t.inkSoft }}\n        />\n        <ChartsReferenceLine\n          x={treatmentMeanBin}\n          label=\"Treatment mean\"\n          labelAlign=\"end\"\n          lineStyle={{ stroke: withAlpha(t.ink, 0.35), strokeDasharray: \"4 4\" }}\n          labelStyle={{ fontSize: 11, fill: t.inkSoft }}\n        />\n      </BarChart>\n    </div>\n  );\n}\n"}