{"spec_id":"histogram-basic","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// histogram-basic: Basic Histogram\n// Library: chartjs 4.4.7 | JavaScript 22.23.2\n// Quality: 91/100 | Created: 2026-08-26\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ---------------------------------------\nfunction makeLcg(seed) {\n  let state = seed >>> 0;\n  return function lcg() {\n    state = (1664525 * state + 1013904223) >>> 0;\n    return state / 4294967296;\n  };\n}\n\nfunction normal(rand, mean, stdDev) {\n  let sum = 0;\n  for (let i = 0; i < 12; i++) sum += rand();\n  return mean + (sum - 6) * stdDev;\n}\n\nconst rand = makeLcg(42);\nconst examScores = [];\nfor (let i = 0; i < 400; i++) {\n  // Most students cluster near a strong score; a clearly separated,\n  // sizeable group struggles, producing a legible second mode near 40.\n  const score = rand() < 0.8 ? normal(rand, 78, 8) : normal(rand, 40, 10);\n  examScores.push(Math.max(0, Math.min(100, score)));\n}\n\n// --- Binning -----------------------------------------------------------------\nconst binCount = 20;\nconst minScore = Math.min(...examScores);\nconst maxScore = Math.max(...examScores);\nconst binWidth = (maxScore - minScore) / binCount;\nconst counts = new Array(binCount).fill(0);\nexamScores.forEach((score) => {\n  const idx = Math.min(binCount - 1, Math.floor((score - minScore) / binWidth));\n  counts[idx]++;\n});\nconst labels = counts.map((_, i) => Math.round(minScore + i * binWidth));\n\n// The tallest bin gets a two-tone accent to call out the most common range;\n// the mean-score bin gets a dashed reference line (custom plugin below).\nconst modalIndex = counts.indexOf(Math.max(...counts));\nconst meanScore = examScores.reduce((sum, v) => sum + v, 0) / examScores.length;\nconst meanBinIndex = Math.min(\n  binCount - 1,\n  Math.floor((meanScore - minScore) / binWidth)\n);\nconst barColors = counts.map((_, i) => (i === modalIndex ? t.palette[2] : t.palette[0]));\n\n// --- Mount -----------------------------------------------------------------\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// --- Custom plugin: dashed mean-score reference line ------------------------\nconst meanLinePlugin = {\n  id: \"meanLine\",\n  afterDatasetsDraw(chart) {\n    const bar = chart.getDatasetMeta(0).data[meanBinIndex];\n    if (!bar) return;\n    const { ctx, chartArea } = chart;\n    const x = Math.min(Math.max(bar.x, chartArea.left + 45), chartArea.right - 45);\n    ctx.save();\n    ctx.strokeStyle = t.ink;\n    ctx.lineWidth = 2;\n    ctx.setLineDash([6, 4]);\n    ctx.beginPath();\n    ctx.moveTo(x, chartArea.top);\n    ctx.lineTo(x, chartArea.bottom);\n    ctx.stroke();\n    ctx.setLineDash([]);\n    ctx.fillStyle = t.ink;\n    ctx.font = \"bold 14px sans-serif\";\n    ctx.textAlign = \"center\";\n    ctx.textBaseline = \"bottom\";\n    ctx.fillText(`Mean: ${meanScore.toFixed(1)}`, x, chartArea.top - 8);\n    ctx.restore();\n  },\n};\n\n// --- Chart -----------------------------------------------------------------\nnew Chart(canvas, {\n  type: \"bar\",\n  data: {\n    labels,\n    datasets: [\n      {\n        label: \"Frequency\",\n        data: counts,\n        backgroundColor: barColors,\n        borderColor: t.pageBg,\n        borderWidth: 1,\n        categoryPercentage: 1.0,\n        barPercentage: 1.0,\n      },\n    ],\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    layout: {\n      padding: { top: 28 },\n    },\n    plugins: {\n      title: {\n        display: true,\n        text: \"histogram-basic · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 22 },\n      },\n      legend: { display: false },\n    },\n    scales: {\n      x: {\n        ticks: {\n          color: t.inkSoft,\n          font: { size: 14 },\n          autoSkip: true,\n          maxTicksLimit: 10,\n        },\n        grid: { display: false },\n        title: {\n          display: true,\n          text: \"Exam Score\",\n          color: t.ink,\n          font: { size: 16 },\n        },\n      },\n      y: {\n        beginAtZero: true,\n        ticks: { color: t.inkSoft, font: { size: 14 } },\n        grid: { color: t.grid },\n        title: {\n          display: true,\n          text: \"Frequency (Students)\",\n          color: t.ink,\n          font: { size: 16 },\n        },\n      },\n    },\n  },\n  plugins: [meanLinePlugin],\n});\n"}