import { ChainReactionChart } from "semiotic/physics"
ChainReactionChart compiles a dependency graph into lanes, sockets, and delivery routes. A completed task releases one ball per outgoing dependency; a downstream task arms only once every prerequisite ball has arrived. A blocked task releases nothing, so the chart shows reach — how far one unmade decision propagates.
Completion is always an explicit data event. The simulation delivers prerequisites; it never decides that a task is done. SeeData Viz for Dummies VIfor a worked example andWhen Physics? for when motion earns its place.
Example
Approve retention policy affects 5 unfinished tasks across 4 lanes.
Approve retention policy affects 5 unfinished tasks across 4 lanes.| Task | Lane | Progress | State | Waiting on | Downstream reach |
|---|
| Approve product brief | Product | 100% | Completed | None | 6 tasks / 5 lanes |
|---|
| Finalize interaction spec | Product | 100% | Completed | None | 3 tasks / 3 lanes |
|---|
| Approve retention policy | Product | 90% | Blocked | Legal decision on 30-day event retention | 5 tasks / 4 lanes |
|---|
| Build event fixture | Data | 100% | Completed | None | 5 tasks / 4 lanes |
|---|
| Finalize event schema | Data | 25% | Waiting | retention | 4 tasks / 4 lanes |
|---|
| Implement streaming ingest | Data | 0% | Waiting | schema | 1 tasks / 1 lanes |
|---|
| Build dashboard charts | Frontend | 40% | Waiting | schema | 2 tasks / 2 lanes |
|---|
| Accessibility audit | Quality | 0% | Waiting | charts | 1 tasks / 1 lanes |
|---|
| Ship release | Launch | 0% | Waiting | audit, ingest | 0 tasks / 0 lanes |
|---|
Displacement ledger
Every physics chart owes a ledger naming what moves, why, and what state change the movement represents. Here it is for this chart:
JS
const chainReactionLedger = { source: "task rows plus an explicit dependency array per task", body: "one delivery ball per satisfied prerequisite edge", displacement: "a completed task releases one ball down each outgoing edge", barrier: "a blocked task releases nothing, so its dependents never arm", projection: "task state (completed / blocked / armed / waiting) + blocker reach", accessibleReadout: "settled task table + machine observations", }
The settled reading
mode="snapshot" derives task state directly from the data atcurrentTime — no simulation runs. That makes the settled state the authoritative reading, and it is what a reader withprefers-reduced-motion, a server render, or an exported snapshot receives. mode="replay" adds the animated delivery pass on top of the same derived state; it never changes the answer.
The accessible table carries the whole reading: each task's lane, progress, state, what it is waiting on, and its downstream reach in tasks and lanes.
Imperative handle
The ref exposes replay transport plus a preview mode for asking “what would resolving this blocker unlock?” without editing the data.
JSX
const machineRef = useRef(null) // Preview the effect of resolving a blocker. This changes mechanical // state only — it never edits the supplied data. machineRef.current.previewResolve("retention") machineRef.current.clearPreview() // Read the derived reach of any task. const { downstreamTaskCount, affectedLaneCount } = machineRef.current.getAmplification("retention") // Replay transport. machineRef.current.play() machineRef.current.settle()
Props
| Prop | Type | Required | Default | Description |
|---|
data | array | Yes | — | Task rows. Each row becomes one placed task in the dependency machine. |
taskIDAccessor | string | function | Yes | — | Stable task id. Dependency arrays reference these resolved ids. |
labelAccessor | string | function | Yes | — | Human-readable task name used in labels and the data table. |
laneAccessor | string | function | Yes | — | Workstream the task belongs to. Lanes become the chart's columns. |
dependencyAccessor | string | function | Yes | — | Array of prerequisite task ids. Edges are never inferred — a task with the wrong prerequisites reads as a different claim. |
startAccessor | string | function | — | — | Planned start time (number or Date). |
endAccessor | string | function | — | — | Planned end time (number or Date). |
progressAccessor | string | function | — | 0 | Fractional completion 0–1, shown on the task body. |
statusAccessor | string | function | — | — | Authored status (done / blocked / waiting / active). Completion is an explicit data event, never discovered by the simulation. |
completionTimeAccessor | string | function | — | — | When the task actually completed. Drives replay ordering against currentTime. |
blockerAccessor | string | function | — | — | Reason this task is blocked. A blocked task never arms its dependents. |
milestoneAccessor | string | function | — | false | Marks a task as a milestone for emphasis. |
mode | "snapshot" | "replay" | "mechanical" | — | "snapshot" | snapshot derives the settled state at currentTime without animating; replay animates deliveries forward from the earliest recorded completion; mechanical demonstrates the apparatus. |
insight | "none" | "blocker-amplification" | — | "blocker-amplification" | Which derived reading to surface. blocker-amplification reports how many unfinished tasks and lanes each blocker reaches. |
currentTime | number | Date | — | Infinity | Clock position used to derive the settled state. |
controls | boolean | array | — | false | Replay control buttons: true for all, or a subset of play / pause / step / reset / settle. |
selectedTaskIDs | array | — | — | Controlled selection. Pair with onSelectionChange. |
onSelectionChange | function | — | — | Receives the selected task ids when a task is activated. |
onObservation | function | — | — | Machine events: task-completed, dependency-delivered, task-armed, machine-stalled, blocker-previewed, machine-settled. |
reducedMotion | "settle" | — | — | Force the settled reading instead of a replay. The frame also honors prefers-reduced-motion automatically. |
mechanism | "domino-ball" | — | "domino-ball" | Delivery mechanism representing a satisfied prerequisite. |
orientation | "vertical" | — | "vertical" | Lane orientation. Lanes run as vertical columns. |
seed | number | — | 31 | Deterministic seed for delivery-ball motion. |
width | number | — | 920 | Chart width in pixels. |
height | number | — | 620 | Chart height in pixels. |
accessibleTable | boolean | — | true | Render the screen-reader task table (task, lane, progress, state, waiting on, downstream reach). |