Categorical Scatter Plot — Highcharts

A scatter plot where points are colored according to a categorical variable. Each category has a distinct color, allowing visual comparison of patterns across groups. A legend maps colors to category names.

Categorical Scatter Plot rendered with Highcharts

Renders

JavaScript source (Highcharts)

// anyplot.ai
// scatter-categorical: Categorical Scatter Plot
// Library: highcharts 12.6.0 | JavaScript 22.23.2
// Quality: 92/100 | Created: 2026-09-05

const t = window.ANYPLOT_TOKENS;

// --- Data (in-memory, deterministic LCG) -----------------------------------
function makeLcg(seed) {
  let state = seed;
  return function lcg() {
    state = (state * 1664525 + 1013904223) % 4294967296;
    return state / 4294967296;
  };
}

function gaussian(rng) {
  const u1 = Math.max(rng(), 1e-9);
  const u2 = rng();
  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
}

// Ordinary least-squares fit, used to draw a per-tier trend overlay.
function linearFit(points) {
  const n = points.length;
  const sumX = points.reduce((acc, [x]) => acc + x, 0);
  const sumY = points.reduce((acc, [, y]) => acc + y, 0);
  const sumXY = points.reduce((acc, [x, y]) => acc + x * y, 0);
  const sumXX = points.reduce((acc, [x]) => acc + x * x, 0);
  const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
  const intercept = (sumY - slope * sumX) / n;
  return { slope, intercept };
}

const rng = makeLcg(42);

const tiers = [
  { name: "Basic", engagement: 30, spend: 25, spread: 8, symbol: "circle" },
  { name: "Pro", engagement: 55, spend: 65, spread: 9, symbol: "diamond" },
  { name: "Enterprise", engagement: 78, spend: 140, spread: 10, symbol: "triangle" },
];

const scatterSeries = tiers.map((tier, i) => {
  const data = [];
  for (let j = 0; j < 60; j += 1) {
    const engagement = tier.engagement + gaussian(rng) * 9;
    const spend = tier.spend + engagement * 0.6 + gaussian(rng) * tier.spread;
    data.push([Math.round(engagement * 10) / 10, Math.round(spend * 10) / 10]);
  }
  return {
    type: "scatter",
    name: tier.name,
    data,
    color: t.palette[i],
    marker: { symbol: tier.symbol, radius: 5, lineWidth: 1, lineColor: t.pageBg },
  };
});

// Trend overlay: one thin dashed regression line per tier, sharing its
// series color — a genuine per-group fit, not a decorative flourish.
const trendSeries = scatterSeries.map((s) => {
  const xs = s.data.map((p) => p[0]);
  const { slope, intercept } = linearFit(s.data);
  const xMin = Math.min(...xs);
  const xMax = Math.max(...xs);
  return {
    type: "line",
    name: `${s.name} trend`,
    data: [
      [xMin, slope * xMin + intercept],
      [xMax, slope * xMax + intercept],
    ],
    color: s.color,
    lineWidth: 2,
    dashStyle: "ShortDash",
    marker: { enabled: false },
    enableMouseTracking: false,
    showInLegend: false,
  };
});

const series = [...scatterSeries, ...trendSeries];

// --- Chart -----------------------------------------------------------------
Highcharts.chart("container", {
  chart: {
    type: "scatter",
    backgroundColor: "transparent",
    animation: false,
    style: { fontFamily: "inherit" },
  },
  credits: { enabled: false },
  colors: t.palette,
  title: {
    text: "scatter-categorical · javascript · highcharts · anyplot.ai",
    style: { color: t.ink, fontSize: "22px", fontWeight: "700" },
  },
  xAxis: {
    title: { text: "Engagement Score", style: { color: t.inkSoft, fontSize: "16px" } },
    lineColor: t.inkSoft,
    tickLength: 0,
    gridLineWidth: 0,
    labels: { style: { color: t.inkSoft, fontSize: "14px" } },
  },
  yAxis: {
    title: { text: "Monthly Spend ($)", style: { color: t.inkSoft, fontSize: "16px" } },
    lineColor: t.inkSoft,
    tickLength: 0,
    gridLineWidth: 1,
    gridLineColor: t.grid,
    labels: { style: { color: t.inkSoft, fontSize: "14px" } },
  },
  legend: {
    title: {
      text: "Subscription Tier",
      style: { color: t.ink, fontSize: "14px", fontWeight: "400" },
    },
    itemStyle: { color: t.inkSoft, fontSize: "14px" },
    itemHoverStyle: { color: t.ink },
  },
  plotOptions: {
    series: { animation: false },
    scatter: { opacity: 0.65, states: { hover: { enabled: true } } },
  },
  series,
});

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/scatter-categorical/highcharts/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.

{
  "spec_id": "scatter-categorical",
  "language": "javascript",
  "library": "highcharts",
  "page": "https://anyplot.ai/scatter-categorical/javascript/highcharts",
  "hub": "https://anyplot.ai/scatter-categorical",
  "code_json": "https://api.anyplot.ai/specs/scatter-categorical/highcharts/code",
  "spec_json": "https://api.anyplot.ai/specs/scatter-categorical",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/scatter-categorical/javascript/highcharts/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/scatter-categorical/javascript/highcharts/plot-dark.png",
  "interactive_light_html": "https://storage.googleapis.com/anyplot-images/plots/scatter-categorical/javascript/highcharts/plot-light.html",
  "interactive_dark_html": "https://storage.googleapis.com/anyplot-images/plots/scatter-categorical/javascript/highcharts/plot-dark.html",
  "quality_score": 92.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Categorical Scatter Plot on anyplot.ai.

Other implementations