{"spec_id":"scatter-text","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// scatter-text: Scatter Plot with Text Labels Instead of Points\n// Library: chartjs 4.4.7 | JavaScript 22.23.2\n// Quality: 89/100 | Created: 2026-09-02\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic t-SNE-style embedding) ------------------\n// Fixed-seed LCG — the browser has no seeded Math.random().\nlet seed = 42;\nfunction rand() {\n  seed = (seed * 1664525 + 1013904223) % 4294967296;\n  return seed / 4294967296;\n}\nfunction jitter(cx, cy, spread) {\n  return { x: cx + (rand() - 0.5) * spread, y: cy + (rand() - 0.5) * spread };\n}\n\n// Cluster centers/spreads chosen to spread coverage across all four quadrants\n// of the wide 16:9 canvas (avoids the empty bottom-right seen at attempt 1) and\n// to open up the previously crowded Scripting & Web cluster.\nconst clusters = [\n  {\n    domain: \"Systems & Low-Level\",\n    center: [-7, 3],\n    spread: 6,\n    words: [\"C\", \"C++\", \"Rust\", \"Go\", \"Zig\", \"Ada\", \"Fortran\", \"Assembly\", \"D\", \"Nim\"],\n  },\n  {\n    domain: \"Scripting & Web\",\n    center: [6, 1],\n    spread: 7,\n    words: [\"JavaScript\", \"TypeScript\", \"Python\", \"Ruby\", \"PHP\", \"Perl\", \"Lua\", \"Dart\", \"Elixir\", \"Swift\"],\n  },\n  {\n    domain: \"Functional & Data\",\n    center: [1, -7],\n    spread: 6,\n    words: [\"Haskell\", \"Scala\", \"Clojure\", \"F#\", \"Erlang\", \"OCaml\", \"Julia\", \"Elm\", \"Racket\", \"R\"],\n  },\n];\n\nconst datasets = clusters.map((cluster, i) => ({\n  label: cluster.domain,\n  data: cluster.words.map((word) => {\n    const p = jitter(cluster.center[0], cluster.center[1], cluster.spread);\n    return { x: p.x, y: p.y, label: word };\n  }),\n  backgroundColor: t.palette[i],\n  borderColor: t.palette[i],\n  pointRadius: 0,\n  pointHoverRadius: 0,\n  pointHitRadius: 14,\n}));\n\n// --- Plugin: draw each point as its text label instead of a marker ---------\nconst textLabelPlugin = {\n  id: \"textLabels\",\n  afterDatasetsDraw(chart) {\n    const { ctx } = chart;\n    chart.data.datasets.forEach((dataset, datasetIndex) => {\n      const meta = chart.getDatasetMeta(datasetIndex);\n      meta.data.forEach((point, index) => {\n        const { x, y } = point.getProps([\"x\", \"y\"], true);\n        const label = dataset.data[index].label;\n        ctx.save();\n        ctx.font = \"600 15px sans-serif\";\n        ctx.textAlign = \"center\";\n        ctx.textBaseline = \"middle\";\n        // Halo in the page background keeps text legible where labels crowd or cross gridlines.\n        ctx.lineWidth = 3;\n        ctx.strokeStyle = t.pageBg;\n        ctx.strokeText(label, x, y);\n        ctx.fillStyle = dataset.backgroundColor;\n        ctx.fillText(label, x, y);\n        ctx.restore();\n      });\n    });\n  },\n};\n\n// --- Mount -------------------------------------------------------------------\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// --- Chart ---------------------------------------------------------------------\nnew Chart(canvas, {\n  type: \"scatter\",\n  data: { datasets },\n  plugins: [textLabelPlugin],\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    layout: { padding: 24 },\n    plugins: {\n      title: {\n        display: true,\n        text: \"scatter-text · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 32, weight: \"600\" },\n      },\n      legend: {\n        position: \"top\",\n        labels: { color: t.ink, font: { size: 16 }, boxWidth: 16, boxHeight: 16 },\n      },\n      tooltip: {\n        callbacks: {\n          label: (context) => context.raw.label,\n        },\n      },\n    },\n    scales: {\n      // Numeric t-SNE coordinates carry no literal meaning, so ticks stay hidden;\n      // going fully borderless (no grid, no axis line) avoids the four-sided box\n      // a bare grid would draw and keeps focus on the text-label clusters.\n      x: {\n        title: { display: true, text: \"Semantic Dimension 1 (t-SNE)\", color: t.ink, font: { size: 16 } },\n        ticks: { display: false },\n        grid: { display: false },\n        border: { display: false },\n      },\n      y: {\n        title: { display: true, text: \"Semantic Dimension 2 (t-SNE)\", color: t.ink, font: { size: 16 } },\n        ticks: { display: false },\n        grid: { display: false },\n        border: { display: false },\n      },\n    },\n  },\n});\n"}