Reshape Explorer
A teaching primitive for tensor reshape. Render inputShape and outputShape side-by-side as labelled grids; every cell preserves its row-major flat index and morphs from its input position to its output position as progress advances 0 → 1. The element identity stays visible all the way through, so the lesson "reshape changes the indexing, not the data" lands without further narration.
Reshape · [2, 3] → [3, 2]50%
Reshape from [2, 3] to [3, 2]. Progress 50 percent.[2, 3]input
[3, 2]output
Customize
Shape
[2,3] → [3,2]
Animation
0.50
Style
36
Installation
npx shadcn@latest add https://craftbits.dev/r/reshape-explorer.jsonUsage
import { ReshapeExplorer } from "@craft-bits/core";
<ReshapeExplorer inputShape={[2, 3]} outputShape={[3, 2]} />;Drive the morph from outside — e.g. synced to a scroll scrubber or a slider:
const [progress, setProgress] = useState(0);
<ReshapeExplorer
inputShape={[2, 3]}
outputShape={[3, 2]}
progress={progress}
onProgressChange={setProgress}
/>;Higher-rank shapes collapse for rendering — the last axis becomes columns, every leading axis is multiplied into rows:
// (2, 3, 4) tensor renders as a 6×4 grid; reshape collapses batch + seq.
<ReshapeExplorer inputShape={[2, 3, 4]} outputShape={[6, 4]} defaultProgress={1} />Understanding the component
- Two panels, one identity. The component renders an input panel and an output panel side-by-side, each showing the target shape as a labelled placeholder. A single layer of morphing cells sits over both panels — one cell per flat index, painted in
--cb-accent, labelled with its index whenshowLabelsis on. - Row-major mapping. Cell
isits at(⌊i / inputCols⌋, i mod inputCols)in the input grid and at(⌊i / outputCols⌋, i mod outputCols)in the output grid. Asprogressadvances0 → 1, each cell's(x, y)is a linear interpolation between those two positions — element identity is preserved, so the eye can track exactly where every value lands. - Higher-rank collapse.
inputShapeandoutputShapeaccept any rank. Rank 1 renders as a single row; rank ≥ 3 multiplies every leading axis into the row count and keeps the trailing axis as columns. This matches how a higher-rank tensor reads in row-major memory — the most common mental model for reshape. - Shape-compatibility guardrail. When
product(inputShape) !== product(outputShape)the component renders an inlineshape mismatchbanner instead of misrendering. Useful as a teaching primitive — a wrong reshape is supposed to be a loud error. - Controlled or uncontrolled progress. Pass
progress+onProgressChangeto drive from outside (a slider, a scroll scrubber, a parent stepper), or leave them undefined and usedefaultProgressto seed an internal value. - Reduced-motion fallback.
prefers-reduced-motion: reduceswaps theSPRINGS.smoothmorph for aduration: 0snap so scrubbing throughprogressis jank-free.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
inputShape | readonly number[] | required | Input tensor shape. Product must match outputShape. |
outputShape | readonly number[] | required | Output tensor shape. Product must match inputShape. |
progress | number | — | Controlled morph phase in [0, 1]. Pair with onProgressChange. |
defaultProgress | number | 0 | Uncontrolled initial progress. |
onProgressChange | (progress: number) => void | — | Fires whenever progress changes. |
cellSize | number | 36 | Pixel size of each grid cell. |
cellGap | number | 4 | Pixel gap between adjacent cells. |
showLabels | boolean | true | Render the per-cell flat-index label. |
transition | Transition | SPRINGS.smooth | Spring used for the cell-position morph. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The figure is
role="figure"with anaria-labelledbyheading that announces both shapes (Reshape · [2, 3] → [3, 2]) and anaria-live="polite"summary that reports the current progress percent. - When
product(inputShape) !== product(outputShape)the component renders a visiblerole="alert"mismatch banner; screen readers announce the per-shape element counts inline. - The morph layer is marked
aria-hiddenbecause its information is redundant with the textual summary — non-sighted users get the shape transition described, not pantomimed. prefers-reduced-motion: reducereplaces theSPRINGS.smoothcell morph with an instant snap so scrubbing throughprogressnever triggers continuous animation for users who opt out.- Color is never the only signal: every cell is labelled with its row-major flat index (toggleable via
showLabels), and both panels carry textual shape captions.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/viz/ReshapeExplorer.tsx). The source was a multi-mode lesson widget bundled with theWidgetchrome, theTogglePilltranspose toggle, a four-shape selector (2×3 / 3×2 / 6×1 / 1×6), the bespoke "batch danger" mode withBATCH_COLORSand(2,3,4) → (6,4)flattening, the flat-memory readout strip, and bespoke captions per mode. The library extract is the morph primitive — arbitraryinputShape → outputShapewith controlled / uncontrolledprogress, a shape-compatibility guardrail, and reduced-motion support. The transpose / batch / readout modes belong to the source lesson and stay there.