Row By Row Animator

A nine-phase walk-through of a 2×2 matrix-vector multiplication, rendered one row at a time. The learner taps Step through phases 0–8: each row of the matrix scans the input vector, the active pair (A[i][j], x[j]) lights up, a product chip pops above the matrix, and once both products in a row exist they converge along an arc into the corresponding output slot. The terminal phase pulses both outputs and reveals y = Ax.

The component owns its own phase machine; Step advances it, and once you reach phase 8 the button becomes Replay and re-arms cleanly.

Row by row matrix-vector animator. Matrix rows [1, 2] and [3, 4]. Vector [5, 6].
Phase 0 / 8
Phase 0 of 8. Row 0 is about to scan the input. Every entry in this row will pair with a matching entry in x — one multiplication each, then a single sum.

Row 0 is about to scan the input. Every entry in this row will pair with a matching entry in x — one multiplication each, then a single sum.

Customize
Matrix A
1
2
3
4
Input vector x
5
6

Installation

npx shadcn@latest add https://craftbits.dev/r/row-by-row-animator.json

Usage

import { RowByRowAnimator } from "@craft-bits/viz/row-by-row-animator";
 
<RowByRowAnimator />

Re-skin with a different matrix and vector:

<RowByRowAnimator
  matrix={[[0.7, -0.4], [-1.5, 0.6]]}
  vector={[2, -1]}
/>

Drive a parent reducer off each phase change:

<RowByRowAnimator
  onPhaseChange={(phase) => {
    /* sync with sibling narration, analytics, etc. */
  }}
/>

Understanding the component

  1. Coordinate system. The diagram is a 480 × 200 SVG. The matrix sits on the left, the input vector to its right, and the output column on the far right. Row 0 lives near the top, row 1 near the bottom. Product chips float above the matrix for row 0 and below it for row 1.
  2. Phase machine. Internal state is the phase index 0 to 8. Each phase has its own choreography (row 0 highlights / pair 0 / pair 1 / row 0 lands / row 1 highlights / pair 2 / pair 3 / row 1 lands / terminal pulse). Replay resets to 0.
  3. Imperative animation. Every per-frame attribute write lands on a ref via motion's animate()strokeDashoffset for the connection draws and convergence arcs, opacity for cell glows and chips, scale for the output pop. React only re-renders on phase changes, so the SVG itself never tears.
  4. Pair palette. The first pair in each row uses --cb-accent, the second uses --cb-warning, and the final convergence arc + output uses --cb-success. The hues stay distinguishable independent of theme because they pull from semantic tokens; consumers can recolour the whole animation by overriding the tokens in their app.
  5. Row band. A faint full-width band highlights whichever row is currently scanning. It animates between rows so the structural "row 1 now takes over" moment reads even if the learner missed a step.
  6. Reduced motion. Under prefers-reduced-motion: reduce, every phase collapses to an instant attribute set: connection lines snap to their drawn state, chips appear without slide, the convergence arcs are skipped, and the output cells appear instantly.

Props

PropTypeDefaultDescription
matrixreadonly [readonly [number, number], readonly [number, number]][[1, 2], [3, 4]]2 × 2 weight matrix. Row 0 drives phases 0–3; row 1 drives phases 4–7.
vectorreadonly [number, number][5, 6]Two-element input vector, multiplied row-by-row.
transitionTransitionSPRINGS.bouncyOverride the chip / output pop spring.
onPhaseChange(phase) => voidFires after each Step (or Replay) with the new phase index.
onReset() => voidFires when the user clicks Replay.
classNamestringMerged onto the root via cn().

Accessibility

  • The SVG has role="img" and an aria-label summarising the matrix, the vector, and the current phase.
  • The component root has aria-labelledby pointed at an sr-only heading naming the two matrix rows and the input vector.
  • A live region below the buttons announces the current phase index and narration prose so screen-reader users get the same narrative as sighted users.
  • The narration paragraph also has aria-live="polite" and reads as plain prose; it is the canonical explanation for each phase.
  • Colour is never the only signal — every phase change updates the narration text and the live-region status as well as the SVG.
  • The Step / Replay button has a visible focus ring via :focus-visible and is the only operable control, so the entire animator is reachable in one Tab.
  • Motion respects prefers-reduced-motion: reduce — every staged animation collapses to an instant attribute set.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/math/RowByRowAnimator.tsx). The source was a tightly bundled lesson component — it consumed SvgLabel for every text run, ChallengeBtn for the Step button, depended on per-track lesson palette tokens, and inlined an EASINGS.out import. The viz extract drops the lesson chrome (raw <text> plus a token-styled button), remaps the colour palette to var(--cb-*) semantic tokens so consumer themes repaint freely, re-keys the pop spring to the canonical SPRINGS.bouncy, and replaces the project-specific easing import with a literal cubic-bezier so the component carries no project-specific motion import.