ROC Curve Explorer

Plots true-positive-rate against false-positive-rate as the classifier's threshold sweeps from permissive to strict. The area under the curve (AUC) summarizes overall discriminative power; the marker on the curve tracks the current threshold.

ROC curve. AUC 1.000. Threshold 0.50: FPR 0.0%, TPR 100.0%.
0%25%50%75%100%0%25%50%75%100%False Positive RateTrue Positive Rate
AUC1.000
t0.50
FPR0.0%
TPR100.0%
Customize
Classifier
strong
Threshold
0.50
Display

Installation

npx shadcn@latest add https://craftbits.dev/r/roc-curve-explorer.json

Usage

import { ROCCurveExplorer } from "@craft-bits/core";
 
<ROCCurveExplorer scores={scores} labels={labels} />

Pass pre-computed ROC points instead:

<ROCCurveExplorer
  points={[
    { threshold: 1.0, fpr: 0, tpr: 0 },
    { threshold: 0.7, fpr: 0.1, tpr: 0.6 },
    { threshold: 0.4, fpr: 0.3, tpr: 0.9 },
    { threshold: 0.0, fpr: 1, tpr: 1 },
  ]}
/>

Drive the threshold from outside:

const [t, setT] = useState(0.5);
 
<ROCCurveExplorer
  scores={scores}
  labels={labels}
  threshold={t}
  onThresholdChange={setT}
/>

Understanding the component

  1. Two input modes. Either pass paired scores + labels arrays and let the component derive the ROC points, or pass points directly. When both are supplied, points wins — useful for plotting curves that don't have raw scores (e.g., remote inference results).
  2. Threshold sweep. When scores + labels are given, the component sorts every unique score, computes (FPR, TPR) at each, and dedupes to keep the path compact. The resulting curve runs corner-to-corner from (0, 0) to (1, 1).
  3. AUC via trapezoidal integration. AUC is the trapezoidal area under the ROC line. AUC = 0.5 means random guessing (the diagonal); 1.0 means perfect separation. The readout below the chart shows the value to three decimals.
  4. Threshold marker. A filled circle on the curve locates the current threshold. Dragging the threshold slider — or driving threshold externally — animates the marker between curve points with SPRINGS.snap. The closest curve point by threshold wins (the curve is piecewise-constant between observed scores).
  5. Diagonal reference. When showDiagonal is on, a dashed y = x line in var(--cb-fg-subtle) shows where a random classifier would sit. Curves above the diagonal beat random.
  6. Reduced motion. prefers-reduced-motion: reduce short-circuits both the curve fade-in and the marker spring — they snap instantly.

Props

PropTypeDefaultDescription
scoresreadonly number[]Raw classifier scores, paired with labels.
labelsreadonly boolean[]Ground-truth labels (true = positive class).
pointsreadonly ROCPoint[]Pre-computed ROC points. Takes precedence over scores + labels.
thresholdnumberControlled threshold in [0, 1].
defaultThresholdnumber0.5Initial uncontrolled threshold.
onThresholdChange(t: number) => voidFires when the threshold changes.
showAUCbooleantrueRender the AUC readout.
showDiagonalbooleantrueRender the y = x random-classifier reference.
transitionTransitionSPRINGS.snapOverride the marker transition.
classNamestringMerged onto the root <div> via cn().

The shape:

interface ROCPoint {
  threshold: number; // in [0, 1]
  fpr: number;       // in [0, 1]
  tpr: number;       // in [0, 1]
}

Accessibility

  • The root <div> is role="figure" with a visually hidden summary of AUC plus the current threshold's (FPR, TPR).
  • The threshold slider is a native <input type="range"> — keyboard control (arrow keys, Home, End) and screen-reader semantics come for free.
  • Focus ring uses :focus-visible with the accent color and a 2px offset so keyboard users always see where they are.
  • Motion respects prefers-reduced-motion: reduce — curve and marker animations collapse to instant updates.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/viz/ROCCurveExplorer.tsx). The source had two lesson modes (Explore / Predict), a paired score-scatter panel, threshold-quiz rounds, narration heuristics, and a usePredictRounds controller. The library extract is the bare ROC primitive — points in, curve + AUC + threshold marker out.