Skip to main content

Nodes

Nodes (nodes) are the static objects of the scene: servers, clients, databases... The engine places them automatically (see Layout); you only describe their identity and appearance. Each node has a unique id reused everywhere else (from/to of actions, connections, zones, object of comments...).

Node types

The type field chooses the appearance. Ten types display a pictogram:

Loading…
{ id: 'web', type: 'server', text: 'Web Server' }
CategoryTypes
Workstationsdesktop, laptop, mobile
Networkclient, server, cloud
Datadatabase
Actorsuser, admin, users
Charactersalice, bob, eve

Two additional types display text rather than a pictogram: simple_node and complex_node (see below). Eight types draw a geometric shape that can contain a short text: square, diamond, circle, triangle, parallelogram, width_rectangle, height_rectangle and star (see Geometric shapes).

Text nodes: simple_node and complex_node

When a node needs to show text (a code snippet, an HTTP header, a config key...) rather than a pictogram, use:

  • simple_node — a text box (body field). No large pictogram, but the subicon (icon) is still possible.
  • complex_node — like simple_node, plus a header displayed above the body and separated by a line: the node looks like an HTTP packet.

The language field applies syntax highlighting to all text areas of the node (the header and the body). The recognized values are the same as for content (javascript, json, sql, http...).

Loading…
nodes: [
// simple_node: text body + subicon, no pictogram.
{
id: 'snippet',
type: 'simple_node',
icon: 'node',
body: 'const total = a + b;',
language: 'javascript',
},
// complex_node: header + body, like an HTTP packet.
{
id: 'request',
type: 'complex_node',
header: 'GET /api/users HTTP/1.1',
body: 'Host: api.example.com\nAccept: application/json',
language: 'http', // highlights both header AND body
},
],
body/header vs text

text remains the label under the node (common to all types). For text nodes, the content of the box goes in body (and header for complex_node). An active set_content replaces the text panel, exactly as it hides a pictogram.

Geometric shapes

Eight types draw a shape rather than a pictogram: square, diamond, circle, triangle, parallelogram, width_rectangle, height_rectangle and star. Each shape can contain a short centered text via body.

Loading…
nodes: [
{ id: 'cache', type: 'circle', text: 'Cache', body: 'Redis' },
{ id: 'choice', type: 'diamond', text: 'Routing', body: 'GET ?' },
{ id: 'queue', type: 'height_rectangle', body: 'Queue' },
],
Keep the text short

The body of a shape is designed for a brief label (a word, a number, an acronym). The shape expands to accommodate the text, but it is bounded (max-width) and cropped if necessary to never overflow the outline: an entire paragraph would be truncated. For long text, prefer a simple_node.

Electrical components

A family of schematic symbolsresistor, capacitor, inductor, battery, dc_source / ac_source, diode, led, transistor_npn, opamp, switch, push_button, lamp, motor, ground, junction, ammeter, voltmeter, fuse, potentiometer, transformer… — for drawing electrical circuits. Unlike the other types, they expose named terminals you wire to by name ("R1:a", "battery:+", "Q1:collector"), and they read best in direction: 'circuit', where connections become orthogonal wires. Two extra fields go with them:

  • value + unit build the label (value: 220, unit: 'Ω'"220 Ω"; combined with text if both are set, e.g. "R1 · 220 Ω");
  • closed (on a switch / push_button) sets the initial contact state, animated at runtime by the toggle action.

The family also includes digital logic gatesand_gate, or_gate, not_gate, nand_gate, nor_gate, xor_gate, xnor_gate, buffer_gate — with two inputs a / b on the left and an output y on the right (a single input for not_gate / buffer_gate), their three-input counterparts and3_gate, or3_gate, nand3_gate, nor3_gate and xor3_gate (inputs a / b / c, the middle one at mid-height so a straight wire needs no bend), and a signal node — a labelled I/O pad that shows a bit in its centre (set via set_icon, lit with set_color) — for the inputs and outputs of a logic diagram.

Functional blocks

Some circuits are clearer when a sub-circuit is drawn as one labelled box instead of the gates inside it. Eight types cover the blocks whose terminal count is fixed:

TypeTerminals
d_flip_flop, t_flip_flopd / t, clkq, qn
jk_flip_flopj, clk, kq, qn
sr_latchs, rq, qn (level-sensitive: no clock)
mux_2to1i0, i1, sel (from below) → y
demux_1to2i, sel (from below) → y0, y1
half_addera, bs (sum), cout (carry)
full_addera, b, cins, cout

qn also answers to q_bar, sel to s, and s to sum — spell a terminal whichever way reads best in your spec.

Loading…
nodes: [
{ id: 'i0', type: 'signal', x: 0.12, y: 0.28, text: 'A', icon: '1' },
{ id: 'i1', type: 'signal', x: 0.12, y: 0.72, text: 'B', icon: '0' },
{ id: 'm', type: 'mux_2to1', x: 0.55, y: 0.45, text: 'MUX 2:1' },
{ id: 'sel', type: 'signal', x: 0.45, y: 0.86, text: 'S', icon: '0' },
{ id: 'y', type: 'signal', x: 0.88, y: 0.45, text: 'Y', icon: '1' },
],
connections: [
{ from: 'i0', to: 'm:i0' },
{ from: 'i1', to: 'm:i1' },
{ from: 'sel', to: 'm:sel' },
{ from: 'm:y', to: 'y' },
],
Only fixed-pin blocks

A block whose terminal COUNT varies with its size — an N-bit register, a 4:1 multiplexer, an n→2ⁿ decoder — is deliberately not a type. One type per size would multiply symbols without end; those blocks need their pins declared in the spec, which the format does not do yet.

MOS transistors

mosfet_n and mosfet_p expose gate (g), drain (d) and source (s), and transmission_gate is the CMOS pass gate (inout, controls en above and enb below). N and P are told apart by the bubble on the gate, the convention CMOS logic diagrams use.

The two channel terminals are mirrored between the two: source is the upper terminal on a pMOS, drain is the upper one on an nMOS. That is not a quirk — it is what every CMOS schematic does, and it is what lets a pull-up and a pull-down stack be wired straight down the page:

Loading…
connections: [
{ from: 'vdd', to: 'P:s' }, // pMOS source faces the supply
{ from: 'P:d', to: 'out' },
{ from: 'out', to: 'N:d' }, // nMOS drain faces the output
{ from: 'N:s', to: 'gnd:a' },
{ from: 'out', to: 'y' },
{ from: 'in', to: 'P:g' },
{ from: 'in', to: 'N:g' },
],

In a logic diagram, each wire driven by a signal input or a gate output is automatically tinted by its net (its driver), so wires that cross or run in parallel read as distinct and are visibly not joined. The blocks above drive a net too, so a flip-flop's q is tinted like a gate's y. A transmission_gate does not — it passes a net rather than driving one — and neither do the MOS transistors, whose pull-up and pull-down networks share one output node. A wire's explicit color (or a set_color on the connection) always overrides the automatic tint; wires driven by a non-logic source (a battery, a junction) stay neutral.

Several gallery demos build on all this: Electrical circuit (a battery, switch, resistor and LED loop with animated current), Parallel circuit (two resistor + LED branches), Ohm's law (voltage vs current vs resistance vs power), RC circuit (a capacitor charging — the transient and its time constant τ = R·C), Logic gates (all eight gates stepping through the full truth table), Half-adder, and a family built entirely from the universal NAND gateHalf-adder, Half-subtractor, Full adder, Full subtractor and an SR latch (two cross-coupled NANDs — one bit of memory), and one level further down, the CMOS NAND gate — the same NAND drawn as its four MOS transistors, lighting up the ones that conduct. In the logic demos each wire is coloured by the bit it carries (green = 1) via set_color, so students can trace the signal propagating through the gates.

Colors: background_color and border_color

Two fields adjust a node's colors:

  • background_color — the background: fill of a shape, background of a panel (simple_node/complex_node), or badge behind a pictogram.
  • border_color — the border: stroke of a shape, border of a panel, or color of the lines of a pictogram.
  • text_color — the color of the text inside the node (body of a shape, header/body of a panel), only if syntax highlighting is disabled (no language). With language, syntax colors take precedence.

Each field accepts a predefined color (any CSS name: tomato, steelblue, gold...) or an exact hexadecimal value (#3b82f6).

Loading…
nodes: [
// background_color only → border AND text derived automatically.
{ id: 'api', type: 'server', background_color: '#bfdbfe' },
// predefined name + explicit border, on a shape.
{
id: 'cache',
type: 'circle',
body: 'Cache',
background_color: 'gold',
border_color: 'darkgoldenrod',
},
// dark background, auto-contrasted text (white); or explicit text_color.
{ id: 'note', type: 'simple_node', body: 'TODO', background_color: '#1e3a8a' },
{
id: 'tag',
type: 'square',
body: 'SALE',
background_color: '#fee2e2',
text_color: '#b91c1c',
},
],
Automatic border and text

If you provide a background_color without border_color, the border is derived from the background (a darker variant that looks good). Similarly, without text_color, the color of the internal text is chosen to offer a very high contrast with the background (black or white depending on its luminance) — a dark background therefore remains legible without setting anything. Specify border_color / text_color to force a hue. With language (syntax highlighting), token colors take precedence over text_color.

text — the label

The text is displayed under the node. It is optional but recommended to remove any ambiguity between two nodes of the same type.

{ id: 'authdb', type: 'database', text: 'Auth DB' }

icon — the tech badge

The icon field overlays a small badge in the corner of the node. Three sources are accepted, in this resolution order:

  1. a known technology (built-in react-icons icon) — e.g., react, node, postgres, mongodb, redis, nginx, docker, kubernetes, dotnet, python, typescript, go, rust, git, azure, aws, graphql, protocols (http, dns, oidc, wifi, bluetooth, 5g) and payment brands (visa, mastercard, googlepay, applepay)...;
  2. an icon registered by you via registerSubIcon;
  3. otherwise, free text displayed in a pill (truncated to 4 characters).
Loading…
{ id: 'spa', type: 'laptop', text: 'react', icon: 'react' }, // known tech
{ id: 'edge', type: 'cloud', text: 'v2', icon: 'v2' }, // free text → pill
Registering your own badges

registerSubIcon(name, icon) adds a technology to the global registry, where icon is SVG markup or a () => SVGElement factory:

registerSubIcon('k8s', '<svg viewBox="0 0 24 24">…</svg>');

Call it only once at application startup (never inside a component body) — see the warning in the API Reference.

url — making a node clickable

Providing a url makes the node clickable: it opens the link in a new tab. Handy for pointing to a service's documentation.

{ id: 'api', type: 'server', text: 'API', url: 'https://example.com/api' }

content — initial content

A node can display content upon initialization (before any action), via the content field. It uses the same shape as the set_content action: a code terminal, a browser window, an image or a table.

{
id: 'editor',
type: 'laptop',
text: 'Editor',
content: {
type: 'code',
language: 'javascript',
value: 'const add = (a, b) => a + b;',
},
}

visible — initial visibility

By default, every node is visible (visible: true). Pass visible: false to hide it initially and reveal it later with the set_visible action — useful for showing the addition of a component (cache, replica...) as the narrative progresses.

Loading…
nodes: [
{ id: 'app', type: 'server', text: 'App' },
{ id: 'cache', type: 'database', text: 'Cache', visible: false }, // hidden initially
],
timeline: [
{ type: 'set_visible', object: 'cache', visible: true }, // … then revealed
],

rotation — node orientation

rotation orients the node's visual (pictogram, shape or panel) by an angle in degrees (clockwise, default 0). The label below the node stays upright, and arrow anchoring is computed on the unrotated box — so a rotated node connects exactly like a straight one.

The orientation can be animated at runtime with the rotate action, which moves the node toward an absolute target angle. Successive rotations chain from the current angle.

Loading…
nodes: [
{ id: 'arm', type: 'triangle', text: '45°', rotation: 45 }, // static orientation
{ id: 'spin', type: 'width_rectangle', text: 'rotate' },
],
timeline: [
{ type: 'rotate', object: 'spin', to: 180 }, // animate toward 180°
{ type: 'rotate', object: 'spin', to: 360 }, // … then a full turn
],