Flow & animation
A diagram's animation is a flow — an ordered list of steps the engine compiles into a timeline and
plays when the diagram scrolls into view. When no flow is authored, Beck derives one from the
edges. For a task-oriented walkthrough, see Animate the flow.
Animation is skipped entirely — and the motion runtime never loads — when meta.animate is false
or the reader prefers reduced motion. See reduced motion.
The flow block
| key | type | default | description |
|---|---|---|---|
repeat |
number | -1 |
-1 loops forever; 0 plays once. meta.loop: false forces 0. |
repeatDelay |
number (s) | 1.5 |
Pause between repeats. |
steps |
list | — | Ordered steps; each is a single-key mapping (see below). |
flow:
repeat: -1
repeatDelay: 1.5
steps:
- packet: { from: client, to: api, label: GET /item }
- working: { node: db }
- packet: { from: api, to: db, color: info }
- idle: { node: db }
- packet: { from: db, to: api, color: success }
- wait: 1
Derived flow
With no flow block, Beck topologically sorts the nodes (roots → leaves), emits a phase label the
first time each node sends, sends a packet along every edge in order, then waits one second and
resets — looping forever. This is why nodes and edges alone already animate.
Steps
Each step is a mapping with a single key naming its type. from/to/node values must reference a
declared node id (or, for edge endpoints, a group id).
| step | fields | effect |
|---|---|---|
packet |
from, to, via?, color?, label?, + knobs |
One dot travels the edge (or multi-hop chain). |
burst |
from, to (id or list), via?, count, stagger, color?, label?, + knobs |
count waves; each broadcasts a dot to every target. |
status |
node, text, color? |
Set the node's status pill (persists until changed or reset). |
highlight |
node, color? |
A brief scale-and-glow flourish. |
pulse |
node, color? |
A scale bump with an expanding ripple ring. |
activate |
from, to, color? |
Persistently recolour an edge and its arrowhead until reset. |
stream |
from, to, color? |
Continuous flowing dashes along an edge until reset. |
working |
node, color? |
Leave a node visibly busy (breathing glow) until idle or reset. |
idle |
node |
Clear a node's working state. |
fail |
node, text?, color? |
A red shake and flash, with optional status text. |
phase |
<label> (string) |
A named label the handle's seek(label) can jump to. |
wait |
<seconds> (number) |
Pause. Default 0.5. |
reset |
— | Restore the initial state (clears trails, streams, working, recolours, pills). |
parallel |
[ <steps> ] |
Run the listed steps simultaneously. |
burst clamps count to 1–24 (default 3) and defaults stagger to 0.12 seconds. status,
working, stream, and activate persist until a later step or reset clears them; everything
else is a one-shot beat.
- status: { node: scan, text: FAILED, color: danger }
- fail: { node: scan, text: vulnerable }
- parallel:
- burst: { from: alert, to: [oncall, slack, email], count: 4 }
- pulse: { node: alert }
- phase: notified
- wait: 1
- reset:
Packet knobs
packet and burst share these. Each unset knob falls back to the traversed edge kind's default
(see per-edge-kind motion), then to an engine constant.
| knob | type | default | description |
|---|---|---|---|
shape |
dot circle ring |
dot |
Packet form. See shapes. |
size |
number (px) | edge kind | Dot radius. |
speed |
number (px/s) | edge kind | Travel speed. |
glow |
bool | edge kind | Soft glow around the dot. |
impact |
bool | false |
Emit an expanding ring at the destination on arrival. |
ease |
enum | edge kind | Easing of the travel. See eases. |
via |
list of ids | — | Waypoints — the packet chains through each in turn. A group waypoint pulses its members on arrival. |
color |
token or colour | --beck-packet |
Dot and trail colour. |
label |
string | — | Text that rides above the dot (only the first dot of a burst is labelled). |
A travelling packet draws a colour trail in lockstep with its easing and pulses the target node on arrival.
Packet shapes
Each shape travels below so you can read it in motion. An explicit size always overrides a shape's
baseline radius.
A small glowing dot (default).
meta: { direction: LR }
nodes: [ { id: a, title: Source }, { id: b, title: Sink } ]
edges: [ { from: a, to: b } ]
flow:
repeat: -1
repeatDelay: 0.5
steps:
- packet: { from: a, to: b, shape: dot, speed: 200, impact: true }
- wait: 0.4
- reset: trueA larger filled disc.
meta: { direction: LR }
nodes: [ { id: a, title: Source }, { id: b, title: Sink } ]
edges: [ { from: a, to: b } ]
flow:
repeat: -1
repeatDelay: 0.5
steps:
- packet: { from: a, to: b, shape: circle, speed: 200, impact: true }
- wait: 0.4
- reset: trueA hollow stroked circle.
meta: { direction: LR }
nodes: [ { id: a, title: Source }, { id: b, title: Sink } ]
edges: [ { from: a, to: b } ]
flow:
repeat: -1
repeatDelay: 0.5
steps:
- packet: { from: a, to: b, shape: ring, speed: 200, impact: true }
- wait: 0.4
- reset: trueEases
An ease only reveals itself in motion, so each one runs live — same edge, same speed, different curve.
Constant speed (default).
meta: { direction: LR }
nodes: [ { id: a, title: Start }, { id: b, title: End } ]
edges: [ { from: a, to: b } ]
flow:
repeat: -1
repeatDelay: 0.5
steps:
- packet: { from: a, to: b, ease: linear, size: 9, speed: 180, glow: true }
- wait: 0.4
- reset: trueEase in and out.
meta: { direction: LR }
nodes: [ { id: a, title: Start }, { id: b, title: End } ]
edges: [ { from: a, to: b } ]
flow:
repeat: -1
repeatDelay: 0.5
steps:
- packet: { from: a, to: b, ease: smooth, size: 9, speed: 180, glow: true }
- wait: 0.4
- reset: trueStart slow, then speed up.
meta: { direction: LR }
nodes: [ { id: a, title: Start }, { id: b, title: End } ]
edges: [ { from: a, to: b } ]
flow:
repeat: -1
repeatDelay: 0.5
steps:
- packet: { from: a, to: b, ease: accelerate, size: 9, speed: 180, glow: true }
- wait: 0.4
- reset: trueStart fast, then slow down.
meta: { direction: LR }
nodes: [ { id: a, title: Start }, { id: b, title: End } ]
edges: [ { from: a, to: b } ]
flow:
repeat: -1
repeatDelay: 0.5
steps:
- packet: { from: a, to: b, ease: decelerate, size: 9, speed: 180, glow: true }
- wait: 0.4
- reset: trueExponential ease in and out.
meta: { direction: LR }
nodes: [ { id: a, title: Start }, { id: b, title: End } ]
edges: [ { from: a, to: b } ]
flow:
repeat: -1
repeatDelay: 0.5
steps:
- packet: { from: a, to: b, ease: expo, size: 9, speed: 180, glow: true }
- wait: 0.4
- reset: trueGentle sine ease in and out.
meta: { direction: LR }
nodes: [ { id: a, title: Start }, { id: b, title: End } ]
edges: [ { from: a, to: b } ]
flow:
repeat: -1
repeatDelay: 0.5
steps:
- packet: { from: a, to: b, ease: sine, size: 9, speed: 180, glow: true }
- wait: 0.4
- reset: trueDiscrete stepping — a digital tick.
meta: { direction: LR }
nodes: [ { id: a, title: Start }, { id: b, title: End } ]
edges: [ { from: a, to: b } ]
flow:
repeat: -1
repeatDelay: 0.5
steps:
- packet: { from: a, to: b, ease: steps, size: 9, speed: 180, glow: true }
- wait: 0.4
- reset: trueDecelerating bounce on arrival.
meta: { direction: LR }
nodes: [ { id: a, title: Start }, { id: b, title: End } ]
edges: [ { from: a, to: b } ]
flow:
repeat: -1
repeatDelay: 0.5
steps:
- packet: { from: a, to: b, ease: bounce, size: 9, speed: 180, glow: true }
- wait: 0.4
- reset: truePer-edge-kind motion
When a packet's knobs are unset, it inherits the motion of the edge kind it travels — so data,
control, async, and dependency packets read differently with zero authoring. Explicit knobs
always win. Each card below sends a default packet along an edge of that kind, so the difference in
size, speed, glow, and easing is something you watch rather than read off a table.
A data flow — the default. Solid line; a medium, steady packet.
meta: { direction: LR }
nodes: [ { id: a, title: A }, { id: b, title: B } ]
edges: [ { from: a, to: b, kind: data } ]A control signal. Solid line; a small, fast, accelerating packet.
meta: { direction: LR }
nodes: [ { id: a, title: A }, { id: b, title: B } ]
edges: [ { from: a, to: b, kind: control } ]An asynchronous message. Dashed line; a large, slow, eased packet.
meta: { direction: LR }
nodes: [ { id: a, title: A }, { id: b, title: B } ]
edges: [ { from: a, to: b, kind: async } ]A dependency relationship. Dashed neutral line; a small packet with no glow.
meta: { direction: LR }
nodes: [ { id: a, title: A }, { id: b, title: B } ]
edges: [ { from: a, to: b, kind: dependency } ]Reduced motion
If meta.animate is false, a <beck-diagram> carries animate="false", or the reader's system
requests reduced motion, Beck renders the static frame and never loads its animation runtime (GSAP,
fetched from a CDN at runtime). The persistent CSS-driven effects (working, stream, status and
icon tints) consume theme variables directly, so they survive a theme change and keep running even
while the timeline is paused.
To drive playback yourself — play, pause, seek to a phase label — render with
window.Beck.renderDiagram and use the returned handle; see the API reference.