Neuron Playground
An interactive playground for the simplest neural-network unit. The learner drags three sliders — w₁, w₂, bias — and a dashed line in (x₁, x₂) space pivots (weights rotate the line) or shifts (bias translates it). Eight labelled points are coloured by class; misclassified ones pulse so the eye finds the gaps. A heatmap of the chosen activation (sigmoid or relu) tints each side of the line.
Three modes layer goals on top: explore for free-play with a row of bookmarked starting positions, predict for an intuition test against five hidden points, and challenge to beat a target accuracy and then meet XOR — the proof that one straight line cannot separate the two XOR-pocket points from the rest.
blue = class 1 · grey = class 0
Equation
z = 0.50x₁ + 1.00x₂ − 1.50
output = σ(z)
Accuracy: 6/8
6/8 correct. Misclassified points pulse — can you fix them?
Installation
npx shadcn@latest add https://craftbits.dev/r/neuron-playground.jsonUsage
import { NeuronPlayground } from "@craft-bits/viz/neuron-playground";
<NeuronPlayground />Start in challenge mode with a custom target:
<NeuronPlayground defaultMode="challenge" challengeTarget={6} />Subscribe to state changes for an external dashboard:
<NeuronPlayground
onStateChange={(s) => {
/* read s.w1, s.w2, s.bias, s.activation */
}}
/>Drive the mode from outside (controlled):
const [mode, setMode] = useState<"explore" | "predict" | "challenge">("explore");
<NeuronPlayground mode={mode} onModeChange={setMode} />Understanding the component
- Coordinate system. The plot is a 280 × 280 SVG with origin in the top-left (SVG default).
x₁runs left-to-right;x₂is flipped so positivex₂reads as up visually.toSvg(v)maps a domain value to a pixel coordinate. - The boundary line. The neuron decides
class 1wheneverw₁·x₁ + w₂·x₂ + bias ≥ 0. The line where that expression equals zero is what we draw. We walk the four sides of the visible box and keep the first two intersections, so the line is always clipped to the plot. - Activation heatmap. Eight-by-eight tiles fade between accent (high activation) and muted (low). Switching activations rewrites the gradient without changing the boundary line, so the dead-zone behaviour of
relubecomes visible against the smooth ramp ofsigmoid. - Misclassified pulses. Each data point is reclassified on every render; the wrong ones get an outer ring that pulses via inline
<animate>(collapsed to a static outline underprefers-reduced-motion: reduce). This is the visual nudge that says here's a gap. - Predict mode. A single test point appears with a
?marker. The learner picks a class; the point fills with the neuron's actual answer and the marker becomes✓or✗. The intuition score counts agreements between learner and neuron, not learner and ground truth. - Challenge mode. When accuracy hits
challengeTarget, the caption hints at the XOR pocket. A reveal button then highlights the two pocket points and explains why no single line can separate them — the natural intro to multi-layer networks. - Reduced motion. The misclassification, XOR-pocket, and predict-mode pulses all become static outlines. No animation runs.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
defaultState | NeuronPlaygroundState | { w1: 0.5, w2: 1, bias: -1.5, activation: "sigmoid" } | Initial neuron parameters. Reset returns here. |
data | ReadonlyArray<NeuronPlaygroundDataPoint> | 8-point demo set | Points to classify, each with x1, x2, label. |
testPoints | ReadonlyArray<NeuronPlaygroundDataPoint> | 5-point demo set | Test points shown one-at-a-time in predict mode. |
challengeTarget | number | 7 | Score required to "win" the challenge mode. |
defaultMode | "explore" | "predict" | "challenge" | "explore" | Uncontrolled starting mode. |
mode | "explore" | "predict" | "challenge" | — | Controlled mode (pair with onModeChange). |
onModeChange | (mode) => void | — | Fires when the user picks a different tab. |
onStateChange | (state) => void | — | Fires whenever w₁, w₂, bias, or activation changes. |
bookmarks | ReadonlyArray<NeuronPlaygroundBookmark> | 4-preset row | Named starting positions shown in the bookmark row. |
showBookmarks | boolean | true in explore | Force the bookmark row on (or off) in every mode. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The plot SVG is
role="img"with anaria-labelsummarising the boundary equation and current accuracy. - Each parameter slider is a native
<input type="range">with anaria-labelcarrying the label, description, and live value — full keyboard support comes for free. - The mode tabs are a
role="tablist"; each tab isrole="tab"witharia-selectedreflecting the active mode. - The narration paragraph has
aria-live="polite"and reads the current mode's prose status. It updates after every parameter change, every predict guess, and every challenge milestone. - Colour is never the only signal — the misclassification pulse uses motion as well as colour, and the predict-mode
✓/✗glyphs encode correctness without relying on green vs. red. - Motion respects
prefers-reduced-motion: reduce— every<animate>is gated and collapses to a static outline.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/math/NeuronPlayground.tsx). The source was wrapped in the lessonWidgetchrome (useWidgetHistory, undo/redo, formula bar, premise card) and consumed lesson-track palette tokens (--color-accent-500,--color-ink-700,--color-fail-500, …) plus theSvgLabelhelper. The viz extract drops the lesson chrome, swaps the palette tovar(--cb-*)semantic tokens so consumer themes repaint freely, replaces the lesson-history hook with a singledefaultStatereset, and exposes the three modes plus the bookmark row as a controlled / uncontrolled prop API.