Flow diagram
Turns a boxes-and-arrows sketch of a system into an animated canvas diagram whose pulses run the loop in the order the real code does, then records and verifies it. Flow diagram is one of the Claude Code skills I wrote for building this site, with the site-specific values turned into placeholders and examples so it works on any project. The skill is the SKILL.md below, exactly as it ships in the zip.
Install
unzip flow-diagram.zip -d ~/.claude/skillsThat installs it for every project. For one project, unzip into that project’s .claude/skills/ instead. Either way, Claude Code reads the folder on its next session and applies the skill when a request matches its description:
Build an animated architecture, pipeline, request-flow or data-flow diagram for any React or Next.js site, driven by a small spec and a bundled canvas engine, with hairline boxes, quiet wires and accent-coloured pulses that run the loop in the order the real system does. Use this whenever someone asks for a diagram of how a system, server, device, pipeline, sync, deploy, webhook or request loop works, wants "red dots" or pulses moving between boxes, wants an animated version of a boxes-and-arrows sketch for a post, docs page or case study, or asks to change, re-time, verify or record an existing FlowMotion diagram, even if they only say "diagram" or "show how it works".
Inside the zip
- —
flow-diagram/SKILL.md· 8 KB - —
flow-diagram/assets/flow-motion.tsx· 27 KB - —
flow-diagram/references/language.md· 4 KB - —
flow-diagram/references/spec.md· 10 KB - —
flow-diagram/scripts/record-loop.sh· 3 KB - —
flow-diagram/scripts/timeline.py· 3 KB - —
flow-diagram/scripts/zoom.py· 1 KB
The skill
Animated diagrams built with this skill are specs for one engine, FlowMotion, so every
diagram on a site shares the same drawing and the same rules: every node the same hairline
box, wires with no arrowheads, the accent spent only on what is happening now. Your job is
to write a truthful spec, wire it into a page, and verify it the way a careful reviewer
would: zoomed in, frame by frame, and as a recorded loop they can watch.
0. Install the engine once
The engine is assets/flow-motion.tsx in this skill: one client component, about 500
lines, React 18 or later, no other dependencies and no utility framework (its few styles
are inlined). Copy it into the project’s components folder, for example
src/components/motion/flow-motion.tsx, and leave it unmodified apart from the two
tables described below.
Colours and the mono font are read live from five CSS custom properties on the wrapper:
--flow-fg, --flow-border, --flow-bg, --flow-accent, --flow-mono. Map them to the
site’s own tokens once in global CSS, so dark mode and theme switches come free:
:root {
--flow-fg: var(--color-fg);
--flow-border: var(--color-border);
--flow-bg: var(--color-bg);
--flow-accent: var(--color-accent);
--flow-mono: var(--font-mono);
}Or pass them per diagram through the tokens prop, as literals or var() references.
Anything unset falls back to the DEFAULTS in the file (near-black, light grey, white, a
red). Find the site’s real token names first; do not guess them.
1. Learn the real sequence before drawing anything
The dots are not decoration. A reader takes the loop as a claim about how the system works, and the first thing a reviewer who knows the system will reject is a loop that runs in the wrong order (a diagram that opened with a side widget’s push was sent back because the widget does not start the process). So before writing beats, read the code that runs the thing: the request handler, the cron, the firmware’s call order, the CI config. Write the sequence down as numbered steps with who initiates each one, and decide what to fold away. Folding is allowed when the honest version misleads (two responses in a row read as the image arriving twice), but say so in a comment in the spec.
Distinguish pushes from pulls. A pull is a round trip: the puller’s pulse goes out and comes back, and the far node flashes as it is read. A push is a one-way pulse that lands. Things that happen on their own clock (a widget posting every 15 minutes) go in the loop’s rest period, after the main sequence, so they never read as the trigger.
2. Write the spec
Read references/spec.md for the FlowSpec type and two worked examples (a dashboard’s
request loop and a deploy pipeline). In short: nodes with a lowercase mono label, a mark
and a column; wires between node ids (direction does not matter, pulses can run either
way); beats with loop-fraction windows.
Marks. Two ship with the engine: vercel (a triangle drawn as geometry) and device (a
12-pixel bitmap of a small e-paper frame). Everything else you add:
- A brand goes into the engine’s
BRANDtable as a filled path on a 24-grid plus an optical size ({ path: 'M12 ...', size: 11 }). simple-icons (simpleicons.org) is the place to find the path; check the brand’s guidelines before shipping it. Optical size, not bounding box: a chunky square mark sits at 9 in the 12 slot, a round disk at 11, a sparse starburst at 12. Judge against the other marks in a zoomed crop. - A piece of hardware is a 12-row bitmap of
#and.passed throughbitmapMark(), measured off a product photo so the details (screen, grille, button, lid) sit where they really are. Pass it as the node’smarkstraight from the spec file, or add it to the engine’sDRAWNtable to give it a name. - Never use an outline icon next to the filled marks: one family, all filled.
- Actors that are neither a brand nor a device you have a bitmap for (a person pushing, a
build step) are fine as label-only boxes; leave
markoff rather than inventing an icon.
Timing: pulses should cross wires at roughly the same speed everywhere, so give longer
wires longer windows. Leave a rest at the end of the loop. The e-paper refresh is about 7.4
seconds long; a loop that ends with it needs a period around 18 seconds, and a guard like
the one in the examples (LAND + refreshSeconds() / PERIOD must stay below the next beat).
Put the spec in a 'use client' file next to the project’s other media components and
export a wrapper component that renders <FlowMotion spec={...} />. For an MDX post,
register the wrapper in the MDX component map inside the layout’s wide-media container and
place the tag in the MDX; for a page component, import the wrapper directly.
The loop starts at its first beat whenever the canvas first runs and whenever a
flow-restart event is dispatched on it, so a reader scrolling to the diagram meets the
story from the top. Order the beats so the first one is the true trigger. Choose still
(the frame shown under reduced motion) at a moment with one pulse mid-wire.
3. Verify like a reviewer
Run the project’s typecheck first (npx tsc --noEmit). Then, with the page served (a dev
server is fine), use the scripts in scripts/. They need agent-browser, ffmpeg and
Python 3 with Pillow; each prints its usage with --help.
scripts/record-loop.sh <page url> "<start of aria-label>" <out.mp4> <period>records one loop with agent-browser, rewinds the canvas as it scrolls into view, crops the video to the drawing and writes the MP4 plus aframes/folder of 1x PNGs.scripts/timeline.py frames/ --accent '#c22'prints one character per tenth of a second for where the accent appears (left, middle, right third of the canvas) and the colour state of a probe point, so you can read the beat order off the recording instead of trusting the spec. Pass your accent colour; the default probe sits just inside the rightmost box, which suits a single device in the last column, and--probe x,ymoves it.scripts/zoom.py <screenshot.png> <x,y,w,h> zoom.pngmakes a 3x nearest-neighbour crop for checking marks and box edges. Look at it. Box edges must be one pixel wide, marks must read as the same weight, the bitmap marks must be on whole pixels. agent-browser screenshots are 1x, which is the harder case for edges, so a clean 1x crop is enough.
In the timeline, a held flash on a middle box and a pulse crossing that third both print
as p; tell them apart by the frames or by the probe, or move the probe to the box you
care about.
Send the MP4 along with a one-line caption naming the beats in order. Expect notes on mark size, pixel alignment and sequence truth; those are the three things a reviewer always checks.
4. The language, briefly
Why the engine draws what it draws, so a spec does not fight it: every node is the same
hairline box, because size and fill carrying meaning was the first thing stripped out.
Wires are quiet border-colour curves with no arrowheads, because pulses run both ways on
the same wire and arrowheads then lie. The accent is spent only on pulses and the box a
pulse reads or lands in, so it always means “happening now”. A frame landing on an e-paper
device is the real multi-second refresh, not a polite flash. Details and their reasons are
in references/language.md; read it before changing the engine.