Column Blender
An interactive picture of the column view of Ax. Two column vectors sit at the origin of a Cartesian plane; sliders scale each one; dashed ghost arrows show the weighted columns; a green result arrow sums them. An optional row-view panel cross-checks the same product via the dot-product formula so the learner sees that the two algorithms — sum the columns, or dot the rows — produce the same vector.
Recipe
x[0] = 2
x[1] = 2
2 · [1, 3] + 2 · [2, 4] = [6, 14]
Customize
Canvas
320 px
±4
Matrix A (column-major)
1
3
2
4
Initial x
2
2
Story
Installation
npx shadcn@latest add https://craftbits.dev/r/column-blender.jsonUsage
import { ColumnBlender } from "@craft-bits/viz/column-blender";
<ColumnBlender
matrixA={[1, 3, 2, 4]}
initialX={[2, 2]}
presets={[{ label: "Load x = [5, 6]", w0: 5, w1: 6 }]}
showRowView
onWeightsChange={([w0, w1]) => console.log(w0, w1)}
/>The matrix is column-major: [col0.x, col0.y, col1.x, col1.y] corresponds to
A = | col0.x col1.x |
| col0.y col1.y |
Strip everything down to the bare visualizer — no presets, no row-view:
<ColumnBlender matrixA={[2, 0, 0, 2]} initialX={[1, 1]} />Stack multiple presets to walk through a small recipe deck:
<ColumnBlender
matrixA={[1, 3, 2, 4]}
presets={[
{ label: "x = [1, 0]", w0: 1, w1: 0 },
{ label: "x = [0, 1]", w0: 0, w1: 1 },
{ label: "x = [5, 6]", w0: 5, w1: 6 },
]}
/>Understanding the component
- Two base arrows. Column 0 paints in the accent token, column 1 in the warning token. Both are anchored at the origin and persist regardless of the slider values — they are the vocabulary of the matrix.
- Two weighted ghost arrows. Whenever the weighted column has non-trivial magnitude the component draws a dashed arrow of that column scaled by its weight. Ghosts share the colour of their base column so the eye tracks them automatically.
- The result arrow. A solid success-coloured arrow runs from the origin to
w0·col0 + w1·col1. Two short dashed construction lines connect the ghost tips to the result tip to make the parallelogram closure visible. - Lerp on preset load. Pressing a preset pill smoothly eases
w0andw1to the target over ~300ms (cubic ease-out) so the learner sees the recipe being assembled. Dragging a slider cancels the lerp. - Row-view cross-check. When
showRowViewis set, a "Verify with row view" button revealsrow₀ · xandrow₁ · xwritten out and confirms they equal the result coordinates — the visual claim that the two algorithms agree. - Reduced motion. Under
prefers-reduced-motion: reducethe preset lerp is replaced by an instant snap; everything else is static.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
matrixA | [number, number, number, number] | [1, 3, 2, 4] | The 2×2 matrix in column-major order. |
initialX | [number, number] | [2, 2] | Initial weights applied to the two columns. |
weightMin | number | -6 | Slider lower bound for each weight. |
weightMax | number | 6 | Slider upper bound for each weight. |
weightStep | number | 0.5 | Slider step. |
range | number | 4 | Half-range of the visible axis. |
size | number | 320 | Pixel size of the square SVG canvas. |
presets | ColumnBlenderPreset[] | — | Optional weight presets rendered as pill buttons. Loading a preset smoothly lerps the weights. |
showRowView | boolean | false | When true, reveals a "Verify with row view" panel. |
onWeightsChange | ([w0, w1]) => void | — | Called whenever either weight changes. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The SVG canvas is
role="img"with anaria-labelthat describes both columns and the current weights/result so screen readers can read the visual state at a glance. - Each slider has a dedicated
aria-label("Weight for column 0" / "Weight for column 1"). - Colour is reinforced with position and label text: weights show next to slider tracks, the formula readout repeats
w0/w1/result in their respective token colours, and the row-view panel duplicates the result digits. - Motion respects
prefers-reduced-motion: reduce— preset loading snaps instantly instead of lerping; slider drags are unaffected either way.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/math/ColumnBlender.tsx). The source was a hand-rolled lesson primitive with built-inModeStrip/ChallengeBtnchrome, a narration banner driven by a phase machine, a hard-codedLoad x = [5, 6]preset, and a project-specific accent/warn/success/ink palette. The viz extract drops the lesson chrome, generalises the preset list and slider range, lifts the "Verify with row view" toggle to a simple boolean prop, repaints onto the craft-bits accent/warning/success/fg tokens, and exposesonWeightsChangefor callers that want to mirror state outside the component.