circuitsnap
README.md
# circuitsnap
**Snap it, sim it.** Turn a circuit drawing — a photo, a sketch, or just a description —
into a live simulation in [Falstad's CircuitJS1](https://www.falstad.com/circuit/circuitjs.html).
Ships as an **MCP server** (so Claude and other assistants can build circuits), a **CLI**,
and a **library**.
```console
$ circuitsnap build rc.json
https://www.falstad.com/circuit/circuitjs.html?ctz=CQAgjCAMB0l3BWcMBMcUHYMGZIA4UA2ATmIxAUgo...
$ 1 0.000005 10.20027730826997 50 5 50
v 128 64 192 64 0 0 40 5 0 0 0.5
r 384 64 448 64 0 10000
c 128 176 192 176 0 0.000001 0 0
...
```
Open the URL and the circuit is running.
## Why
Asking a language model to write CircuitJS1's file format directly does not work. The
format is positional and unforgiving — `v 128 64 192 64 0 0 40 5 0 0 0.5` has six trailing
parameters whose order, units and flag bits are only discoverable by reading the
simulator's Java source. A wrong guess produces a circuit that **loads happily and
simulates incorrectly**, which is the worst possible failure: the user cannot tell.
circuitsnap moves that job into tested code. The model describes *what connects to what*;
this library handles format, geometry, escaping and encoding.
## Install
```bash
npm install -g circuitsnap
```
### As an MCP server
```json
{
"mcpServers": {
"circuitsnap": { "command": "npx", "args": ["-y", "circuitsnap-mcp"] }
}
}
```
(`circuitsnap` is the CLI; `circuitsnap-mcp` is the stdio server. `circuitsnap mcp` runs
the server too, if you prefer a single binary.)
For Claude Code, also copy `skills/circuitsnap/` into `.claude/skills/` to get the
`/circuitsnap` workflow, which covers reading a drawing and verifying the result.
## Usage
Describe the circuit as a netlist — components and net names, **no coordinates**:
```json
{
"title": "RC lowpass",
"components": [
{ "id": "V1", "kind": "voltage", "nodes": ["gnd", "in"], "value": 5 },
{ "id": "R1", "kind": "resistor", "nodes": ["in", "out"], "value": "10k" },
{ "id": "C1", "kind": "capacitor", "nodes": ["out", "gnd"], "value": "1uF" }
]
}
```
Terminals sharing a net name are connected. That is the whole model.
```bash
circuitsnap build rc.json # URL + circuit text
circuitsnap build rc.json --url # just the URL
circuitsnap elements # pin order and parameters for every kind
circuitsnap decode "<share-url>" # read someone else's circuit
```
As a library:
```ts
import { snap } from 'circuitsnap';
const { url, text, diagnostics } = snap({
components: [
{ id: 'V1', kind: 'voltage', nodes: ['gnd', 'in'], value: 5 },
{ id: 'R1', kind: 'resistor', nodes: ['in', 'gnd'], value: '1k' },
],
});
console.log(url.url);
```
### Values
Engineering notation, written the way a person writes it on a schematic:
| Input | Value |
| --- | --- |
| `"10k"` | 10 000 |
| `"2.2uF"` | 2.2e-6 |
| `"4k7"` | 4700 |
| `"1M"` | 1e6 (**mega**, not milli) |
| `"1R2"` | 1.2 |
`M` is mega and `m` is milli — the schematic convention. SPICE's `M`-means-milli would turn
a hand-labelled "10M resistor" into 10 milliohms.
### Elements
`wire`, `resistor`, `capacitor`, `inductor`, `voltage`, `rail`, `ground`, `switch`,
`diode`, `led`, `current`, `labeledNode`, `transistor`, `opamp`, `output`, `probe`, `text`.
Run `circuitsnap elements` for pin order, parameters, units and defaults. Pin order
matters: a transistor is `[base, collector, emitter]`, an op-amp is `[minus, plus, out]`.
### Ground
Use the net name `gnd` (or `ground`, `0`, `vss`). Those terminals get a real ground symbol,
not a label — a labeled node named "gnd" carries no 0 V reference and would leave the
solver without a datum. circuitsnap warns if a circuit has no ground at all.
## How it works
The pipeline is netlist -> validate -> lay out -> emit -> URL, with only the *reading a
drawing* step needing a model. Two decisions are worth knowing about:
- **Nets are wired with named labels, not routed wires.** An auto-router that is 95%
correct produces circuits that look right and simulate wrong. A label next to a pin is
verifiable. Output reads like a hierarchical schematic.
- **We emit the legacy text format, not XML.** Current upstream writes XML but still
*reads* text, so text is the only encoding that loads on both old and new builds.
See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the reasoning and
[docs/FORMAT.md](docs/FORMAT.md) for the file-format reference, derived from the upstream
source with line citations.
## Correctness
The format is reverse-engineered, so correctness is load-bearing:
- **372 upstream circuits** round-trip byte-identically through the parser.
- **Generated output** is checked for terminal coincidence, escaping, and the flag bits
upstream forces on.
- **Live verification**: generated circuits are loaded into the real simulator and node
voltages read back. That is how the multi-pin geometry was validated — an op-amp
follower must actually follow, and a transistor must report *fwd active* with Ic/Ib = β.
```bash
npm run reference:fetch # clone upstream (not vendored; see docs/LICENSING.md)
npm test
```
The test suite makes no network requests.
## Licence
circuitsnap is MIT. CircuitJS1 is GPL-2.0-or-later by Paul Falstad and Iain Sharp, and is
neither bundled nor modified here — circuitsnap generates files in its format and opens its
URL. See [docs/LICENSING.md](docs/LICENSING.md) for the full reasoning, including how this
project handles falstad.com's AI-crawler signals (short version: no crawling, no scraping,
tests are fully offline, and `--base-url` lets you point at your own self-hosted copy).
Not affiliated with or endorsed by the CircuitJS1 authors.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues