{"spec_id":"line-multi","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// line-multi: Multi-Line Comparison Plot\n// Library: chartjs 4.4.7 | JavaScript 22.23.1\n// Quality: 92/100 | Created: 2026-08-05\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Daily active users for 4 product features over a 30-day month.\nconst days = Array.from({ length: 30 }, (_, i) => i + 1);\n\nlet seed = 7;\nfunction lcg() {\n  seed = (seed * 1103515245 + 12345) % 2147483648;\n  return seed / 2147483648;\n}\n\nfunction trendSeries(base, drift, wiggle) {\n  let value = base;\n  return days.map((_, i) => {\n    value += drift + (lcg() - 0.5) * wiggle;\n    return Math.max(0, Math.round(value + Math.sin(i / 4) * wiggle * 1.5));\n  });\n}\n\nconst search = trendSeries(1200, 18, 60);\nconst dashboard = trendSeries(900, 10, 50);\nconst mobileApp = trendSeries(600, 22, 40);\nconst apiCalls = trendSeries(1500, -6, 70);\n\n// Find where the primary series (Search) and its closest rival (API Calls)\n// swap order, so the crossover can be called out with a marker.\nfunction crossoverIndex(a, b) {\n  for (let i = 1; i < a.length; i++) {\n    if ((a[i - 1] - b[i - 1]) * (a[i] - b[i]) < 0) return i;\n  }\n  return -1;\n}\nconst crossIdx = crossoverIndex(search, apiCalls);\n\nfunction markerRadii(length, highlightIdx) {\n  return Array.from({ length }, (_, i) => (i === highlightIdx ? 7 : 2.5));\n}\n\n// Search is the primary series (thickest, solid); the rest use distinct\n// dash patterns so the four trends stay distinguishable without color alone.\nconst series = [\n  { label: \"Search\", data: search, borderWidth: 4, borderDash: [], pointRadius: markerRadii(days.length, crossIdx) },\n  { label: \"Dashboard\", data: dashboard, borderWidth: 2.5, borderDash: [10, 5], pointRadius: 2.5 },\n  { label: \"Mobile App\", data: mobileApp, borderWidth: 2.5, borderDash: [2, 4], pointRadius: 2.5 },\n  { label: \"API Calls\", data: apiCalls, borderWidth: 3, borderDash: [10, 4, 2, 4], pointRadius: markerRadii(days.length, crossIdx) },\n];\n\n// --- Mount -------------------------------------------------------------------\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// --- Chart ---------------------------------------------------------------\nnew Chart(canvas, {\n  type: \"line\",\n  data: {\n    labels: days,\n    datasets: series.map((s, i) => ({\n      label: s.label,\n      data: s.data,\n      borderColor: t.palette[i % t.palette.length],\n      backgroundColor: t.palette[i % t.palette.length],\n      borderWidth: s.borderWidth,\n      borderDash: s.borderDash,\n      pointRadius: s.pointRadius,\n      pointHoverRadius: 5,\n      tension: 0.3,\n      fill: false,\n    })),\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    plugins: {\n      title: {\n        display: true,\n        text: \"line-multi · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 22 },\n        padding: { bottom: 20 },\n      },\n      legend: {\n        position: \"top\",\n        align: \"end\",\n        labels: { color: t.ink, font: { size: 16 }, boxWidth: 24, boxHeight: 3, usePointStyle: false },\n      },\n    },\n    scales: {\n      x: {\n        ticks: { color: t.inkSoft, font: { size: 14 }, maxTicksLimit: 10 },\n        grid: { display: false },\n        title: { display: true, text: \"Day of Month\", color: t.ink, font: { size: 16 } },\n      },\n      y: {\n        ticks: { color: t.inkSoft, font: { size: 14 } },\n        grid: { color: t.grid },\n        title: { display: true, text: \"Daily Active Users (count)\", color: t.ink, font: { size: 16 } },\n        beginAtZero: true,\n      },\n    },\n  },\n});\n"}