{"spec_id":"streamgraph-basic","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// streamgraph-basic: Basic Stream Graph\n// Library: chartjs 4.4.7 | JavaScript 22.23.1\n// Quality: 93/100 | Created: 2026-08-05\n\n//# anyplot-orientation: landscape\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ---------------------------------------\nconst months = [\n  \"Jan 24\", \"Feb 24\", \"Mar 24\", \"Apr 24\", \"May 24\", \"Jun 24\",\n  \"Jul 24\", \"Aug 24\", \"Sep 24\", \"Oct 24\", \"Nov 24\", \"Dec 24\",\n  \"Jan 25\", \"Feb 25\", \"Mar 25\", \"Apr 25\", \"May 25\", \"Jun 25\",\n  \"Jul 25\", \"Aug 25\", \"Sep 25\", \"Oct 25\", \"Nov 25\", \"Dec 25\",\n];\nconst genres = [\"Pop\", \"Hip-Hop\", \"Electronic\", \"Rock\", \"Jazz\"];\n\n// Smooth seasonal listening-hour trends (thousands of hours) per genre\nconst baseHours = [42, 35, 28, 24, 14];\nconst amplitude = [10, 14, 9, 6, 4];\nconst periodMonths = [12, 9, 6, 14, 18];\nconst phaseShift = [0, 2, 5, 8, 3];\nconst series = genres.map((_, i) =>\n  months.map((_, m) =>\n    Math.max(\n      2,\n      Math.round(\n        baseHours[i] +\n          amplitude[i] * Math.sin((2 * Math.PI * (m + phaseShift[i])) / periodMonths[i])\n      )\n    )\n  )\n);\n\n// Centered (symmetric) baseline so the stack reads as a river, not a bar stack\nconst baseline = months.map((_, m) => {\n  const total = series.reduce((sum, s) => sum + s[m], 0);\n  return -total / 2;\n});\n\nlet cumulative = baseline.slice();\nconst layerTops = series.map((s) => {\n  cumulative = cumulative.map((c, m) => c + s[m]);\n  return cumulative.slice();\n});\n\n// Storytelling focal point: the genre with the largest combined listening\n// hours gets a defined ink outline so the flow has a clear point of entry.\nconst totals = series.map((s) => s.reduce((sum, v) => sum + v, 0));\nconst heroIndex = totals.indexOf(Math.max(...totals));\n\n// Lavender (Hip-Hop) and ochre (Rock) fall below WCAG 3:1 contrast on the\n// cream background — a thin ink stroke keeps their band edges legible.\nconst lowContrast = new Set([\"Hip-Hop\", \"Rock\"]);\n\n// --- Mount -------------------------------------------------------------------\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// --- Chart ---------------------------------------------------------------\nconst datasets = [\n  {\n    label: \"\",\n    data: baseline,\n    borderWidth: 0,\n    pointRadius: 0,\n    fill: false,\n    tension: 0.4,\n    cubicInterpolationMode: \"monotone\",\n  },\n  ...genres.map((genre, i) => ({\n    label: genre,\n    data: layerTops[i],\n    borderColor: i === heroIndex || lowContrast.has(genre) ? t.ink : t.palette[i % t.palette.length],\n    backgroundColor: t.palette[i % t.palette.length],\n    borderWidth: i === heroIndex ? 2 : lowContrast.has(genre) ? 1 : 0,\n    pointRadius: 0,\n    fill: i,\n    tension: 0.4,\n    cubicInterpolationMode: \"monotone\",\n  })),\n];\n\nnew Chart(canvas, {\n  type: \"line\",\n  data: { labels: months, datasets },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    interaction: { mode: \"nearest\", intersect: false },\n    plugins: {\n      title: {\n        display: true,\n        text: \"streamgraph-basic · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 25 },\n      },\n      subtitle: {\n        display: true,\n        text: `${genres[heroIndex]} led combined listening hours across all 24 months`,\n        color: t.inkSoft,\n        font: { size: 15, weight: \"normal\" },\n        padding: { bottom: 12 },\n      },\n      legend: {\n        labels: {\n          color: t.ink,\n          font: { size: 17 },\n          filter: (item) => item.text !== \"\",\n        },\n      },\n    },\n    scales: {\n      x: {\n        type: \"category\",\n        ticks: {\n          color: t.inkSoft,\n          font: { size: 15 },\n          maxRotation: 0,\n          autoSkip: true,\n          maxTicksLimit: 12,\n        },\n        grid: { display: false },\n        title: { display: true, text: \"Month\", color: t.ink, font: { size: 17 } },\n      },\n      y: {\n        display: false,\n      },\n    },\n  },\n});\n"}