{"spec_id":"histogram-basic","library":"muix","language":"javascript","code":"// anyplot.ai\n// histogram-basic: Basic Histogram\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 89/100 | Created: 2026-08-26\nimport { BarChart } from \"@mui/x-charts/BarChart\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst TITLE = \"histogram-basic · javascript · muix · anyplot.ai\";\nconst TITLE_HEIGHT = 56;\n\n// --- Data (in-memory, deterministic): subscriber ages for a streaming service\n// Deterministic LCG so the sampled distribution is 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\n// Log-normal draw: a young-skewed subscriber base with a long tail of older\n// loyal customers, rather than a perfectly symmetric bell curve.\nconst SAMPLE_COUNT = 600;\nconst LOG_MU = Math.log(29);\nconst LOG_SIGMA = 0.34;\nconst ages = Array.from({ length: SAMPLE_COUNT }, () => {\n  const raw = Math.exp(LOG_MU + LOG_SIGMA * gaussianSample());\n  return Math.round(Math.min(85, Math.max(16, raw)));\n});\n\n// Bin into 5-year age brackets covering the observed range.\nconst BIN_WIDTH = 5;\nconst minAge = Math.min(...ages);\nconst maxAge = Math.max(...ages);\nconst binStart = Math.floor(minAge / BIN_WIDTH) * BIN_WIDTH;\nconst binEnd = Math.ceil((maxAge + 1) / 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);\nconst counts = new Array(binCount).fill(0);\nages.forEach((age) => {\n  const idx = Math.min(binCount - 1, Math.floor((age - binStart) / BIN_WIDTH));\n  counts[idx] += 1;\n});\n\n// Call out the modal bin so the shape of the distribution reads at a glance,\n// not just from bar heights alone.\nconst peakIndex = counts.indexOf(Math.max(...counts));\nconst PEAK_LABEL_COLOR = \"#1A1A17\"; // fixed (not theme-adaptive): stays legible on the brand-green bar in both themes\n\n// --- Chart (default-exported component — the harness mounts it) -------------\nexport default function Chart() {\n  const { width, height } = window.ANYPLOT_SIZE;\n  const chartHeight = height - TITLE_HEIGHT;\n\n  return (\n    <div style={{ width, height }}>\n      <div\n        style={{\n          height: TITLE_HEIGHT,\n          lineHeight: `${TITLE_HEIGHT}px`,\n          textAlign: \"center\",\n          fontSize: 27,\n          fontWeight: 500,\n          color: t.ink,\n        }}\n      >\n        {TITLE}\n      </div>\n      <BarChart\n        width={width}\n        height={chartHeight}\n        skipAnimation\n        barLabel={(item) => (item.dataIndex === peakIndex ? \"Peak\" : undefined)}\n        series={[\n          {\n            data: counts,\n            label: \"Subscribers\",\n            color: t.palette[0],\n            valueFormatter: (v) => `${v} subscribers`,\n          },\n        ]}\n        xAxis={[\n          {\n            scaleType: \"band\",\n            data: binLabels,\n            label: \"Subscriber Age (years)\",\n            labelStyle: { fontSize: 16, fontWeight: 500 },\n            tickLabelStyle: { fontSize: 13 },\n            // No gap between bins — a histogram's bars are contiguous by\n            // convention; the stroke below (sx) draws the bin-edge lines.\n            categoryGapRatio: 0,\n          },\n        ]}\n        yAxis={[\n          {\n            min: 0,\n            label: \"Number of Subscribers\",\n            labelStyle: { fontSize: 16, fontWeight: 500 },\n            tickLabelStyle: { fontSize: 14 },\n          },\n        ]}\n        grid={{ horizontal: true }}\n        margin={{ left: 92, right: 32, top: 24, bottom: 76 }}\n        slotProps={{ legend: { hidden: true } }}\n        sx={{\n          \"& .MuiBarElement-root\": { stroke: t.pageBg, strokeWidth: 1.5 },\n          \"& .MuiBarLabel-root\": {\n            fill: PEAK_LABEL_COLOR,\n            fontWeight: 700,\n            fontSize: \"15px\",\n          },\n          \"& .MuiChartsAxis-tickLabel\": { fontSize: \"13px\" },\n        }}\n      />\n    </div>\n  );\n}\n"}