Basic Parallel Coordinates Plot — Chart.js

A parallel coordinates plot visualizes multivariate data by representing each variable as a vertical axis and each observation as a line connecting values across all axes. This technique is powerful for identifying patterns, clusters, and outliers in high-dimensional datasets where traditional 2D plots fall short. It enables simultaneous comparison of multiple variables for each data point.

Basic Parallel Coordinates Plot rendered with Chart.js

Renders

JavaScript source (Chart.js)

// anyplot.ai
// parallel-basic: Basic Parallel Coordinates Plot
// Library: chartjs 4.4.7 | JavaScript 22.23.1
// Quality: 90/100 | Created: 2026-07-24

const t = window.ANYPLOT_TOKENS;

// --- Data (in-memory, deterministic, iris-inspired) ------------------------
// Tiny fixed-seed LCG + Box-Muller so the sample is reproducible without
// Math.random (the browser has no seeded RNG).
let lcgSeed = 42;
function lcgRand() {
  lcgSeed = (lcgSeed * 1103515245 + 12345) & 0x7fffffff;
  return lcgSeed / 0x7fffffff;
}
function randNormal() {
  const u1 = Math.max(lcgRand(), 1e-9);
  const u2 = lcgRand();
  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
}

const dimensions = ["Sepal Length", "Sepal Width", "Petal Length", "Petal Width"];

// Approximate per-species mean/sd for each dimension (cm), iris-inspired.
const speciesStats = [
  { name: "Setosa", stats: [[5.0, 0.35], [3.42, 0.38], [1.46, 0.17], [0.24, 0.11]] },
  { name: "Versicolor", stats: [[5.94, 0.52], [2.77, 0.31], [4.26, 0.47], [1.33, 0.2]] },
  { name: "Virginica", stats: [[6.59, 0.64], [2.97, 0.32], [5.55, 0.55], [2.03, 0.27]] },
];
const OBS_PER_SPECIES = 20;

const observations = [];
speciesStats.forEach((species, speciesIndex) => {
  for (let i = 0; i < OBS_PER_SPECIES; i++) {
    const raw = species.stats.map(([mean, sd]) => mean + sd * randNormal());
    observations.push({ speciesIndex, raw });
  }
});

// Min-max normalize each dimension independently so all axes share one 0-1
// scale and can be compared side by side, per the spec's normalization note.
const mins = dimensions.map((_, d) => Math.min(...observations.map((o) => o.raw[d])));
const maxs = dimensions.map((_, d) => Math.max(...observations.map((o) => o.raw[d])));
observations.forEach((o) => {
  o.normalized = o.raw.map((v, d) => (v - mins[d]) / (maxs[d] - mins[d]));
});

function hexToRgba(hex, alpha) {
  const r = parseInt(hex.slice(1, 3), 16);
  const g = parseInt(hex.slice(3, 5), 16);
  const b = parseInt(hex.slice(5, 7), 16);
  return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}

const speciesColors = speciesStats.map((_, i) => t.palette[i % t.palette.length]);

// Per-species mean profile (one point per dimension), drawn as a bolder line
// on top of the faint individual traces so each species has a clear focal
// line to anchor the eye, especially where Versicolor/Virginica overlap.
const meanProfiles = speciesStats.map((_, speciesIndex) => {
  const speciesObs = observations.filter((o) => o.speciesIndex === speciesIndex);
  return dimensions.map(
    (_, d) => speciesObs.reduce((sum, o) => sum + o.normalized[d], 0) / speciesObs.length
  );
});

// --- Mount -------------------------------------------------------------
const canvas = document.createElement("canvas");
document.getElementById("container").appendChild(canvas);

// --- Chart ---------------------------------------------------------------
// One line dataset per observation (Chart.js has no native parallel-coords
// type); category x-axis ticks stand in for the per-dimension axes, and a
// shared normalized y-axis keeps every dimension comparable. A bold mean
// line per species is appended last so it draws on top of the faint
// individual traces (Chart.js draws line datasets in array order).
const individualDatasets = observations.map((o) => ({
  data: o.normalized,
  borderColor: hexToRgba(speciesColors[o.speciesIndex], 0.35),
  borderWidth: 1.25,
  pointRadius: 0,
  pointHoverRadius: 0,
  tension: 0,
  fill: false,
}));
const meanDatasets = meanProfiles.map((profile, speciesIndex) => ({
  data: profile,
  borderColor: speciesColors[speciesIndex],
  borderWidth: 4,
  pointRadius: 0,
  pointHoverRadius: 0,
  tension: 0,
  fill: false,
}));

new Chart(canvas, {
  type: "line",
  data: {
    labels: dimensions,
    datasets: [...individualDatasets, ...meanDatasets],
  },
  options: {
    responsive: true,
    maintainAspectRatio: false,
    animation: false,
    plugins: {
      title: {
        display: true,
        text: "parallel-basic · javascript · chartjs · anyplot.ai",
        color: t.ink,
        font: { size: 22 },
      },
      legend: {
        labels: {
          color: t.ink,
          font: { size: 16 },
          boxWidth: 24,
          generateLabels: () =>
            speciesStats.map((species, i) => ({
              text: species.name,
              fillStyle: speciesColors[i],
              strokeStyle: speciesColors[i],
              lineWidth: 2,
              datasetIndex: individualDatasets.length + i,
            })),
        },
        onClick: () => {},
      },
      tooltip: { enabled: false },
    },
    scales: {
      x: {
        type: "category",
        ticks: { color: t.inkSoft, font: { size: 14 } },
        grid: { color: t.ink, lineWidth: 1.5, tickLength: 0 },
      },
      y: {
        min: 0,
        max: 1,
        ticks: { color: t.inkSoft, font: { size: 14 }, stepSize: 0.25 },
        grid: { display: false },
        title: {
          display: true,
          text: "Normalized Value (min–max scaled per dimension)",
          color: t.ink,
          font: { size: 16 },
        },
      },
    },
  },
});

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/parallel-basic/chartjs/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": "parallel-basic",
  "language": "javascript",
  "library": "chartjs",
  "page": "https://anyplot.ai/parallel-basic/javascript/chartjs",
  "hub": "https://anyplot.ai/parallel-basic",
  "code_json": "https://api.anyplot.ai/specs/parallel-basic/chartjs/code",
  "spec_json": "https://api.anyplot.ai/specs/parallel-basic",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/parallel-basic/javascript/chartjs/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/parallel-basic/javascript/chartjs/plot-dark.png",
  "interactive_light_html": "https://storage.googleapis.com/anyplot-images/plots/parallel-basic/javascript/chartjs/plot-light.html",
  "interactive_dark_html": "https://storage.googleapis.com/anyplot-images/plots/parallel-basic/javascript/chartjs/plot-dark.html",
  "quality_score": 90.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Basic Parallel Coordinates Plot on anyplot.ai.

Other implementations