Reduction Tree Animator

A binary-tree reduction visualisation tuned for teaching parallel sum and tree all-reduce. N workers sit at the leaves; at each level, adjacent pairs combine into one value, halving the active worker count: N → N/2 → N/4 → … → 1. After log₂ N levels every worker's contribution has been folded into the root. Where RingAllReduceViz shows the ring-topology variant used by NCCL, this is the tree variant — use it in lessons that introduce reduction as a divide-and-conquer pattern before discussing ring-based scheduling.

Reduction tree. 8 leaves, log₂ 8 = 3 levels.
Reduction treelevel 00 / 03
6.188.642.881.917.927.341.483.57leaveslevel 1level 2root
Customize
Tree
8
0
Playback
800ms

Installation

npx shadcn@latest add https://craftbits.dev/r/reduction-tree-animator.json

Usage

import { ReductionTreeAnimator } from "@craft-bits/core";
 
<ReductionTreeAnimator numLeaves={8} />

Pass your own per-worker values:

<ReductionTreeAnimator
  numLeaves={8}
  values={[3.2, 1.5, 4.8, 0.9, 2.1, 5.3, 1.7, 3.6]}
/>

Drive playback from outside the component:

const [level, setLevel] = useState(0);
 
<ReductionTreeAnimator
  numLeaves={8}
  currentLevel={level}
  onCurrentLevelChange={setLevel}
  playing={false}
/>

Understanding the component

  1. One cursor, one wavefront. currentLevel indexes 0 … log₂(numLeaves). Level 0 shows just the leaves; level l shows every node up to depth l. Pairs freshly combined at the active level light up in --cb-accent; already-swept levels stay tinted; not-yet-swept levels stay dimmed.
  2. Edges are the wavefront. Active edges (those feeding the current level) use the bold stroke and --cb-accent colour. Already-swept edges stay at a tinted opacity so the user can see the trail; not-yet-swept edges hold a low opacity so the diagram never goes blank.
  3. Snap to power of two. numLeaves is rounded down to the nearest power of two (clamped to [2, 64]). Binary reduction is only clean on powers of two — rather than pretend otherwise, the component normalises silently.
  4. Synthesised values when shape-only. Pass numLeaves={8} without values and the component renders deterministic placeholder leaves (sine-phased). The diagram never renders blank, but you should pass real values when you have them.
  5. SPRINGS.smooth on the active edge / node. Level transitions use the smooth spring from @craft-bits/core/motion so the wavefront settles between levels rather than snapping.
  6. Reduced-motion fallback. With prefers-reduced-motion: reduce, the tree renders fully reduced on mount with no autoplay and no per-level animation.
  7. Auto layout from numLeaves alone. Every internal node sits horizontally above the midpoint of its two children; levels are evenly spaced vertically. Change numLeaves and the tree re-flows.

Props

PropTypeDefaultDescription
numLeavesnumber8Number of leaf workers. Rounded down to the nearest power of two in [2, 64].
valuesreadonly number[]Per-leaf values (length matches numLeaves). Synthesised if omitted.
currentLevelnumberControlled cursor in [0, log₂(numLeaves)]. Pair with onCurrentLevelChange.
defaultCurrentLevelnumber0Uncontrolled initial cursor.
onCurrentLevelChange(level) => voidFires on tick and on manual scrub.
playingbooleanControlled play state. Pair with onPlayingChange.
defaultPlayingbooleanfalseUncontrolled initial play state.
onPlayingChange(playing) => voidFires when play / pause flips.
playSpeednumber800Milliseconds between level advances during autoplay.
transitionTransitionSPRINGS.smoothSpring for active-edge / node transitions.
classNamestringMerged onto the root <div> via cn().

Accessibility

  • The figure is role="figure" with aria-label="Reduction tree visualization".
  • An aria-live="polite" summary announces the active level, the number of active workers, and the final root value when reduction completes.
  • The play / pause button is aria-pressed; the label flips between "Play reduction" and "Pause reduction".
  • The scrubber is a native <input type="range"> with an explicit aria-label, so keyboard arrows nudge the cursor and screen readers narrate the value.
  • Colour is never the only signal — level counter and per-node value labels are textual.
  • prefers-reduced-motion: reduce renders the tree fully reduced on mount and disables autoplay.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/viz/ReductionTreeAnimator.tsx). The source was a multi-mode lesson widget (max / sum / Welford reduction, plus a "break the barrier" pedagogical toggle, plus an embedded Explore / Predict mode strip with quiz pool and DoneCard). Stripped the predict mode, the mode strip, the Welford struct, the barrier-break demo, the narration helpers, the ChallengeBtn / TogglePill chrome, and the usePredictRounds dependency. Generalised the surviving binary-tree reduction into the public surface: a flat numLeaves / values pair with controlled / uncontrolled cursor and play state, sum-only aggregation (the cleanest teaching default), and --cb-accent semantic colour wired through SVG_TOKENS. Architecture mirrors ForwardPassAnimator (auto layout from a single shape argument, level cursor sweeping a wavefront) so the two compose cleanly in side-by-side lessons.