pcbFork is an experiment in AI-assisted component placement for low-noise biosignal PCBs. It asks a narrow question: can a language model, treated as an untrusted proposal generator, improve the physical placement of components on an EEG/BCI front-end when its output is constrained, validated, and scored deterministically in the browser?
It is not an EDA tool and it is not a design authority. It is a harness for studying that question on real KiCad boards, entirely client-side.
The tool separates two things that are usually conflated when people talk about "AI PCB design":
- Proposal — a stochastic step that suggests where components could go. This is the language model's only job. It is assumed to be unreliable.
- Judgement — deterministic, inspectable code that decides whether a proposal is structurally valid and whether it is geometrically better or worse than the original.
The model never edits the board directly. It returns a placement patch (a list of ref x y [rot] moves), which is applied textually to the original KiCad source. Nets, pads, tracks, and zones are preserved by construction, so the model cannot silently corrupt connectivity regardless of board size. Everything the model produces is then re-parsed, validated, and re-scored by the same code path that judges the original.
KiCad .kicad_pcb → parse → summarise → model proposes placement patch
→ apply patch → re-parse → validate → score → compare → iterate
All metrics are geometric heuristics computed from placement and routing geometry. They are proxies for electrical behaviour, not measurements of it. They exist to (a) give the optimisation loop a differentiable-enough objective and (b) let a human compare two layouts on the same axes.
BCIAnalyzer locates the analog front-end (ADS1299 / ADS129x / RHD families, by value/footprint markers) and derives, among others:
| Metric | Definition | Proxy for |
|---|---|---|
| AFE↔radio distance | mm from AFE to nearest radio component | RF ingress into analog inputs |
| Decoupling proximity | mean/max distance from AFE power-pad decoupling caps to the AFE | supply loop area / bypass effectiveness |
| Channel symmetry | 0–1 regularity of the differential input channel layout | matched channel behaviour |
| Analog/digital separation | mean gap between analog and digital-categorised parts | digital noise coupling |
| Vias on sensitive nets | count of vias on IN*, CH*, SRB*, BIAS, REF nets |
impedance discontinuity / stub pickup on high-impedance inputs |
| Analog input track length | total routed length on sensitive-input nets | antenna length / noise pickup |
Net classification is name-based (e.g. IN1P, AIN3, SRB1, BIAS → sensitive; SCLK, MOSI, DRDY → digital; AVDD, DGND, 3V3 → power). This is a heuristic and will misclassify boards that use unconventional net names.
BoardScorer maps the heuristic metrics to a transparent 0–100 score with a per-axis breakdown. Every sub-score exposes the number that produced it and the reason (e.g. "AFE↔radio 18.4 mm (target ≥ 25 mm)"). Sub-score weights are set by the user's optimisation-priority sliders (signal integrity, separation, compactness, routing simplicity, symmetry, manufacturability).
Structural validity is a hard gate: a board that fails deterministic validation has its score multiplied by 0.3, so an invalid layout can never outrank a valid one on aggregate.
The score is a relative comparison instrument, not an absolute quality rating. A score of 82 means "better on these weighted geometric axes than a board that scores 74," nothing more.
Before a candidate is scored it passes BoardValidator, a browser-side preflight that catches regressions detectable without KiCad itself:
- balanced parentheses and a
(kicad_pcb …)root - component / net / pad preservation against the original
- duplicate reference designators
- changed footprints, added/removed components
- out-of-bounds placement and bounding-box overlap approximations
This is not DRC/ERC. It cannot check clearances, impedance, or electrical rules. It only rejects proposals that are structurally broken or that mutate more than placement.
A separate, fully deterministic red-team / blue-team layer tests whether the geometric metrics are sensitive enough to notice a deliberate regression. No language model is involved.
- Red injects one plausible physical-design defect into a parsed board and records hidden ground truth. Defect families: extra via on a sensitive net, degraded decoupling loop, radio moved into the analog keep-out, clock routed parallel to an input, differential-pair length imbalance, lengthened sensitive route.
- Blue independently recomputes metrics, detects and localises the regression to a component or net, explains the mechanism via a causal interference graph (aggressor → coupling → victim), attempts a minimal-intervention repair, and re-scores to demonstrate the effect.
- Counterfactual search finds the smallest change (
|ΔPCB|) that improves the score by at least a margin, ranked by improvement per unit change. - Benchmark runs Red against Blue across all applicable defects and reports detection rate, localisation rate, and repair recovery.
Known limitation: the repair "proof" only closes for defects the current BoardScorer actually measures (vias, radio distance). Coupling and small route-length changes are detected and localised but are not yet reflected in the aggregate score, so their repairs cannot be numerically proven by this harness.
An optional closed-loop mode that runs generation → validation → adversarial testing → counterfactual critique → improvement until it converges. All control logic is client-side; the model is called only for the propose and improve steps.
- Objective parsing — natural-language goal → structured config (weights, constraints, robustness threshold, generation budget, convergence delta, batch size) via the model, with schema validation.
- Generation — N candidates per generation, each validated and scored.
- Robustness — top candidates undergo Red/Blue testing; the loop records detection rate and specific vulnerabilities.
- Counterfactual critique — the best candidate's minimal-intervention opportunities are turned into concrete instructions ("move C5 3.2 mm closer to U1 for +4.5 pts") rather than vague feedback.
- Improvement — the model refines the best candidate using that concrete critique.
- Convergence — stops on score plateau (avg Δ over 3 generations below threshold), constraint + robustness satisfaction, or budget exhaustion.
The loop reports total AI calls, candidates generated, wall-clock time, and a rough cost estimate.
This section is the point of the tool, not a disclaimer.
- Heuristics are not electrical signoff. Every metric is geometry. None model impedance, current return paths, coupling coefficients, stackup, or noise numerically. A layout that scores well can still be electrically wrong.
- Net classification is name-based and will misread boards with unconventional naming.
- The preview is a legibility aid, not a rendering of the real board. The authoritative artifact is always the KiCad source.
- No DRC/ERC. The browser preflight does not replace KiCad's rule checks.
- The AFE detector is marker-based and targets ADS129x / RHD-class parts; other front-ends may not be recognised.
- Every generated design requires human review and a full KiCad DRC/ERC before fabrication.
- The app runs entirely in the browser. There is no backend, no account, and no server-side storage.
- PCB contents leave the browser only in the request you explicitly send to the AI provider you configured.
- API keys are persisted in browser
localStoragefor convenience (keypcbFork:credentials). They are never logged and never included in any exported artifact. Use Clear credentials in the UI, or clear site data, to remove them. - No CORS bypass. If a provider blocks direct browser access, the app surfaces a clear message rather than attempting to work around it. Providers are pluggable: OpenAI-compatible, Anthropic-compatible, or a custom endpoint behind one
callAI()interface.
npm install
npm run dev # http://localhost:5173 (or next free port)A bundled ADS1299 demo board loads automatically, so parsing, metrics, preview, and scoring are explorable with no API key. Additional real open-hardware boards ship under public/boards/ (VolksEEG, an OpenBCI-style 8-channel AFE, and ADS1299-based designs).
To generate candidates:
- Open AI Connection, pick a provider, set endpoint/model, paste your API key, and test the connection.
- Write an objective (optionally set constraints and priority sliders).
- Generate 1 / 3 / 5 candidates.
- Select a candidate to compare against the original, then Improve to iterate or Download to export.
npm run build # tsc + vite build → docs/ (static, host anywhere)
npm run preview # preview the production buildType-checking must pass clean: npx tsc --noEmit.
Everything is client-side. The analysis, scoring, validation, and AI modules are pure and never touch the DOM; AppController is the only module that does.
src/
parser/
sexpr.ts S-expression tokenizer/parser + tree queries
KiCadParser.ts .kicad_pcb → Board (components, nets, tracks, vias, zones, bounds)
placementPatch.ts parse + apply `ref x y [rot]` patches onto original source
analysis/
geometry.ts distance / stats / bbox helpers
BoardAnalyzer.ts semantic categorisation (analog/digital/power/radio/…)
BCIAnalyzer.ts biosignal placement heuristics + derived metrics
scoring/
BoardScorer.ts transparent 0–100 score, slider-weighted
validation/
BoardValidator.ts deterministic browser preflight
ai/
AIProvider.ts provider adapters behind one callAI(); live model listing
PromptBuilder.ts system + user + critique prompt construction
candidates/
CandidateManager.ts patch → parse → analyse → validate → score, keeps history
redblue/
RedTeam.ts defect injectors with hidden ground truth
BlueTeam.ts detect → localise → explain → repair → re-score
CausalGraph.ts aggressor → coupling → victim hypotheses
Counterfactual.ts minimal-intervention (|ΔPCB|) search
Benchmark.ts Red vs Blue harness + summary
boardOps.ts board clone + mutation primitives
netClasses.ts net taxonomy (sensitive/clock/digital/power)
agent/
AgentOrchestrator.ts autonomous loop controller
ObjectiveParser.ts natural language → structured config
RobustnessScorer.ts Red/Blue batch testing
CounterfactualCritique.ts repair-instruction builder
ConvergenceDetector.ts stopping criteria
render/
BoardRenderer.ts SVG preview + overlays + selection
diff/
DiffViewer.ts LCS line diff
app/
AppController.ts UI state + orchestration
shell.ts static HTML shell
dom.ts / report.ts helpers + JSON report builder
demo/
demoBoard.ts bundled ADS1299 sample board + real-board manifest
types.ts shared data model
The model must return only the components it wants to move, never a whole board. Echoing a full .kicad_pcb does not scale — a large board exceeds the model's output-token budget and returns truncated (corrupted). The expected response is exactly:
<reasoning_summary>
brief engineering rationale
</reasoning_summary>
<placement>
U1 84.30 62.10 90
C3 78.00 55.50
</placement>
Each placement line is ref x y [rot] (absolute mm; rotation optional). A full <kicad_pcb> board is still accepted as a fallback. Candidates that cannot be parsed are rejected; structural failures hard-cap the score.
Vite · TypeScript · vanilla DOM · SVG. No server, no database, no framework. Geometric heuristics only — no numerical field solver, no DRC engine, no persistence of secrets beyond the localStorage credential entry described above.