Computation Graph Viz

Visualises a computation graph used in automatic differentiation. Nodes carry a kindinput, op, or output — and the renderer picks the shape that matches the role (rounded squares for tensor values, circles for operations). Edges encode dependencies; for an expression like z = (x + y) * sin(x) the component lays out the unfolded graph so each intermediate value sits at its topological rank.

Where DAGRenderer is the generic graph primitive (every node is a labelled circle), ComputationGraphViz is the typed-node specialisation built for teaching how autograd / backprop works at the per-node level.

x1.50y0.500+sin×z2.00
Customize
Layout
Gradients
Highlight path
none

Installation

npx shadcn@latest add https://craftbits.dev/r/computation-graph-viz.json

Usage

import { ComputationGraphViz } from "@craft-bits/core";
 
const nodes = [
  { id: "x",   kind: "input",  label: "x",   value: 1.5 },
  { id: "y",   kind: "input",  label: "y",   value: 0.5 },
  { id: "add", kind: "op",     label: "+",   value: 2 },
  { id: "sin", kind: "op",     label: "sin", value: 0.997 },
  { id: "mul", kind: "op",     label: "×",   value: 1.995 },
  { id: "z",   kind: "output", label: "z",   value: 1.995 },
] as const;
 
const edges = [
  { from: "x",   to: "add" },
  { from: "y",   to: "add" },
  { from: "x",   to: "sin" },
  { from: "add", to: "mul" },
  { from: "sin", to: "mul" },
  { from: "mul", to: "z" },
] as const;
 
<ComputationGraphViz nodes={nodes} edges={edges} />

Show backward-pass gradient labels by passing showGradients and supplying edge.label with the local derivative:

<ComputationGraphViz
  nodes={nodes}
  edges={[
    { from: "x",   to: "add", label: "1" },
    { from: "y",   to: "add", label: "1" },
    { from: "x",   to: "sin", label: "cos(x)" },
    { from: "add", to: "mul", label: "sin(x)" },
    { from: "sin", to: "mul", label: "x + y" },
    { from: "mul", to: "z",   label: "1" },
  ]}
  showGradients
/>

Highlight a specific path (e.g. the chain-rule branch from x to z through sin):

<ComputationGraphViz
  nodes={nodes}
  edges={edges}
  highlightPath={["x", "sin", "mul", "z"]}
/>

Understanding the component

  1. Typed nodes. Every node declares a kind"input", "op", or "output" — and the renderer picks the shape that matches. Values are rounded squares, operations are circles, the output is an accent-filled circle. The reader can tell at a glance which nodes are data and which are computation.
  2. Auto-layout via Kahn's algorithm. Each render computes one topological rank per node (longest-path layering) so dependencies always flow forward. Nodes at the same rank share a coordinate on the layout axis. Cycles throw with the unresolved ids in the message — autograd graphs are acyclic by contract.
  3. Forward values + backward gradients. When a node has a value, it's drawn below the node in tabular-numbers. When showGradients is on and a node has a gradient, it appears in the warning color near the node corner. Per-edge label strings (typically the local derivative) render as italic warning-color labels at the edge mid-point — the same treatment as backward-edge labels in BackpropFlow.
  4. Path highlighting. Pass highlightPath as a sequence of node ids; the edges connecting consecutive entries pick up the accent color and the matching nodes gain an accent ring. Use it to narrate a single branch of the chain rule.
  5. Same primitives as DAGRenderer. Arrowheads come from the shared SvgDefs block; the topological-layout maths is the same shape; SPRINGS.smooth drives node entry; per-rank stagger uses STAGGER from @craft-bits/core/motion. Reduced-motion users snap to the final state.

Props

PropTypeDefaultDescription
nodesreadonly { id; kind; label; value?; gradient? }[]requiredGraph nodes. kind is "input", "op", or "output".
edgesreadonly { from; to; label? }[]requiredDirected dependency edges. Must be acyclic.
showGradientsbooleanfalseDraw per-node gradients and per-edge labels in the warning color.
highlightPathreadonly string[]Sequence of node ids forming an active path.
direction"left-right" | "top-down""left-right"Layout axis.
classNamestringMerged onto the outer <div> via cn().

Accessibility

  • The figure is role="figure" with aria-label="Computation graph visualization".
  • The inner <svg> is role="img" with an aria-label summarizing the nodes ("Computation graph with 6 nodes: x, y, +, sin, ×, z.").
  • Color is never the only signal — node shape (square vs circle) and the inner label distinguish data nodes from operation nodes, and value / gradient text appears beside each node.
  • Motion respects prefers-reduced-motion: node entries, edge draws, and the rank-stagger reveal all collapse to instant when the user has opted out.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/viz/ComputationGraphViz.tsx). The original was tightly coupled to the project's streaming-data props — it parsed computation_graph data from a latest history array, ran a custom DFS-driven layout per render, and rendered two bespoke modes (forward / backward) with a built-in play/pause/step/reset transport plus a hand-tuned speed select. The library version drops the streaming-data parser, the imperative play transport, and the two-mode split. Instead it accepts a plain nodes / edges pair, reuses the canonical Kahn's-algorithm layout (matching DAGRenderer), and exposes showGradients + highlightPath as declarative props so callers compose forward / backward / chain-rule narratives by passing different data — no internal state.