XOR Puzzle
A focused one-screen puzzle. The learner drags three sliders — w₁, w₂, bias — and a glowing dashed line in (x₁, x₂) space pivots and shifts. Eight labelled points sit on the plot; six are easy, two are XOR-pocket intruders that no straight line can rescue. When the learner's best score reaches revealThreshold (default 7), a small reveal CTA appears. Clicking it pulses the two intruders and explains why no single boundary can place them both correctly — the natural intro to multi-layer networks.
XOR puzzle — drag the line to classify eight points.
Accuracy5/8
w₁0.50
w₂1.00
bias-1.50
Customize
Puzzle
7
0.50
1.00
-1.50
Installation
npx shadcn@latest add https://craftbits.dev/r/xor-puzzle.jsonUsage
import { XORPuzzle } from "@craft-bits/viz/xor-puzzle";
<XORPuzzle />Subscribe to state changes:
<XORPuzzle
onStateChange={(s) => {
/* read s.w1, s.w2, s.bias */
}}
onReveal={() => analytics.track("xor-revealed")}
/>Pass your own dataset with custom intruder indices:
<XORPuzzle
data={[
{ x1: 0.1, x2: 1.4, label: 1 },
{ x1: 2.2, x2: 0.6, label: 0 },
// …
]}
xorIndices={[0, 3]}
revealThreshold={5}
/>Understanding the component
- Coordinate system. The plot is a 300 × 300 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 — clipped to the visible box by walking the four sides and keeping the first two intersections. - The dataset. Eight points are arranged so that six are linearly separable but two — at indices
6and7by default — form an XOR pocket: each one sits in the opposing class's territory. - Best-score gating. A
bestScoreReftracks the highest score seen this session. The reveal CTA appears only when the learner has at some point hitrevealThreshold— the discovery is rewarded, not gated behind the current adjustment. - The reveal. Clicking the CTA pulses the two intruders with a 3-second
<animate>ring (collapsed to a static outline underprefers-reduced-motion: reduce), labels themXOR, and expands the explanation panel underneath. - Reduced motion. The pulsing intruder ring becomes a static dashed outline. The reveal-panel slide-in collapses to an instant swap.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
defaultState | XORPuzzleState | { w1: 0.5, w2: 1, bias: -1.5 } | Initial neuron parameters. Reset returns here. |
data | ReadonlyArray<XORPuzzleDataPoint> | 8-point demo set | Points to classify, each with x1, x2, label. |
xorIndices | ReadonlyArray<number> | [6, 7] | Indices into data that are highlighted as intruders when revealed. |
revealThreshold | number | 7 | Best score required before the reveal CTA appears. |
onStateChange | (state) => void | — | Fires whenever w₁, w₂, or bias changes. |
onReveal | () => void | — | Fires when the learner clicks "reveal". |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The plot SVG is
role="img"with anaria-labelsummarising the current accuracy. - Each parameter slider is a native
<input type="range">with anaria-labelcarrying the label and live value — full keyboard support comes for free. - The reveal CTA and reset are real
<button>elements withfocus-visiblerings. - Colour is never the only signal — the intruder pulse uses motion plus a
XORtext label so colour-blind users see the cue. - 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/XORPuzzle.tsx). The source consumed lesson-track palette tokens (--color-accent-500,--color-ink-700,--color-fail-500, …), theSvgLabelhelper, and a customneuron-sliderclass. The viz extract swaps the palette tovar(--cb-*)semantic tokens so consumer themes repaint freely, drops theSvgLabelhelper for a plain<text>element, replaces the inlinetransition: { duration, ease }withSPRINGS.default/DURATIONS.default+EASINGS.out, and exposes the reveal threshold and intruder indices as props so the puzzle generalises beyond the bundled dataset.