trevato/circuit
Public: anyone can read this; the crew can change it.
An analog circuit simulator: a schematic editor on a canvas (place resistors, capacitors, inductors, voltage and current sources, diodes; wire them; rotate; delete), DC operating point and transient analysis by modified nodal analysis written by hand, an oscilloscope view of any node, and a netlist import/export; every circuit has a shareable URL and a saved-designs list. Results must match hand analysis of RC and RLC circuits in the tests.
main
- src
- static
- .gitignore 37 B · ~11 tok
- AGENTS.md 2.5 KB · ~636 tok
- forge.json 111 B · ~37 tok
- package.json 144 B · ~48 tok
- README.md 4.3 KB · ~1.1k tok
README.md
circuit
An analog circuit simulator: draw a schematic, get the DC operating point and a transient run, look at any node on the oscilloscope, and take the netlist, the CSV or the JSON away with you. Every circuit is its own URL.
Everything is real: modified nodal analysis with the device stamps written out
by hand (src/mna.ts), trapezoidal integration for the transient
(src/transient.ts), Newton-Raphson with SPICE's junction limiting for the
diode. Nothing is mocked, nothing is a lookup table, and there are no
dependencies — no chart library, no framework, no solver. The tests check the
answers against closed-form hand analysis: the RC step response
V(1 − e^(−t/RC)), the underdamped series-RLC step
V[1 − e^(−αt)(cos ω_d t + (α/ω_d)·sin ω_d t)], the LC ringing period, and a
diode operating point solved independently by bisection.
PORT=8080 FORGE_APP_DATA=./data bun run src/server.ts # run it here
bun test # the check every pull runs
FORGE_APP_DATA=./data bun run src/tty.ts # the terminal interface
Using it
- Draw: pick a part from the toolbar and click to place it; the wire tool
drags a wire. r rotates the selection, Delete removes it, clicking a part
opens its value. Wires connect where an end meets a pin or lands on another
wire — two wires that merely cross do not connect.
- Run: the DC operating point and the transient are recomputed after every
edit. The step and stop time are under "Run"; "start from initial
conditions" is SPICE's UIC, using each capacitor's ic voltage and each
inductor's ic current instead of the DC point.
- Share: the address bar always holds the circuit on screen. "Save this
design" gives it a short link (/d/xxxxxxxx) in the saved-designs list.
- Import and export: the netlist box at the bottom takes a SPICE-ish
netlist and loads it as a drawing (R1 in out 1k, C1 out 0 1u ic=0,
V1 in 0 SIN(0 5 1k), .tran 5u 5m uic, .probe out). /netlist?c=…,
/results.csv?c=… and /results.json?c=… export the same run;
/print?c=… is the print view.
- Without JavaScript the page is still a server-rendered schematic, scope and
results, and the netlist box still edits the circuit.
What is real and what is not
Real: resistors, capacitors, inductors, independent voltage and current sources (DC, SIN, PULSE), and the Shockley diode at 27 °C. DC operating point and fixed-step transient analysis. Currents are positive from pin 1 to pin 2.
Not there: AC or frequency sweeps, transistors, transformers, dependent sources, temperature, adaptive timestep, diode capacitance or breakdown. Every node carries a 1 pS conductance to ground (SPICE's GMIN), which is why voltages agree with hand analysis to about seven digits rather than exactly.
GMIN also means a mistake solves instead of failing, so the mistakes are named:
a node with no DC path to ground, or with a single connection, gets a warning
next to the errors on the page, in the print view, in the terminal and in
warnings in the JSON. Its voltage is the solver's, not the circuit's.
Shape
src/mna.ts builds and solves the matrix, src/transient.ts steps it through
time, src/schematic.ts turns a drawing into a netlist, src/floating.ts
finds the nodes the netlist does not decide, src/netlist.ts is the text form,
src/draw.ts and src/scope.ts render SVG on the server, src/pages/* are
the pages, src/server.ts is the routes and the security floor, src/body.ts
reads a POST body under a cap counted on the stream (a POST over 256 kB is
refused as it arrives, whatever its Content-Length says), and
static/editor.js is the canvas editor — served under a name made
from its own bytes and cached for a year.
This is an app: the forge runs it. Production follows the default branch;
every open pull gets a preview at its own address with its own copy of the
data. Everything the app keeps lives under FORGE_APP_DATA.
It also has a terminal: the exact command is in FORGE_APP_SSH when the app
runs; it opens src/tty.ts on a real pty over the same designs, with the same
operating point and an ASCII oscilloscope.
The forge sets PORT, FORGE_APP_DATA, FORGE_APP_ENV, FORGE_APP_URL and
FORGE_APP_COMMIT when it starts the app. forge.json says how.