Create architecture diagrams using Arc's JSON format. Use when asked to "create an architecture diagram", "draw a system diagram", "visualize the architecture", or "make a diagram of".
Arc is a visual diagram editor for creating architecture diagrams. This skill enables you to generate Arc-compatible diagram configs that can be rendered in React applications.
Activate this skill when the user asks to:
Arc diagrams are JSON objects with this structure:
interface ArcDiagramData {
layout: { width: number; height: number }
nodes: Record<string, NodePosition>
nodeData: Record<string, NodeData>
connectors: Connector[]
connectorStyles: Record<string, ConnectorStyle>
flows?: DiagramFlow[]
}
Set canvas dimensions (default 1600x900 for editor, smaller for exports):
"layout": { "width": 860, "height": 400 }
Each node needs an x, y position and size:
"nodes": {
"frontend": { "x": 50, "y": 100, "size": "l" },
"api": { "x": 300, "y": 100, "size": "m" },
"database": { "x": 550, "y": 100, "size": "m" }
}
Size options (canonical — use these):
| Size | Key | Dimensions (px) |
|---|---|---|
| Extra Small | xs |
80 × 36 |
| Small | s |
110 × 48 |
| Medium | m |
160 × 75 |
| Large | l |
220 × 90 |
Source: src/utils/constants.ts → NODE_SIZES. Do not use large/normal/small.
Each node needs an icon, name, and color:
"nodeData": {
"frontend": {
"icon": "Monitor",
"name": "Frontend",
"subtitle": "React",
"description": "User interface",
"color": "violet"
},
"api": {
"icon": "Server",
"name": "API",
"subtitle": "Node.js",
"description": "REST endpoints",
"color": "emerald"
},
"database": {
"icon": "Database",
"name": "PostgreSQL",
"subtitle": "Storage",
"description": "Persistent data",
"color": "blue"
}
}
Available icons (from Lucide):
Available colors:
violet | emerald | blue | amber | sky | zinc | rose | orange
Define connections between nodes:
"connectors": [
{
"from": "frontend",
"to": "api",
"fromAnchor": "right",
"toAnchor": "left",
"style": "http"
},
{
"from": "api",
"to": "database",
"fromAnchor": "right",
"toAnchor": "left",
"style": "sql"
}
]
Anchor positions:
top | bottom | left | right | bottomLeft | bottomRight | topLeft | topRight
Optional: Add "curve": "natural" or "curve": "step" for curved connectors.
Stable identity: Give a connector an id when it may be referenced by a focus story or guided view, reviewed in a diff, or when another connector shares the same from/to pair. Without an id, a connector is only addressable by endpoint pair or array index.
Guided views: when the diagram explains a flow a reader should follow in order, add views[] — {id, title, node?, nodes?, connectors?, mode?, caption?, steps?} per chapter. The player draws a chapter rail (showViews) and /player/<session>?view=<id> deep-links a chapter. A view anchored on node inherits that node's focusTargets story; declare nodes/connectors/mode to override it.
Add flows[] when a message or packet should travel the diagram rather than a connector merely having motion styling:
"flows": [
{
"id": "request",
"label": "Request",
"legs": [
{ "id": "client-api" },
{ "id": "api-db", "pause": 0.4 }
],
"duration": 3,
"delay": 0.2,
"hold": 0.6,
"repeat": "indefinite",
"easing": "ease-in-out",
"marker": "packet",
"color": "emerald",
"size": 11,
"trail": "wake"
}
]
id (preferred) or endpoint { "from": "...", "to": "..." }; add direction: "reverse" to run a leg backwards and pause to dwell between hops.speed is px/s (default 160), duration is total route seconds including pauses and overrides speed, delay/hold are seconds, and repeat is a count or "indefinite".marker is dot (default), packet, pulse, or arrow; trail is none, fade, or wake; color/size tune the marker.render_svg/render_png can sample a still at flowTime, and arc render --format gif|mp4 or MCP render_animation captures deterministic frames through Chrome and encodes with ffmpeg.Define appearance for each connector style:
"connectorStyles": {
"http": { "color": "amber", "strokeWidth": 2, "label": "HTTP" },
"sql": { "color": "blue", "strokeWidth": 2, "label": "SQL" },
"async": { "color": "zinc", "strokeWidth": 1.5, "dashed": true }
}
Here's a full diagram for a typical web application:
{
"layout": { "width": 700, "height": 300 },
"nodes": {
"client": { "x": 50, "y": 100, "size": "l" },
"api": { "x": 300, "y": 100, "size": "m" },
"cache": { "x": 300, "y": 220, "size": "s" },
"database": { "x": 520, "y": 100, "size": "m" }
},
"nodeData": {
"client": { "icon": "Monitor", "name": "Client", "subtitle": "React", "color": "violet" },
"api": { "icon": "Server", "name": "API", "subtitle": "Express", "color": "emerald" },
"cache": { "icon": "Zap", "name": "Cache", "subtitle": "Redis", "color": "amber" },
"database": { "icon": "Database", "name": "Database", "subtitle": "Postgres", "color": "blue" }
},
"connectors": [
{ "from": "client", "to": "api", "fromAnchor": "right", "toAnchor": "left", "style": "http" },
{ "from": "api", "to": "database", "fromAnchor": "right", "toAnchor": "left", "style": "sql" },
{ "from": "api", "to": "cache", "fromAnchor": "bottom", "toAnchor": "top", "style": "cache" }
],
"connectorStyles": {
"http": { "color": "amber", "strokeWidth": 2, "label": "HTTP" },
"sql": { "color": "blue", "strokeWidth": 2, "label": "SQL" },
"cache": { "color": "emerald", "strokeWidth": 2, "label": "cache" }
}
}
The contract past "does it validate": a good Arc diagram reads top-to-bottom or left-to-right in one glance. These are the composition rules validateDiagram partially enforces (composition/* warnings) and the rest is on you.
Budget. ≤12 nodes per flat view. Past ~16 without groups/layoutHints/views, composition/too-dense warns — the fix is chaptering, not a bigger canvas: split into groups + layoutHints for regions, or views[] for a walkthrough.
One spine. Pick one dominant flow direction and hold it: left→right for request/data paths, top→bottom for stacks and pipelines. Everything not on the spine (replicas, sidecars, observability taps) sits on a parallel lane — above/below for a horizontal spine — and reads as secondary: zinc color, dashed, thinner strokeWidth.
Spacing math. NODE_SIZES are xs 80×36, s 110×48, m 160×75, l 220×90. Budget ~150–200px of horizontal pitch between connected nodes (edge gap ≈ 40–80px of clear channel for the stroke + label), ~60–100px vertically. Keep sibling nodes on one baseline and aligned to a ~50px grid when hand-placing.
When to autoLayout. Call autoLayout() (or the editor's auto-layout) when node count exceeds ~8, when groups own their members via layoutHints, or when the graph is dense and any hand order will cross edges. Hand-place only when the shape itself carries meaning (a ring, a hub-and-spoke) and the node count is small.
Labels. The name row is what scans. Budget by size: xs ~5, s ~9, m ~17, l ~26 chars (composition/label-overflow warns past it, and suggests the next size up). Prefer subtitle for the qualifier instead of a long name — "API" + subtitle "Express" beats "Express API Server".
Routing. Anchors take the direction of travel: right→left on a horizontal spine, bottom→top on a vertical one. Never route a connector through an unrelated node (geometry/connector-through-node is an error). Diagonal corner anchors (bottomRight etc.) are for fan-out, not the spine. Give an id to any connector a focus story or view will reference, and to parallel edges sharing from/to.
Always validate before handing off JSON.
validateDiagram(value) from src/utils/diagramDiagnostics.ts.validate_diagram; it returns { ok, diagnostics }.validateDiagramShape() remains the low-level shape gate; validateDiagram wraps it and adds repairable diagnostics.Each diagnostic has a stable code, severity, subject, human message, optional evidence, and supportedFixes.
Repair loop:
diagnostics; fix severity: "error" before warnings.supportedFixes entry when offered, or make the direct correction it names.Common codes:
shape/* — malformed document or entries; fix these before anything else.semantic/dangling-connector-endpoint — retarget from/to or remove the connector.semantic/unknown-* and semantic/duplicate-connector-id — use the suggested valid value or unique connector id.geometry/node-overlap, geometry/connector-through-node, geometry/node-outside-layout — move nodes or run autoLayout().composition/too-dense, composition/label-overflow — advisory budget warnings: chapter the diagram, or resize the node.render_ascii previews a diagram in plain text without external dependencies.render_svg returns deterministic SVG markup; authored flows animate via SMIL by default, flowTime samples a deterministic still, and animateFlows: false disables them.render_png returns MCP image content when Chrome/Chromium is installed; set ARC_CHROME when it is not on PATH, and use flowTime to choose a deterministic animation frame. If it returns render/chrome-unavailable, continue with ASCII/SVG or ask the user to install Chrome/Chromium.render_animation writes a GIF or MP4 for diagrams with flows; set output to a .gif/.mp4 path, optionally duration, fps, scale, theme, and mode. It needs Chrome/Chromium plus ffmpeg (ARC_CHROME/ARC_FFMPEG).render_html emits paste-ready output: format=component for a React TSX component, format=iframe for a studio embed tag, or format=html for a standalone SVG page.arc render diagram.json --out artifact.gif --duration 4 --fps 12.When generating a diagram, output:
.arc.json file or paste into the Arc editorFor React projects, diagrams can be exported as TypeScript:
import type { ArcDiagramData } from '@arach/arc'
const diagram: ArcDiagramData = {
// ... config here
}
export default diagram