Proof Machine
A single SVG canvas with two draggable 2D vectors, plus a horizontal "proof strip" of four computation nodes connected by + and =:
[a₁b₁] + [a₂b₂] = [Σ] = [|a||b|cos θ]
Geometric overlays — shadow bar (projection of b onto a), drop-line, component rectangles, projection foot, and angle arc — are always visible at a quiet baseline opacity, so spatial feedback stays primary during drag. Hovering or tapping a proof-strip node enhances the relevant overlay rather than revealing it from nothing. The sign of the dot product colors the shadow bar (success positive, error negative); crossing zero triggers a brief pulse so the perpendicular boundary feels like a real event. A prediction gate hides the sum for the first few interactions — once it opens, a construction mode asks you to stack the two shadow segments yourself, then auto-verifies against the geometric projection.
6.0 + 6.0 = ?
Installation
npx shadcn@latest add https://craftbits.dev/r/proof-machine.jsonUsage
import { ProofMachine } from "@craft-bits/viz/proof-machine";
<ProofMachine />Controlled with external state:
const [vectors, setVectors] = useState({
a: { x: 3, y: 2 },
b: { x: 2, y: 3 },
});
<ProofMachine value={vectors} onValueChange={setVectors} />Skip the prediction gate (geometry always visible, no construction challenge):
<ProofMachine gateThreshold={0} />Understanding the component
- Math-space coordinate system. The plane shows
x,yin[-range, +range](defaultrange = 5) withypointing up. Vector tips clamp to a circle of radius0.92 × rangeso arrowheads never clip the viewBox. - Two draggable arrows. Each vector tip is an SVG circle with
role="slider". Pointer events drive the math-space state directly; arrow-key nudges step0.25units (or1with Shift). - Always-on geometric overlays. The signed shadow bar, the dashed drop-line from
bto the projection foot, the component rectangles on each axis, and the angle arc all render at a quiet baseline opacity. The "active" proof-strip node enhances the matching overlay. - Sign-aware shadow colour. Positive dot product paints the shadow bar with
--cb-success; negative paints with--cb-error. Near-zero (perpendicular) lands on--cb-fg-muted. Crossing zero triggers a brief opacity flash. - Prediction gate. The Σ and |a||b|cos θ nodes hide their values behind a "?" until you tap them. Each successful reveal increments a counter; once it reaches
gateThreshold(default3), the gate stays open and the construction challenge starts. - Construction challenge. After the gate opens, the shadow bar hides and the
a₁b₁/a₂b₂nodes pulse. Tap them in order to stack the two contributions; once both segments are placed, the geometry auto-confirms with a success-coloured projection foot and a check mark on Σ and |a||b|cos θ. - Magnitude lock. The
lock |v|pill freezes the current magnitudes of both vectors — drags now slide along arcs of constant length, isolating the angle as the only free variable. - Animated dot-product readout. The numeric value in the formula tweens smoothly between renders; under
prefers-reduced-motion, it snaps to the new value.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
defaultValue | ProofMachineState | { a: {3,2}, b: {2,3} } | Initial vector state (uncontrolled). |
value | ProofMachineState | — | Controlled vector state. Pair with onValueChange. |
onValueChange | (next) => void | — | Called whenever a vector moves. |
range | number | 5 | Half-range of the visible axis. |
size | number | 320 | Pixel side length of the square SVG canvas. |
gateThreshold | number | 3 | Reveals before the gate opens. 0 disables the gate. |
rehideDelayMs | number | 2000 | Delay before geometry re-hides after a check-flash (gate closed). |
showControls | boolean | true | Show the preset / mag-lock / reset row. |
showNarration | boolean | true | Show the live narration block. |
presetTransition | Transition | SPRINGS.snap | Override the preset-tween transition. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- Each draggable vector tip is an SVG circle with
role="slider",aria-label,aria-valuetext, andaria-valuemin/aria-valuemaxso screen readers announce the current numeric position. - The canvas SVG carries a live
aria-labelsummary ("Vector a equals … Vector b equals … Dot product … Angle … degrees"). - The formula and narration blocks are both
aria-live="polite"so updates are announced without interrupting other speech. - Every proof-strip node is a
<button>witharia-pressedreflecting its active state and a descriptivearia-labelthat changes during the construction challenge to invite the next tap. - Keyboard support: arrow keys nudge the focused vector tip by
0.25math-units; Shift+arrow nudges by1. - Motion respects
prefers-reduced-motion: reduce— the animated dot readout snaps, CSS transitions on overlays collapse tonone, the sign-flip flash never triggers, and the preset tween skips to the final value. - Colour is never the only signal — the active node also bumps opacity and stroke-width, and runs a check-flash to confirm changes.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/math/ProofMachine.tsx). The source was wired into lesson chrome viaSvgLabel, lesson-specific--color-*tokens, andSPRINGS.snappy. The viz extract strips the lesson plumbing, swaps the palette to--cb-*semantic tokens, usesSPRINGS.snapfor preset tweens, and exposesdefaultValue/value/onValueChangefor controlled use.