Animation primitives
The four motion building blocks remaid ships in v0.1, with the visual semantics each one is meant to communicate.
Animation in remaid is composed from a small number of primitives. They were chosen because each one corresponds to a specific kind of system story you would want to tell: data in flight, arrival, fan-out, recovery. Layered together, they cover the majority of architecture diagrams in tech writing.
packet-flow
A small dot travels from the source node to the target node along the edge. The visual statement is “a piece of data is in transit.” This is the most common primitive and the one to reach for first.
diagram "packet-flow" {
node a { kind: client }
node b { kind: service }
edge req: a -> b { label: "GET /users" }
scene s {
loopEvery: 1.5s
packet-flow req { speed: 700ms }
}
}Syntax
packet-flow <edge-id> [{ speed: <duration>, after: <edge-id>, delay: <duration> }]pulse
A ring expands outward from the target node and fades. The visual statement is “something just arrived here.” Use it as a beat after a packet-flowto communicate “and the cache responded.”
diagram "pulse" {
node a { kind: service, label: "API" }
node b { kind: cache, label: "Redis" }
edge get: a -> b { label: "GET" }
scene s {
loopEvery: 1500ms
packet-flow get { speed: 400ms }
pulse get { speed: 500ms, after: get }
}
}Syntax
pulse <edge-id> [{ speed: <duration>, after: <edge-id>, delay: <duration> }]Note: pulsetakes an edge id, not a node id, and the ring is drawn at that edge's target node. This keeps the syntax consistent with every other primitive.
replication-sync
Multiple packets fan out across all listed edges in lockstep. The visual statement is “this data goes to every destination simultaneously.” Most useful for replication, broadcast, and publish-subscribe scenarios.
diagram "replication-sync" {
direction: down
node primary { kind: database, label: "Primary" }
node replicaA { kind: database, label: "Replica A" }
node replicaB { kind: database, label: "Replica B" }
edge wal_a: primary -> replicaA { label: "WAL" }
edge wal_b: primary -> replicaB { label: "WAL" }
scene s {
loopEvery: 2s
replication-sync wal_a, wal_b { speed: 600ms }
}
}Syntax
replication-sync <edge-id>, <edge-id> [, <edge-id>...] [{ speed: ... }]failover
Visualises a path failing and a backup taking over. Takes exactly two edge ids: the failing path (rendered as a rose-tinted pulse on its target) and the recovery path (a packet flow on the second edge). Both plays inside the step's duration.
diagram "failover" {
node api { kind: service, label: "API" }
node primary { kind: database, label: "Primary" }
node standby { kind: database, label: "Standby" }
edge toPrimary: api -> primary
edge toStandby: api -> standby
scene s {
loopEvery: 3.5s
packet-flow toPrimary { speed: 500ms }
failover toPrimary, toStandby { speed: 1s, after: toPrimary }
packet-flow toStandby { speed: 500ms, after: toStandby }
}
}Syntax
failover <failed-edge>, <recovery-edge> [{ speed: ... }]Chaining steps with `after`
after: <edge-id> chains a step to the most recent step that animated that edge in the same scene. This is how you build sequential narratives: request → query → response.
diagram "chained" {
node web { kind: client }
node api { kind: service }
node db { kind: database }
edge req: web -> api
edge query: api -> db
edge ack: api -> web
scene s {
loopEvery: 3s
packet-flow req { speed: 500ms }
packet-flow query { speed: 700ms, after: req }
packet-flow ack { speed: 400ms, after: query }
}
}Running steps in parallel
Steps without an after property all start at scene t=0. Use this when two things should happen simultaneously: a write fanning out to a database and an event queue, for example.
diagram "parallel fan-out" {
node api { kind: service }
node db { kind: database }
node mq { kind: queue, label: "Events" }
edge persist: api -> db
edge publish: api -> mq
scene s {
loopEvery: 2s
packet-flow persist { speed: 600ms }
packet-flow publish { speed: 600ms }
}
}Choosing durations
Durations are arbitrary, but a few rules of thumb help diagrams read well:
300-500msfor short hops (cache lookups, in-process calls).600-900msfor typical network requests.1-2sfor slow paths or to emphasise weight (cold-start, retry, long-running query).- Set
loopEveryto roughly 1.5x the natural duration so the scene has a moment of stillness before repeating.