Sequence Unroll
The diagram every recurrent-network lesson opens with. The same RNN cell drawn numSteps times — left-to-right — with hidden-state arrows flowing between cells, input arrows feeding each cell from below, and output arrows leaving above. The active step paints with the accent colour; earlier steps stay accented at lower opacity so the trace reads as a walk; later steps stay muted. Pair with LSTMGateViz — that one is the per-step primitive; this one is the temporal primitive.
Unrolled RNN with 6 timesteps. Active step 1 of 6.
RNN unroll · 1 / 6paused
Customize
Shape
6 steps
step 1
Playback
700 ms
Display
Installation
npx shadcn@latest add https://craftbits.dev/r/sequence-unroll.jsonUsage
import { SequenceUnroll } from "@craft-bits/viz/sequence-unroll";
<SequenceUnroll numSteps={6} defaultCurrentStep={0} />;Drive the step externally and autoplay it from outside:
const [step, setStep] = useState(0);
const [playing, setPlaying] = useState(false);
<SequenceUnroll
numSteps={6}
currentStep={step}
onCurrentStepChange={setStep}
playing={playing}
onPlayingChange={setPlaying}
playSpeed={500}
/>;Read-only skeleton — no header, no active step:
<SequenceUnroll numSteps={4} defaultCurrentStep={-1} showHeader={false} />Understanding the component
- One cell, drawn many times. The same RNN cell appears
numStepstimes across the row. Each cell is the same recurrent function — the visual is unrolled, not stacked.t=1, t=2, …underneath each box reminds the reader that time, not depth, is the axis. - Hidden-state arrows. Between every pair of cells sits a horizontal arrow representing the hidden state being passed forward. A short dashed stub on the left labelled
h₀feeds the first cell — the conventional zero initial state. Once the active step crosses an arrow it switches to--cb-accentand full opacity; arrows past the active step stay at--cb-border-strong. - Inputs in, outputs out. Each cell carries a vertical input arrow rising into it from below (
x₁, x₂, …) and a vertical output arrow leaving above (y₁, y₂, …). They mirror the standard textbook unroll: input below, output above, recurrence left-to-right. - Active step highlight. When
currentStepist, celltpaints with the accent muted fill and an accent border. Cells at indices< tkeep the accent border but drop to the elevated surface fill, so the row reads as this is where we are, this is what already fired. Cells at indices> tpaint with the muted surface and the default border. - Controlled + uncontrolled, twice over. Both
currentStepandplayingfollow the Radix pattern: passcurrentStepto control externally (pair withonCurrentStepChange) ordefaultCurrentStepto let the component own it. Same forplaying/defaultPlaying/onPlayingChange. Autoplay is asetIntervaltorn down on pause, unmount, or reaching the last step. SPRINGS.smootheverywhere. Highlight fades, arrow opacity changes, and cell-fill swaps all rideSPRINGS.smoothfrom@craft-bits/core/motion.prefers-reduced-motion: reduceparks the active step at the last index and collapses every transition to an instant swap.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
numSteps | number | 6 | How many timesteps to render. Reads best at 4–8. |
currentStep | number | — | Controlled active step in [-1, numSteps - 1]. -1 = skeleton. Pair with onCurrentStepChange. |
defaultCurrentStep | number | -1 | Uncontrolled initial active step. |
onCurrentStepChange | (step) => void | — | Fires when the active step changes (autoplay or external). |
playing | boolean | — | Controlled autoplay flag. Pair with onPlayingChange. |
defaultPlaying | boolean | false | Uncontrolled initial autoplay flag. |
onPlayingChange | (playing) => void | — | Fires when autoplay starts, pauses, or hits the end. |
playSpeed | number | 700 | Milliseconds between auto-advanced steps. Minimum 120ms. |
showHeader | boolean | true | Render the step k / N eyebrow above the diagram. |
transition | Transition | SPRINGS.smooth | Spring for highlight / arrow transitions. |
className | string | — | Merged onto the root <div> via cn(). |
Accessibility
- The whole figure is
role="figure"witharia-labelledbynaming "RNN unroll · k / N" and anaria-describedbyaria-live="polite"summary that announces the active step whenever it changes. - The SVG carries
role="img"and isaria-labelledbythe same heading id. - Colour is never the only signal — the active step also gets a thicker stroke and the eyebrow restates
step k / Nnumerically. data-state="playing" | "paused"anddata-step={k}are exposed on the root for screen-reader hooks and CSS overrides.prefers-reduced-motion: reducecollapses every transition to an instant swap and parks the active step at the last index so the picture reads as the final unrolled state rather than animating.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/viz/SequenceUnroll.tsx). The source was aWidget-hosted Explore / Predict lesson driven byModeStrip,ChallengeBtn,FeedbackBadge,usePredictRounds, anduseWidgetHistory— six hard-coded predict scenarios abouttanhsaturation / memory decay / sign-flip, an explore mode withW_xh,W_hh,bias, and four-element input sliders, a setTimeout autoplay, plain-English narration that switched between five rule branches, an SVG that painted hidden-state badges using a customoklchscale and rendered the trajectory as a translucent polyline, and the source's--color-accent-*/--color-ink-*/--color-surface-*CSS variables. The library version drops the Widget chrome, the predict / challenge state machines, the narration generator, the four-element fixed input array, the weight sliders, the bookmark presets, the hidden-state badges and trajectory polyline, theuseWidgetHistoryundo / redo stack, and every project-specific CSS variable. Reframed as the temporal primitive every Recurrent lesson needs: a row of identical RNN cells with hidden-state arrows between them, anh₀stub on the left, vertical input / output arrows on each cell, and a controlled / uncontrolledcurrentStep+playingpair with optional autoplay. Switched all colour to--cb-*semantic tokens, replaced inline springs withSPRINGS.smoothfrom@craft-bits/core/motion, and exposednumStepsso the same component handles 4-step and 8-step unrolls without forking the SVG geometry. Sits next toLSTMGateVizin ML Viz → Recurrent as the time-axis counterpart to the per-step primitive.