Density Plot with Rug Marks — Highcharts

A kernel density estimation (KDE) plot combined with rug marks along the x-axis, showing both the smoothed probability distribution and the exact location of each individual data point. This combination provides the best of both worlds: the KDE reveals the overall shape, modality, and smoothed density of the distribution, while the rug marks preserve transparency about where actual observations fall, highlighting data density and potential gaps.

Density Plot with Rug Marks rendered with Highcharts

Renders

JavaScript source (Highcharts)

// anyplot.ai
// density-rug: Density Plot with Rug Marks
// Library: highcharts 12.6.0 | JavaScript 22.23.2
// Quality: 90/100 | Created: 2026-09-02

//# anyplot-orientation: landscape
const t = window.ANYPLOT_TOKENS;

// --- Data (in-memory, deterministic, LCG-seeded Box-Muller) ----------------
const makeLcg = (seed) => {
  let state = seed >>> 0;
  return () => {
    state = (Math.imul(state, 1664525) + 1013904223) >>> 0;
    return state / 4294967296;
  };
};
const rand = makeLcg(42);
const gaussian = () => {
  const u1 = Math.max(rand(), 1e-12);
  const u2 = rand();
  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
};

// Reaction times (ms) from a lexical-decision task: a fast majority group
// plus a slower, more variable tail group — a realistic bimodal shape.
const reactionTimes = [];
for (let i = 0; i < 150; i++) {
  const isSlowGroup = rand() < 0.3;
  const mean = isSlowGroup ? 485 : 320;
  const spread = isSlowGroup ? 55 : 38;
  reactionTimes.push(Math.round(mean + gaussian() * spread));
}
reactionTimes.sort((a, b) => a - b);

// --- Kernel density estimation (Gaussian kernel, Silverman bandwidth) ------
const n = reactionTimes.length;
const mean = reactionTimes.reduce((s, v) => s + v, 0) / n;
const variance = reactionTimes.reduce((s, v) => s + (v - mean) ** 2, 0) / (n - 1);
const std = Math.sqrt(variance);
const bandwidth = 1.06 * std * n ** (-1 / 5);

const gaussianKernel = (u) => Math.exp(-0.5 * u * u) / Math.sqrt(2 * Math.PI);
const densityAt = (x) =>
  reactionTimes.reduce((s, v) => s + gaussianKernel((x - v) / bandwidth), 0) / (n * bandwidth);

const gridMin = reactionTimes[0] - 3 * bandwidth;
const gridMax = reactionTimes[n - 1] + 3 * bandwidth;
const gridSteps = 200;
const densityCurve = [];
for (let i = 0; i <= gridSteps; i++) {
  const x = gridMin + ((gridMax - gridMin) * i) / gridSteps;
  densityCurve.push([x, densityAt(x)]);
}
const maxDensity = Math.max(...densityCurve.map((p) => p[1]));

// Rug ticks sit below the zero line as a thin band, separated from the fill.
const tickHeight = maxDensity * 0.12;
const rugColor = `${t.palette[0]}73`; // brand green, ~45% opacity (overlap-friendly)

// Nice round y-axis tick interval (1/2/5 * 10^k) so labeled ticks are spaced
// far enough apart to stay visually distinct after rounding.
const niceStep = (rough) => {
  const exp = Math.floor(Math.log10(rough));
  const base = rough / 10 ** exp;
  const niceBase = base < 1.5 ? 1 : base < 3 ? 2 : base < 7 ? 5 : 10;
  return niceBase * 10 ** exp;
};
const yTickInterval = niceStep(maxDensity / 5);

// --- Chart -------------------------------------------------------------
Highcharts.chart("container", {
  chart: {
    backgroundColor: "transparent",
    animation: false,
    style: { fontFamily: "inherit" },
  },
  credits: { enabled: false },
  colors: t.palette,
  title: {
    text: "density-rug · javascript · highcharts · anyplot.ai",
    style: { color: t.ink, fontSize: "22px", fontWeight: "600" },
  },
  xAxis: {
    title: { text: "Reaction Time (ms)", style: { color: t.inkSoft, fontSize: "16px" } },
    lineColor: t.inkSoft,
    tickColor: t.inkSoft,
    gridLineColor: t.grid,
    labels: { style: { color: t.inkSoft, fontSize: "14px" } },
    min: gridMin,
    max: gridMax,
  },
  yAxis: {
    title: { text: "Density", style: { color: t.inkSoft, fontSize: "16px" } },
    gridLineColor: t.grid,
    labels: {
      style: { color: t.inkSoft, fontSize: "14px" },
      formatter() {
        return this.value >= 0 ? this.value.toFixed(4) : "";
      },
    },
    tickInterval: yTickInterval,
    min: -tickHeight * 1.4,
    max: maxDensity * 1.15,
    plotLines: [{ value: 0, color: t.inkSoft, width: 1, zIndex: 3 }],
  },
  legend: { enabled: false },
  tooltip: { enabled: false },
  plotOptions: {
    series: { animation: false, enableMouseTracking: false },
  },
  series: [
    {
      type: "area",
      name: "Estimated density",
      data: densityCurve,
      color: t.palette[0],
      lineWidth: 2.5,
      fillOpacity: 0.25,
      threshold: 0,
      marker: { enabled: false },
    },
    {
      type: "column",
      name: "Observations",
      data: reactionTimes.map((v) => [v, -tickHeight]),
      color: rugColor,
      borderWidth: 0,
      pointWidth: 2,
      threshold: 0,
      pointPadding: 0,
      groupPadding: 0,
    },
  ],
});

Retrieve this implementation

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

Part of Density Plot with Rug Marks on anyplot.ai.

Other implementations