Matrix Transform

A square SVG plane that visualizes a 2x2 matrix as the linear transformation it performs on the plane. The matrix's two columns are exactly where the basis vectors î = (1, 0) and ĵ = (0, 1) land — drag either column's arrowhead to reshape the whole grid in real time. The signed-area determinant is surfaced below, so collapsing-to-a-line (det = 0) and orientation-flipping (det < 0) are immediately observable.

2 by 2 matrix transformation. i hat lands at (1.00, 0.00). j hat lands at (0.00, 1.00). Determinant 1.00.col 1col 2
det · signed area1.00
Customize
Column 1 (where i hat lands)
1
0
Column 2 (where j hat lands)
0
1
Display

Installation

npx shadcn@latest add https://craftbits.dev/r/matrix-transform.json

Usage

import { MatrixTransform } from "@craft-bits/core";
 
<MatrixTransform
  defaultMatrix={[
    [1, 0],
    [0, 1],
  ]}
/>

Make the basis-vector tips draggable and observe matrix changes:

const [matrix, setMatrix] = useState<Matrix2D>([
  [1, 0],
  [0, 1],
]);
 
<MatrixTransform
  matrix={matrix}
  onMatrixChange={setMatrix}
  interactive
/>

Apply a 90° rotation as a controlled value:

<MatrixTransform
  matrix={[
    [0, -1],
    [1, 0],
  ]}
/>

A singular matrix (det = 0) — the plane collapses to a line:

<MatrixTransform
  matrix={[
    [1, 2],
    [0.5, 1],
  ]}
/>

Understanding the component

  1. Math-space coordinates, y points up. The matrix is row-major: [[a, b], [c, d]]. The first column [a, c] is where î lands; the second column [b, d] is where ĵ lands. Every point (x, y) maps to (a·x + b·y, c·x + d·y).
  2. Deformed grid as the transformation. With showGrid, the integer grid is rendered twice: a very faint original cartesian grid, plus the image of that grid under the matrix in accent color. Watching the lines warp is the linear-algebra "what does this matrix do?" intuition.
  3. Two columns, two tones. Column 1 (where î lands) draws in var(--cb-accent); column 2 (where ĵ lands) draws in var(--cb-warning). The original î / ĵ basis vectors are shown as dashed dim arrows so the user can see the before / after.
  4. Unit-square parallelogram. The image of the unit square is filled at low opacity. Its color switches to var(--cb-warning) when the matrix is singular (det ≈ 0) and var(--cb-error) when orientation flips (det < 0) — visual feedback aligned with the determinant readout.
  5. Drag handles on the column tips. When interactive, each transformed basis-vector arrowhead becomes a 32px-diameter role="slider" invisible hit target. Dragging projects the pointer through getScreenCTM().inverse() so it tracks under any transform; the tip clamps to range.
  6. Numeric entry parity. Below the plot, four numeric <input type="number"> cells let the user type matrix entries directly. Color-coded to match each column.
  7. Determinant readout. The signed area scaling factor is shown beneath the inputs. Green when positive, warning-tinted when zero (singular), error-tinted when negative (orientation flipped).
  8. Spring transitions. The deformed grid + parallelogram animate with SPRINGS.smooth between matrix prop changes. Drag handles follow the pointer with SPRINGS.snap. prefers-reduced-motion: reduce collapses both to duration: 0.

Props

PropTypeDefaultDescription
matrixMatrix2DControlled 2x2 matrix. Pair with onMatrixChange.
defaultMatrixMatrix2DidentityUncontrolled initial matrix.
onMatrixChange(next: Matrix2D) => voidFires on drag, keyboard nudge, or numeric edit.
rangereadonly [number, number][-3, 3]Visible math-space domain on both axes.
showGridbooleantruePaint the original + deformed integer grid.
showAxesbooleantruePaint the x / y axes through the origin.
showBasisVectorsbooleantrueHighlight the original î / ĵ (dashed) + their transformed images.
interactivebooleanfalseMake each column tip a drag + focus handle.
sizenumber320SVG side length in pixels (the plane is square).
transitionTransitionSPRINGS.snapSpring for drag-follow transitions.
classNamestringMerged onto the root <div> via cn().

The Matrix2D shape is a row-major tuple — [[a, b], [c, d]] represents

| a  b |
| c  d |

Accessibility

  • The SVG is role="img" with a visually hidden summary listing where î and ĵ land and the current determinant.
  • When interactive, each column tip exposes a role="slider" invisible 32px hit target meeting WCAG 2.5.8 (Target Size, Enhanced).
  • Keyboard: focus a handle, then / / / nudge in 0.25-unit steps. Shift+Arrow nudges by 1 unit. The tip clamps to range.
  • The four numeric inputs accept arrow-key increment via step={0.1} and full numeric entry; each input carries an aria-label identifying its (row, column) position.
  • The determinant readout is aria-live="polite" — assistive tech announces the value as it changes.
  • Animation respects prefers-reduced-motion: reduce — all springs collapse to an instant swap.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/math/MatrixTransform.tsx). The source was a determinant-intuition lesson wired up with Explore / Predict / Challenge modes, randomly generated predict rounds, challenge pools, narration heuristics, and preset bookmarks. The library extract is the pure visualization primitive — a 2x2 matrix, the deformed grid, drag-the-tips interactivity, numeric entry, and the determinant readout.