Get started

Start with schemd 0.6

pronounced like skemd /skɛmd/

Compile multi-domain engineering diagrams into deterministic accessible SVG, and inspect the circuit behind them.

sheet
1 / 15
rev
v0.6
sections
3
compiled
3

schemd is pronounced like “skemd” (/skɛmd/). Version 0.4 removes the component and connection ceilings, makes the remaining budgets configurable per compilation, and reports the canonical terminal behind every port alias.

01 / InstallCompile on the server

Install the compiler and hand it a string. There is no DOM to wait for, no font to load, no layout pass to measure — so this runs during a build, inside a request handler, or in a test.

npm i @schemd/core
import { compileSchematic, parseSchematicFence } from '@schemd/core';

const fence = parseSchematicFence('schemd bounds="980x420" title="Front end"')!;
const { svg, document, metrics } = compileSchematic(source, fence);

Three things come back: svg is the vector, document is the validated model behind it, and metrics is what the compilation cost. Note that we get all three from one pass — the model is never re-derived by parsing our own output.

The diagram beside this is an instrumentation front end: a bridge sensor into a differential amplifier, a single-pole anti-alias filter, then the converter. Because the topology is declared rather than drawn, we can reason about it. The filter's corner sits at

fc=12πRfCff_c = \frac{1}{2\pi R_f C_f}

and with Rf=2.2kΩR_f = 2.2\,\text{k}\Omega and Cf=4.7nFC_f = 4.7\,\text{nF} that lands near 15.4 kHz15.4\ \text{kHz} — above the signal band, below half the sample rate.

source:CELL "V_{cell}" at (80, 160) #blue [type=voltage-dc]
amplifier:A9 "OPA, G = 10" at (300, 160) #cyan [type=opamp]
inductor:LF9 "100 \mu H" at (520, 160) #amber
junction:TAP9 "f_0" at (690, 160) #cyan
capacitor:CF9 "1 nF" at (690, 280) #cyan [orientation=down]
ground:RTN "RTN" at (400, 350) #slate
port:DAQ "to DAQ" at (890, 160) #emerald

CELL.positive -> A9.positive #blue [ortho]
CELL.negative -> RTN.in #slate [ortho]
A9.out -> LF9.in #cyan [line]
LF9.out -> TAP9.node #amber [line]
TAP9.node -> CF9.in #cyan [ortho]
CF9.out -> RTN.in #cyan [ortho]
TAP9.node -> DAQ.in #emerald [line marker-end=arrow]
compiled by @schemd/core → shown in the rail

02 / MigrationMove to 0.6 deliberately

Existing documents keep compiling. Two changes are worth reading before we lean on them.

A port alias is accepted, not preserved. We may still write R1.r or X1.out; 0.5 rewrites both endpoints to the terminal they resolve to before topology, the netlist, and the design rules ever see them. That is what makes R1.out -> A.in and R1.r -> B.in one net rather than two colliding ones. The consequence is concrete: anything a host keys on the spelling we typed — an overlay, a hover card, a simulation timeline — must key on the emitted name instead. canonicalPortName is exported so we can ask for the same answer the compiler used.

The component and connection ceilings are gone. MAX_SCHEMATIC_COMPONENTS, MAX_SCHEMATIC_CONNECTIONS, and their SCHEMATIC_LIMITS entries were removed; the compiler is linear in both, and sixty-four thousand components compile in about a second. A per-compilation limits option replaces them — see resource budgets.

port:P "P" at (70, 100) #cyan
port:Q "Q" at (70, 210) #cyan
nand:N1 "\overline{P \land Q}" at (360, 155) #purple
port:R "R" at (720, 155) #emerald

P.out -> N1.in1 #cyan [line]
Q.out -> N1.in2 #cyan [line]
N1.out -> R.in #emerald [line marker-end=arrow]
compiled by @schemd/core → shown in the rail

That last connection is written with the out alias and emits data-wire-source="X1.out1". Open it in the playground and read the raw SVG if that matters to us.

03 / BoundariesRead this before trusting a clean compile

A document that compiles is a drawing the compiler could place and route. It is not a claim that the circuit works. The failure mode of a tool like this is a confident picture of something wrong, so here is where the confidence stops.

  • verifyNetlist is structural linting, not verification. It runs deterministic rules over a flat connectivity model. It establishes nothing about analog correctness, timing, impedance, drive strength, metastability, quantum validity, or behaviour. A clean result means no rule fired.
  • Routing is greedy and never rips up. Each trace is placed against the ones already laid, so congestion is order-dependent: a route can be unroutable because of a choice made earlier, not because no arrangement exists.
  • The model is flat. No hierarchy, no sub-sheets, no simulation, no standards conformance. That suits documentation, teaching, and schematics-as-source — not large engineering designs.
  • Descriptions report connectivity, not intent. describeSchematic states what the netlist proves and deliberately names no archetype, because a confident wrong label is worse for a screen-reader user than an accurate structural one.
  • Published performance figures are narrow. Warm medians on one Apple Silicon and Node configuration. Run bun run benchmark on our own hardware.
port:TX "TX" at (70, 110) #cyan
port:RX "RX" at (70, 250) #purple
port:TXOUT "TX'" at (790, 250) #cyan
port:RXOUT "RX'" at (790, 110) #emerald

TX.out -> TXOUT.in #cyan [ortho net=UPLINK]
RX.out -> RXOUT.in #purple [ortho net=DOWNLINK]
compiled by @schemd/core → shown in the rail

Those two traces must cross, and they belong to different nets — so the compiler draws a bridge rather than a junction. Had they carried the same net=, the crossing would have stayed continuous, because then it would be one conductor.