Beck
Docs /Flow & animation

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).
yaml
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.

yaml
- 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.

Dot dot

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: true
Circle circle

A 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: true
Ring ring

A 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: true

Eases

An ease only reveals itself in motion, so each one runs live — same edge, same speed, different curve.

Linear linear

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: true
Smooth smooth

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: smooth, size: 9, speed: 180, glow: true }
    - wait: 0.4
    - reset: true
Accelerate accelerate

Start 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: true
Decelerate decelerate

Start 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: true
Expo expo

Exponential 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: true
Sine sine

Gentle 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: true
Steps steps

Discrete 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: true
Bounce bounce

Decelerating 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: true

Per-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.

Data data

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 } ]
Control control

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 } ]
Async async

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 } ]
Dependency dependency

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.