LSTM Gate Viz
A single LSTM time-step laid bare. Four circular meters — forget, input, output, candidate — feed the cell-state update C_t = f · C_{t-1} + i · g, with the resulting C_t rendered as a signed bar and the hidden state h_t = o · tanh(C_t) written underneath. No time axis, no narration: this is the per-step primitive that teaches what each gate does to the cell state.
LSTM cell update. Forget 0.70, input 0.40, output 0.60, candidate 0.80. Previous cell 1.00, new cell 1.02, hidden 0.46.
LSTM gates · one stepCt-1 = 1.00
forget
input
candidate
output
cell stateCt = 1.020
-30+3
Ct=0.700·1.000+0.400·0.800=1.020
ht=0.600·tanh(1.020)=0.462
0.70
0.40
0.60
0.80
Customize
Gates
0.70
0.40
0.60
0.80
Inputs
1.00
Layout
Installation
npx shadcn@latest add https://craftbits.dev/r/lstm-gate-viz.jsonUsage
import { LSTMGateViz } from "@craft-bits/core";
<LSTMGateViz
defaultForgetGate={0.7}
defaultInputGate={0.4}
defaultOutputGate={0.6}
defaultCandidate={0.8}
cellState={1}
/>;Drive every gate from outside the component:
const [forget, setForget] = useState(0.5);
const [input, setInput] = useState(0.5);
const [output, setOutput] = useState(0.5);
const [candidate, setCandidate] = useState(0.5);
<LSTMGateViz
forgetGate={forget}
onForgetGateChange={setForget}
inputGate={input}
onInputGateChange={setInput}
outputGate={output}
onOutputGateChange={setOutput}
candidate={candidate}
onCandidateChange={setCandidate}
cellState={1}
/>;Read-only embed (no sliders, no formulas):
<LSTMGateViz
forgetGate={0.95}
inputGate={0.05}
outputGate={0.6}
candidate={0.4}
cellState={2}
showFormulas={false}
showControls={false}
/>;Understanding the component
- Four meters, one update. Each meter is a three-quarter arc that fills from the lower-left as the value rises. The forget meter paints
--cb-warning, input paints--cb-success, output paints--cb-accent, and candidate paints--cb-info. The numeric value sits in the centre so it reads even on a small screen. - Candidate is bipolar. Unlike the three sigmoid gates which live in
[0, 1], the candidate istanh-bounded — values can be negative. The arc fills from|candidate|so the picture stays legible at both ends, and the numeric readout carries the sign. - Cell-state bar. The horizontal bar below the meters is centred on zero and ranges over
±3for display. The translucent grey block isC_{t-1}; the coloured block on top is the newC_t = f · C_{t-1} + i · g. Each block grows leftward for negative values, rightward for positive — so the sign is visible at a glance without reading the number. - Formulas. The two equations beneath the bar print the plain arithmetic each gate contributes:
C_t = f · C_{t-1} + i · gandh_t = o · tanh(C_t). Hide them withshowFormulas={false}for inline embeds. - Controlled + uncontrolled per gate. Every meter exposes the Radix
value/defaultValuepair (forgetGate/defaultForgetGate+onForgetGateChange, and the same shape for input / output / candidate). Mix-and-match — drive one gate externally while letting the others own their state. SPRINGS.smoothon every transition. Meter arcs, the cell-state bar, and the previous-state block all rideSPRINGS.smoothfrom@craft-bits/core/motion.prefers-reduced-motion: reducecollapses everything to an instant swap.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
forgetGate | number | — | Controlled forget gate in [0, 1]. Pair with onForgetGateChange. |
defaultForgetGate | number | 0.5 | Uncontrolled initial forget gate. |
onForgetGateChange | (v) => void | — | Fires when the forget gate changes. |
inputGate | number | — | Controlled input gate in [0, 1]. Pair with onInputGateChange. |
defaultInputGate | number | 0.5 | Uncontrolled initial input gate. |
onInputGateChange | (v) => void | — | Fires when the input gate changes. |
outputGate | number | — | Controlled output gate in [0, 1]. Pair with onOutputGateChange. |
defaultOutputGate | number | 0.5 | Uncontrolled initial output gate. |
onOutputGateChange | (v) => void | — | Fires when the output gate changes. |
candidate | number | — | Controlled candidate in [-1, 1]. Pair with onCandidateChange. |
defaultCandidate | number | 0.5 | Uncontrolled initial candidate. |
onCandidateChange | (v) => void | — | Fires when the candidate changes. |
cellState | number | 1 | Previous cell state C_{t-1} used in the update. |
showFormulas | boolean | true | Render the inline C_t = ... and h_t = ... formulas. |
showControls | boolean | true | Render the four sliders below the meters. |
transition | Transition | SPRINGS.smooth | Spring used for meter / bar transitions. |
className | string | — | Merged onto the root <div> via cn(). |
Accessibility
- The whole figure is
role="figure"witharia-labelledbynaming "LSTM gates · one step" and anaria-describedbyaria-live="polite"summary that announces every gate value,C_{t-1},C_t, andh_twhenever any input changes. - Each meter SVG carries
role="img"with a descriptivearia-labelthat names the gate and its current value. - Colour is never the only signal — every meter labels its numeric value in the centre, and the cell-state bar carries a
data-state="positive" | "negative"attribute on theC_treadout for screen-reader hooks. - The sliders are
LabeledSliders — native<input type="range">underneath — so arrow keys nudge the value and screen readers narrate it. prefers-reduced-motion: reducecollapses every meter / bar transition to an instant swap.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/viz/LSTMGateViz.tsx). The source was a sequencedWidget-hosted Explore / Predict / Challenge lesson driven byModeStrip,ChallengeBtn,FeedbackBadge,LabeledSlider, anduseWidgetHistory(undo / redo / bookmark presets like "Remember everything" / "Forget everything" / "Strong input" / "Read only"), with the gates rendered as a 640×200 SVG pipeline of three coloured rectangles whose internal fill rose and fell as the user dragged sigmoid biases throughLCG-seeded predict / challenge setups. The library version drops the Widget chrome, the predict / challenge state machines, the random-seed problem generator, the bias-and-tanh slider wiring, the bookmark presets, the eyebrow / premise / caption / formula bar, and the source's raw CSS variables. Reframed as the per-step primitive every Recurrent lesson actually needs: four circular meters (forget,input,output,candidate) over a signed cell-state bar with theC_t = f · C_{t-1} + i · gandh_t = o · tanh(C_t)equations written out. Switched all colour to--cb-*semantic tokens, replaced inline springs withSPRINGS.smoothfrom@craft-bits/core/motion, and exposed Radix controlled / uncontrolled pairs for all four gates plus thecellStateinput. Opens ML Viz → Recurrent as the first member of the subsection.