Distribution Overlay

Overlay multiple probability distributions on a single x-axis for visual comparison. Each distribution renders as a filled, semi-transparent shape — a histogram, a kernel-density estimate, or an analytic Gaussian curve. Useful for comparing model outputs, visualizing calibration, or showing a before/after shift.

Distribution overlay (histogram). x-range 0.11 to 0.98. Overlaid distributions: ground truth, model A, model B.
0.110.330.540.760.98
  • ground truth
  • model A
  • model B
Customize
Mode
histogram
36
Distributions
Display

Installation

npx shadcn@latest add https://craftbits.dev/r/distribution-overlay.json

Usage

import { DistributionOverlay } from "@craft-bits/core";
 
<DistributionOverlay
  distributions={[
    { id: "p", label: "ground truth", samples: truth, tone: "success" },
    { id: "q", label: "model A",      samples: modelA, tone: "accent"  },
    { id: "r", label: "model B",      samples: modelB, tone: "warning" },
  ]}
/>

Switch to a Gaussian curve from analytic parameters:

<DistributionOverlay
  mode="curve"
  distributions={[
    { id: "prior",     label: "prior",     mean: 0,   stddev: 1.0, tone: "accent"  },
    { id: "posterior", label: "posterior", mean: 0.6, stddev: 0.4, tone: "success" },
  ]}
/>

Understanding the component

  1. Three render modes. histogram bins each distribution's samples; kde smooths samples with a Gaussian kernel; curve draws an analytic Gaussian from mean + stddev. Histogram and KDE consume samples; curve falls back to the sample mean/stddev when raw samples are present but mean / stddev are not.
  2. Shared x-axis. The component derives a single [min, max] range across every distribution unless xRange is provided. For curve it uses μ ± 3σ per item; otherwise it takes the union of sample extents plus a small padding margin.
  3. Shared y-scale. All distributions share a y-scale normalized to the global peak density, so visually-larger filled shapes mean genuinely-higher density — overlapping peaks are comparable at a glance.
  4. Stacking order. Distributions are drawn back-to-front in array order. Put the distribution you want on top — usually the "winner" or "ground truth" — last in the array.
  5. Tones. Each item picks a semantic tone (accent, success, warning, error) that resolves to var(--cb-*). Theming a craft-bits app re-skins every distribution in one place.
  6. Reduced motion. prefers-reduced-motion: reduce short-circuits the SPRINGS.smooth path transition — shapes snap to their new geometry instantly.

Props

PropTypeDefaultDescription
distributionsreadonly DistributionOverlayItem[]Distributions to overlay (stacked back-to-front in array order).
mode"histogram" | "kde" | "curve""histogram"How each distribution is rasterized.
binsnumber40Bin count for histogram / sample count for kde.
xRange[number, number]autoOverride the shared x-axis range.
showAxisbooleantrueRender the x-axis ticks and baseline.
showLegendbooleantrueRender the legend chips below the chart.
transitionTransitionSPRINGS.smoothOverride the path spring.
classNamestringMerged onto the root <div> via cn().

Each item:

interface DistributionOverlayItem {
  id: string;
  label: string;
  samples?: readonly number[];
  mean?: number;
  stddev?: number;
  tone?: "accent" | "warning" | "success" | "error";
}

Accessibility

  • The root <div> is role="figure" with a visually-hidden summary of the mode, x-range, and distribution labels.
  • The SVG itself is role="img" with the same accessible name; inner shapes are decorative.
  • Motion respects prefers-reduced-motion: reduce — path transitions collapse to instant updates.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/nn/DistributionOverlay.tsx). The source was a single-purpose KL-divergence teaching widget with dragging, narration, per-category penalty bars, a flip toggle, and phase state. The library extract is the bare overlay primitive — N distributions in, stacked filled paths out.