Basic Density Plot — Chart.js

A density plot (also known as Kernel Density Estimation or KDE plot) visualizes the distribution of a continuous variable by smoothing the data into a continuous probability density curve. Unlike histograms which use discrete bins, density plots provide a smooth representation of the underlying distribution, making it easier to identify patterns such as skewness, modality, and overall shape.

Basic Density Plot rendered with Chart.js

Renders

JavaScript source (Chart.js)

// anyplot.ai
// density-basic: Basic Density Plot
// Library: chartjs 4.4.7 | JavaScript 22.23.2
// Quality: 90/100 | Created: 2026-08-24

const t = window.ANYPLOT_TOKENS;

// --- Deterministic PRNG (mulberry32) ----------------------------------------
function mulberry32(seed) {
  return function () {
    seed |= 0;
    seed = (seed + 0x6d2b79f5) | 0;
    let x = Math.imul(seed ^ (seed >>> 15), 1 | seed);
    x = (x + Math.imul(x ^ (x >>> 7), 61 | x)) ^ x;
    return ((x ^ (x >>> 14)) >>> 0) / 4294967296;
  };
}
const rand = mulberry32(42);

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

// --- Data: daily commute times (minutes), right-skewed ----------------------
const n = 400;
const commuteMinutes = [];
for (let i = 0; i < n; i++) {
  const minutes = Math.exp(3.1 + 0.35 * randomNormal());
  commuteMinutes.push(Math.max(4, minutes));
}

// --- Kernel density estimate (Gaussian kernel, Silverman bandwidth) --------
const mean = commuteMinutes.reduce((a, b) => a + b, 0) / n;
const variance =
  commuteMinutes.reduce((a, b) => a + (b - mean) ** 2, 0) / (n - 1);
const std = Math.sqrt(variance);
const bandwidth = 1.06 * std * Math.pow(n, -1 / 5);

const dataMin = Math.min(...commuteMinutes);
const dataMax = Math.max(...commuteMinutes);
const pad = (dataMax - dataMin) * 0.15;
const gridStart = Math.max(0, Math.floor((dataMin - pad) / 5) * 5);
const gridEnd = Math.ceil((dataMax + pad) / 5) * 5;
const gridSize = 200;

function gaussianKernel(u) {
  return Math.exp(-0.5 * u * u) / Math.sqrt(2 * Math.PI);
}

const densityPoints = [];
for (let i = 0; i <= gridSize; i++) {
  const x = gridStart + ((gridEnd - gridStart) * i) / gridSize;
  let sum = 0;
  for (let j = 0; j < n; j++) {
    sum += gaussianKernel((x - commuteMinutes[j]) / bandwidth);
  }
  densityPoints.push({ x, y: sum / (n * bandwidth) });
}

const maxDensity = Math.max(...densityPoints.map((p) => p.y));

// --- Rug plot: individual observations along the baseline -------------------
const rugY = -0.07 * maxDensity;
const rugPoints = commuteMinutes.map((minutes) => ({ x: minutes, y: rugY }));

// --- Fill color (Imprint brand green at low alpha) --------------------------
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})`;
}

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

// --- Chart ---------------------------------------------------------------
new Chart(canvas, {
  data: {
    datasets: [
      {
        type: "line",
        label: "Density estimate",
        data: densityPoints,
        borderColor: t.palette[0],
        backgroundColor: hexToRgba(t.palette[0], 0.25),
        borderWidth: 3.5,
        pointRadius: 0,
        tension: 0.35,
        fill: "origin",
      },
      {
        type: "scatter",
        label: "Observations",
        data: rugPoints,
        pointStyle: "line",
        rotation: 90,
        radius: 9,
        borderColor: hexToRgba(t.palette[0], 0.45),
        borderWidth: 1.5,
      },
    ],
  },
  options: {
    responsive: true,
    maintainAspectRatio: false,
    animation: false,
    plugins: {
      title: {
        display: true,
        text: "density-basic · javascript · chartjs · anyplot.ai",
        color: t.ink,
        font: { size: 22 },
      },
      legend: { display: false },
    },
    scales: {
      x: {
        type: "linear",
        min: gridStart,
        max: gridEnd,
        ticks: { color: t.inkSoft, font: { size: 14 } },
        grid: { display: false },
        title: {
          display: true,
          text: "Commute Time (minutes)",
          color: t.ink,
          font: { size: 16 },
        },
      },
      y: {
        min: rugY * 1.6,
        max: maxDensity * 1.15,
        ticks: { color: t.inkSoft, font: { size: 14 } },
        grid: { color: t.grid },
        title: {
          display: true,
          text: "Density",
          color: t.ink,
          font: { size: 16 },
        },
      },
    },
  },
});

Part of Basic Density Plot on anyplot.ai.

Other implementations