Decision Boundary Viz

Tiles the plot area with a resolution × resolution grid; each cell is colored by the classifier's prediction at its center. Training points are overlaid by their ground-truth label so misclassifications — dot color disagreeing with the surrounding tile — pop out.

Decision boundary plot, 60 by 60 background grid, 24 training samples. Classifier accuracy on training data: 41.7%.
Customize
Classifier
linear
0.00
Grid
60
Display

Installation

npx shadcn@latest add https://craftbits.dev/r/decision-boundary-viz.json

Usage

import { DecisionBoundaryViz } from "@craft-bits/core";
 
<DecisionBoundaryViz
  predict={(x, y) => (y > 0.5 ? 0 : 1)}
  samples={[
    { x: 0.2, y: 0.8, label: 0 },
    { x: 0.8, y: 0.2, label: 1 },
  ]}
/>

Plot a classifier that lives outside the unit square:

<DecisionBoundaryViz
  predict={(x, y) => (x * x + y * y < 4 ? 1 : 0)}
  samples={samples}
  xRange={[-3, 3]}
  yRange={[-3, 3]}
/>

Trade smoothness for performance with resolution:

<DecisionBoundaryViz
  predict={costlyModel.predict}
  samples={samples}
  resolution={30}  /* ~4x fewer predict() calls than the 60 default */
/>

Understanding the component

  1. Predict-as-prop. The component is classifier-agnostic — pass any (x, y) => 0 | 1 function. Logistic regression, k-NN, a decision tree, a tiny MLP: as long as it returns a label, the boundary appears.
  2. Centre-sampled grid. The plot area is sliced into resolution² cells. predict is called once per cell at its centre, not its corner, so the visible boundary lands on cell edges. Higher resolution = smoother boundary, but predict runs more times per render — memoize expensive predictors with useCallback.
  3. Two-colour decision regions. Label 1 cells fill with cb-accent at 22% opacity; label 0 cells fill with cb-fg-muted. The low alpha keeps overlaid scatter points readable.
  4. Sample overlay. Each training point renders as a 4.5px filled circle, colored by its ground-truth label. A dot whose colour matches the surrounding tile is correctly classified; a colour mismatch is a misclassification — the eye spots it instantly.
  5. Configurable coordinate space. xRange / yRange define the math-space window. The default [0, 1] square fits unit-cube datasets; pass any other domain to scale the plot.
  6. Reduced motion. prefers-reduced-motion: reduce short-circuits the background fade-in to an instant swap.

Props

PropTypeDefaultDescription
predict(x: number, y: number) => 0 | 1requiredClassifier prediction function. Called resolution² times per render.
samplesreadonly DecisionBoundarySample[]requiredTraining points to overlay on top of the background.
xRangereadonly [number, number][0, 1]Visible x-axis math range.
yRangereadonly [number, number][0, 1]Visible y-axis math range.
resolutionnumber60Grid resolution — boundary is sampled at resolution × resolution cells.
showSamplesbooleantrueRender the training-point scatter overlay.
showAxesbooleantrueRender the x / y axis tick labels.
transitionTransitionSPRINGS.smoothSpring for the background fade-in.
classNamestringMerged onto the root via cn().

The DecisionBoundarySample shape:

interface DecisionBoundarySample {
  x: number;                // feature 1 coordinate
  y: number;                // feature 2 coordinate
  label: 0 | 1;             // ground-truth class
}

Accessibility

  • The root is role="figure" with a visually hidden summary covering the grid resolution, sample count, and the classifier's accuracy on the training set — screen readers hear the gist without needing colour vision.
  • The decision-region cells and the scatter overlay are marked aria-hidden because the summary already conveys their information.
  • Motion respects prefers-reduced-motion: reduce — the background fade collapses to an instant swap.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/viz/DecisionBoundaryViz.tsx). The source paired a draggable decision-tree grower with an info-gain candidate panel and three lesson modes (Explore / Predict / Challenge). The library extract is the bare decision-boundary primitive — a predict function and a samples array in, a tinted grid plus scatter out.