Simulate analog circuits right in the browser, describe them in TypeScript and let AI agents iterate until it works — with diagnostics that point to the line and simulation-verified hints.
Compile → simulate → assert → structured diagnostic. A bench .bench.ts
describes circuit + spec; forge closes the cycle in seconds, without leaving the terminal.
Describe the circuit and the spec in a bench: components with named pins
(anode, +), free parameters via tb.param
and assertions as tests.
forge verify climbs the lint → op → tran ladder with fail-fast.
Lint analyzes the topology as a graph before the solver — singular matrix
never reaches you.
Every failure carries a stable code, a source line, and a simulation-verified hint.
Apply it with --set, run again, go green.
$ forge verify bench/led.bench.ts ✓ lint pass ✗ op fail A301 a1: D1.i within [8m, 12m] — measured 28.039m (2.34x) at bench/led.bench.ts:22 hint (verified): R1 in [261, 316] passes all op assertions — try R1 = 280 FAIL · exit 1 $ forge verify bench/led.bench.ts --set R1=280 ✓ lint pass ✓ op pass PASS · exit 0
A custom numeric engine in TypeScript, validated against analytical solutions — and a diagnostic layer built for whoever iterates: human or agent.
Modified Nodal Analysis + Newton-Raphson, DC operating point and transient with implicit Euler. 100% local, no backend, no cloud token.
Stable code (F105), source location
(at bench/led.bench.ts:10) and a note with the physics behind the problem —
rustc style, detected by graph lint before the solver runs.
Declare tb.param('R1', …) and, when an assertion fails, the platform
sweeps the range, simulates each point, and returns the interval that provably passes.
Exit codes with semantics (0/1/2/3), deterministic --json
(two runs diff cleanly) and a run ledger in .forge/ against
context attrition.
defineComponent({ pins, params, stamp, onStep }) creates components in
code — including nonlinear ones with internal state, in the same engine as the built-ins.
Canvas with animated current and an oscilloscope, automatic wire routing with A*, multi-file editor with highlighting. The canvas is a projection of the code — never the other way around.
Each example opens straight in the editor — tweak the values, turn on the simulation and watch the current flow.
Two resistors in series: the "hello world" of Ohm's law, with a probe on the middle node.
Open in the simulator →Resistor + capacitor: exponential charge in the transient and the time constant τ = RC.
Open in the simulator →R, L and C in series: damped oscillation, ringing and the natural frequency of the tank.
Open in the simulator →A 3-terminal switch toggles between two LEDs — click it with the simulation running.
Open in the simulator →A chip drives two LEDs through series resistors — the classic blink, at the circuit level.
Open in the simulator →The bench is the complete artifact: circuit + free parameters + assertions.
--json returns a deterministic RunRecord that the agent consumes without parsing text.
import { bench, VoltageSource, Resistor, LED } from 'circuit-forge'; export default bench('led-driver', (tb) => { const v1 = tb.add('V1', new VoltageSource(5)); const r1 = tb.add('R1', new Resistor('330')); const d1 = tb.add('D1', new LED()); v1.pin('+').connect(r1.pin('1')); r1.pin('2').connect(d1.pin('anode')); d1.pin('cathode').connect(tb.gnd); v1.pin('-').connect(tb.gnd); tb.name('led', d1.pin('anode')); // sizing is the platform's job, not the agent's tb.param('R1', { min: '100', max: '10k', scale: 'log' }); tb.expect.op('D1.i').toBeWithin('5m', '15m'); // lit, without burning out tb.expect.op('led').toBeWithin(1.8, 2.6); // healthy Vf });
{
"schema": "forge-run/0.1",
"bench": "led-driver",
"engine": "[email protected]",
"stages": {
"lint": { "verdict": "pass" },
"op": {
"verdict": "fail",
"assertions": [{
"id": "a1", "probe": "D1.i",
"check": { "op": "within", "min": 0.008, "max": 0.012 },
"measured": 0.028039,
"margin": { "ratio": 2.34 },
"at": "bench/led.bench.ts:22",
"hint": {
"kind": "verified-param-range",
"param": "R1",
"passingRange": [261.016, 316.228],
"suggestedValue": 261.016
}
}]
},
"tran": { "verdict": "skipped" }
},
"verdict": "fail",
"firstFailure": "a1",
"exitCode": 1
}
A vibecoding loop for electronics: the agent writes, forge simulates, the diagnostic points to the line.