open source · 100% no navegador · sem backend

Código vira circuito.

Simule circuitos analógicos direto no navegador, descreva-os em TypeScript e deixe agentes de IA iterarem até funcionar — com diagnósticos que apontam a linha e hints verificados por simulação.

v(C1) — carga RC · 1 V/div · 1 ms/div● tran

O loop que fez o vibecoding funcionar — agora para eletrônica

Compile → simule → assert → diagnóstico estruturado. Uma bancada .bench.ts descreve circuito + spec; o forge fecha o ciclo em segundos, sem sair do terminal.

1

Escreva

Descreva o circuito e o spec numa bancada: componentes com pinos nomeados (anode, +), parâmetros livres com tb.param e asserções como testes.

2

Verifique

forge verify sobe a escada lint → op → tran com fail-fast. O lint analisa a topologia como grafo antes do solver — singular matrix nunca chega até você.

3

Corrija

Cada falha traz código estável, linha no fonte e hint verificado por simulação. Aplique com --set, rode de novo, fique verde.

forge — zsh
$ 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

Um simulador de verdade, com ergonomia de compilador moderno

Motor numérico próprio em TypeScript, validado contra soluções analíticas — e uma camada de diagnóstico pensada para quem itera: humano ou agente.

Simulação real no navegador

Modified Nodal Analysis + Newton-Raphson, ponto de operação DC e transiente com Euler implícito. 100% local, sem backend, sem token de nuvem.

Erros que ensinam

Código estável (F105), localização no fonte (at bench/led.bench.ts:10) e uma nota com a física do problema — estilo rustc, detectado por lint de grafo antes do solver rodar.

Hints verificados por simulação

Declare tb.param('R1', …) e, quando uma asserção falhar, a plataforma varre a faixa, simula cada ponto e devolve o intervalo que comprovadamente passa.

Feito para agentes LLM

Exit codes com semântica (0/1/2/3), --json determinístico (dois runs fazem diff limpo) e ledger de runs em .forge/ contra o context attrition.

Componentes customizados

defineComponent({ pins, params, stamp, onStep }) cria componentes via código — incluindo não-lineares com estado interno, no mesmo motor dos built-ins.

Editor visual + código

Canvas com corrente animada e osciloscópio, roteamento automático de fios com A*, editor multi-arquivo com highlighting. O canvas é uma projeção do código — nunca o contrário.

Exemplos prontos para abrir

Cada exemplo abre direto no editor — mexa nos valores, ligue a simulação e veja a corrente correr.

Divisor de tensão

Dois resistores em série: o "hello world" da lei de Ohm, com sonda no nó do meio.

Abrir no simulador →

Filtro RC passa-baixa

Resistor + capacitor: carga exponencial no transiente e a constante de tempo τ = RC.

Abrir no simulador →

RLC ressonante

R, L e C em série: oscilação amortecida, ringing e a frequência natural do tanque.

Abrir no simulador →

Chave SPDT

Uma chave de 3 terminais alterna dois LEDs — clique nela com a simulação rodando.

Abrir no simulador →

MCU + LEDs

Um chip aciona dois LEDs com resistores de série — o clássico blink, em nível de circuito.

Abrir no simulador →

Feito para agentes

A bancada é o artefato completo: circuito + parâmetros livres + asserções. O --json devolve um RunRecord determinístico que o agente consome sem parsear texto.

bench/led-driver.bench.tsTypeScript
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 é da plataforma, não do agente
  tb.param('R1', { min: '100', max: '10k', scale: 'log' });

  tb.expect.op('D1.i').toBeWithin('5m', '15m'); // brilha, sem queimar
  tb.expect.op('led').toBeWithin(1.8, 2.6);     // Vf sadio
});
forge verify --json · RunRecord (resumo)JSON
{
  "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
}

Uma vibecoding loop para eletrônica: o agente escreve, o forge simula, o diagnóstico aponta a linha.