APP

kicad-pcb-viewer โ€” 2D viewer for .kicad_pcb files

2D PCB viewer for .kicad_pcb files in a Hydrogen webview. Pan, zoom, toggle layers, flip to inspect the back side. Stdlib Python, ~22 KB binary.

32-second feature tour: open .kicad_pcb, toggle layers, isolate Edge.Cuts, flip to back side, swap to multi-layer board

๐Ÿ’ฌ Sample prompts Paste any of these into Claude Code to use this app
View Open this .kicad_pcb in a viewer
Layers Show only Edge.Cuts and F.SilkS on this board
Flip Flip the board so I can see the back side
Stats How many footprints, traces, and vias does this board have?
Footprints List every footprint on this board with its placement
โšก Install this app

Paste this into Claude Code (VS Code panel, Adom editor, or terminal) to install:

I want to install the "kicad-pcb-viewer โ€” 2D viewer for .kicad_pcb files" app from the Adom Wiki (https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/kicad-pcb-viewer). For the Docker CLI: gh release download v0.1.0 --repo adom-inc/kicad-pcb-viewer --pattern kicad-pcb-viewer-linux -D /tmp && sudo install -m 0755 /tmp/kicad-pcb-viewer-linux /usr/local/bin/kicad-pcb-viewer && kicad-pcb-viewer install Then verify the install works.

Download latest

โŠž Windows coming soon
macOS coming soon
๐Ÿง Linux coming soon
๐Ÿณ Adom Docker CLI kicad-pcb-viewer-linux ยท

See all releases on GitHub (private repo โ€” access required)

A 2D PCB viewer for .kicad_pcb files, rendered as SVG inside a Hydrogen webview panel. For when you want to see a board without launching KiCad on the user's desktop.

BMA400 breakout โ€” front view with copper, silk, and edge cuts

Why this exists

Adom already has 3D viewers (.glb chip models) and gerber viewers (post-fab artwork). What was missing was the in-between: the editable KiCad board, viewed flat, with toggleable layers, in the cloud editor.

This app is deliberately 2D โ€” for 3D inspection, use the existing 3D viewers. Use this when:

  • The user pasted a .kicad_pcb and you want to confirm it looks sane before doing anything else with it.
  • You're debugging a board layout and want to see "did the autorouter stack everything on F.Cu? Where are the vias?"
  • You want to take a layer-isolated screenshot (e.g. board outline only) to drop into a doc, a PR, or a chat message.
  • You want to verify silkscreen orientation, or check what's actually on the back side of a board, without firing up pcbnew.

Install

gh release download v0.1.0 --repo adom-inc/kicad-pcb-viewer \
    --pattern kicad-pcb-viewer-linux \
    -D /tmp
sudo install -m 0755 /tmp/kicad-pcb-viewer-linux /usr/local/bin/kicad-pcb-viewer
kicad-pcb-viewer install   # deploys SKILL.md to ~/.claude/skills/kicad-pcb-viewer/

Runs on any Python 3.10+ system. No external dependencies โ€” just stdlib.

Quick start (human-driven)

kicad-pcb-viewer view path/to/board.kicad_pcb

Opens a Hydrogen webview tab in a non-VSCode pane (per app-creator skill โ€” never inside the editor pane).

ActionInput
PanClick + drag
ZoomScroll wheel (toward cursor)
Reset to fitFit button
Mirror horizontallyFlip button
Toggle layerClick a row in the sidebar

Coordinate readout (mm) and zoom % live in the bottom-right HUD.

CLI subcommands

CommandWhat it does
view <path> [--port N]Start server + open webview tab. Default.
serve <path> [--port N]Start server only (no tab). Useful for headless scripting.
shutdown [--port N]Stop a running viewer (graceful).
installDeploy SKILL.md to ~/.claude/skills/kicad-pcb-viewer/.
versionPrint version.

Default port: 9100. The first positional arg is auto-inferred as view if it ends in .kicad_pcb or is an existing path, so kicad-pcb-viewer board.kicad_pcb works as shorthand.

Multi-layer boards

Inner copper layers (In1.Cu, In2.Cu, In3.Cu, In4.Cu) get distinct colors and z-order. The layer panel only lists layers that actually have geometry on this board โ€” empty layers stay hidden, so the panel doesn't fill up with 30 unused entries.

BMI270 โ€” 4-layer board with copper pours, inner layers, vias

HTTP API โ€” every action is AI-drivable

Per app-creator ยง7, the same endpoints the UI uses are reachable via HTTP. This is the property that turns a webview into an AI surface.

MethodPathDescription
GET/statePhase, file path, filename, stats, bbox, layer visibility.
GET/boardFull parsed board JSON (footprints, pads, segments, arcs, vias, zones, gr).
GET/layers{name: {idx, kind, alias, visible}} for every layer on the board.
POST/layersBody {name: bool, ...} โ€” set visibility for one or more layers.
POST/loadBody {path: "..."} โ€” open a different .kicad_pcb without restarting the server.
POST/fitReset view to fit the board.
POST/flipToggle horizontal mirror (B-side viewing). Returns {flipped: bool}.
GET/consoleLast 500 forwarded UI-side console messages.
POST/consoleAppend a message (called from the UI).
DELETE/consoleClear log.
POST/evalBody {code: "..."} โ€” queue a JS snippet, returns {id}. UI runs it on next poll.
GET/eval/pendingUI poller endpoint โ€” returns {id, code} or empty.
GET/eval/<id>Read result of a prior /eval post.
POST/eval/<id>/resultUI POSTs result back here (internal).
POST/shutdownGraceful exit.
GET/cmds/pendingUI poller for fit/flip commands queued by /fit and /flip.
GET/The UI HTML.
GET/favicon.svgBrand icon.

AI recipes

Self-contained scripts an AI can copy-paste. All assume the viewer is running on port 9100 against some board.

Recipe 1 โ€” "is this board mostly empty?"

Quick stats check before doing anything else.

kicad-pcb-viewer serve board.kicad_pcb --port 9100 &
sleep 1
curl -s http://127.0.0.1:9100/state | jq '.stats, .bbox'
# {"footprints":13,"pads":31,"segments":61,"arcs":0,"vias":6,"zones":0,"gr":16}
# {"x":18.8,"y":18.8,"w":10.4,"h":10.4}
curl -s -X POST http://127.0.0.1:9100/shutdown >/dev/null

Recipe 2 โ€” "isolate Edge.Cuts and screenshot"

For a board-outline export. Hide every layer except the edge.

curl -X POST http://127.0.0.1:9100/layers \
    -H 'Content-Type: application/json' \
    -d '{
      "F.Cu":false,"B.Cu":false,
      "In1.Cu":false,"In2.Cu":false,"In3.Cu":false,"In4.Cu":false,
      "F.SilkS":false,"B.SilkS":false,
      "F.Mask":false,"B.Mask":false,
      "F.Paste":false,"B.Paste":false,
      "F.Fab":false,"B.Fab":false,
      "F.CrtYd":false,"B.CrtYd":false,
      "Edge.Cuts":true
    }'
adom-cli hydrogen screenshot panel --name "PCB: <filename>"

Recipe 3 โ€” "show only the back side"

Hide front-side layers, flip the view, screenshot.

curl -X POST http://127.0.0.1:9100/layers -H 'Content-Type: application/json' \
    -d '{"F.Cu":false,"F.SilkS":false,"F.Mask":false,"F.Paste":false,"F.Fab":false,"F.CrtYd":false,
         "B.Cu":true,"B.SilkS":true,"Edge.Cuts":true}'
curl -X POST http://127.0.0.1:9100/flip
adom-cli hydrogen screenshot panel --name "PCB: <filename>"

Recipe 4 โ€” "list every footprint and its placement"

Useful for cross-referencing with a BOM or schematic.

curl -s http://127.0.0.1:9100/board | \
    jq '.footprints[] | {refdes, value, x, y, rot, layer}'
# {"refdes":"U1","value":"BMA400","x":24.0,"y":24.0,"rot":0,"layer":"F.Cu"}
# ...

Recipe 5 โ€” "diff two boards by stats"

for f in v1.kicad_pcb v2.kicad_pcb; do
    curl -s -X POST http://127.0.0.1:9100/load \
        -H 'Content-Type: application/json' -d "{\"path\":\"$PWD/$f\"}" >/dev/null
    echo "=== $f ==="
    curl -s http://127.0.0.1:9100/state | jq '.stats'
done

Recipe 6 โ€” "swap to a new board without restarting"

The same server can host any board. POST /load to swap without opening a new tab.

curl -X POST http://127.0.0.1:9100/load \
    -H 'Content-Type: application/json' \
    -d '{"path":"/home/adom/project/foo/foo.kicad_pcb"}'
adom-cli hydrogen webview refresh --name "PCB: <old-filename>"

(The tab name keeps the original board's filename โ€” a future version can update the title on /load.)

Recipe 7 โ€” "inspect the rendered DOM via /eval"

Need to know what's actually visible? Eval a snippet inside the page.

curl -X POST http://127.0.0.1:9100/eval \
    -H 'Content-Type: application/json' \
    -d '{"code":"return Array.from(document.querySelectorAll(\".layer-group\")).map(g => ({layer: g.dataset.layer, hidden: g.classList.contains(\"hidden\"), elements: g.children.length}))"}'
# {"id":"a1b2c3"}

# Read result via /console (the UI logs results)
curl -s http://127.0.0.1:9100/console | jq '.messages[-1]'

Recipe 8 โ€” "headless render-to-screenshot pipeline"

Combine serve, layer toggles, and adom-cli hydrogen screenshot to build per-layer images for a doc:

kicad-pcb-viewer view board.kicad_pcb --port 9100 &
sleep 2
for layer in F.Cu B.Cu F.SilkS Edge.Cuts; do
    # Hide everything except this layer + Edge.Cuts (always nice to see outline)
    body=$(jq -nc --arg L "$layer" '
        {"F.Cu":false,"B.Cu":false,"In1.Cu":false,"In2.Cu":false,
         "F.SilkS":false,"B.SilkS":false,"F.Mask":false,"B.Mask":false,
         "F.Paste":false,"B.Paste":false,"F.Fab":false,"B.Fab":false,
         "F.CrtYd":false,"B.CrtYd":false,"Edge.Cuts":true} |
        .[$L] = true')
    curl -s -X POST http://127.0.0.1:9100/layers -H 'Content-Type: application/json' -d "$body" >/dev/null
    sleep 0.5
    adom-cli hydrogen screenshot panel --name "PCB: $(basename board.kicad_pcb)"
done

What it parses

  • (footprint โ€ฆ) โ€” placement (at), rotation, child pads + graphics
  • (pad โ€ฆ) โ€” rect, roundrect, circle, oval, with drill (round or oval slot), through-hole and SMD
  • (segment โ€ฆ), (arc โ€ฆ) โ€” copper traces with width
  • (via โ€ฆ) โ€” size + drill
  • (zone โ€ฆ) โ€” filled polygons (post-fill state preferred, falls back to outline if filled_polygon not present)
  • (gr_line/arc/circle/rect/poly โ€ฆ) โ€” top-level graphics on any layer
  • (fp_line/arc/circle/rect/poly โ€ฆ) โ€” footprint-level graphics (silk, fab, courtyard)
  • (layers โ€ฆ) โ€” layer table with index, kind, alias
  • Pad-side (layers โ€ฆ) lists with *.Cu / *.Mask / *.Paste wildcards expanded to the matching concrete layers

What it doesn't (yet)

  • Schematic (.kicad_sch) rendering โ€” different format, different scope.
  • Net highlighting / cross-probing.
  • File watching โ€” call POST /load to reload after edits.
  • Refdes / value labels overlaid on the board (too cluttered at default zoom; could be added as a togglable text overlay layer).
  • Bezier curves on gr_curve โ€” currently approximated as polylines.

Pane placement (important)

The viewer ALWAYS opens in a pane that is NOT the VSCode pane. If a non-VSCode pane already exists, the tab joins it; otherwise the VSCode pane is split vertically and the tab lands in the new pane.

This rule lives in skills/app-creator ยง4 โ€” it applies to every Adom app, not just this one. Putting webviews as sibling tabs inside the VSCode pane covers the editor and is wrong.

Files in this repo

bin/kicad-pcb-viewer    # Python CLI (subcommands: view, serve, install, shutdown, version)
src/parser.py           # s-expression tokenizer + per-layer extractor
src/server.py           # HTTP server, all endpoints, asset loading (source + zipapp)
src/ui.html             # SVG renderer, pan/zoom, layer panel, console fwd, eval poller
docs/icon.svg           # Brand-teal favicon (also the tab icon)
docs/SKILL.md           # AI usage doc deployed by `kicad-pcb-viewer install`
samples/                # BMA400 + BMI270 reference boards (from adom-inc/bosch-molecules)
scripts/build.sh        # Builds dist/kicad-pcb-viewer-linux zipapp (~22 KB)

Repo

adom-inc/kicad-pcb-viewer โ€” private. Releases attach a single-file zipapp (kicad-pcb-viewer-linux).

KICA
kicad-pcb-viewer-linux 27 days ago
Linux x86_64 zipapp v0.1.0 (~22 KB, Python 3.10+ stdlib)
21.4 KB
MD
SKILL.md 27 days ago
SKILL.md โ€” AI usage doc, deployed by 'kicad-pcb-viewer install'
4.4 KB

Install notes

Adom Docker CLI install steps
gh release download v0.1.0 --repo adom-inc/kicad-pcb-viewer --pattern kicad-pcb-viewer-linux -D /tmp && sudo install -m 0755 /tmp/kicad-pcb-viewer-linux /usr/local/bin/kicad-pcb-viewer && kicad-pcb-viewer install

Screenshots

AI Skill โ€” how Claude uses this app

Edit AI Skill

kicad-pcb-viewer

A 2D viewer for .kicad_pcb files, rendered as SVG inside a Hydrogen webview panel. Use when the user wants to inspect a KiCad PCB without opening KiCad itself, see traces / pads / silk on a flat board view, or share what a board looks like in the editor.

For 3D inspection (.glb / STEP), use the existing 3D viewers โ€” this app is deliberately 2D-only.

Trigger phrases

  • "view this kicad pcb", "open .kicad_pcb", "show me the board"
  • "what does this PCB look like", "preview the pcb", "render the board"
  • "view layer F.Cu / B.Cu / silkscreen", "show pcb layers"
  • "kicad-pcb-viewer", "pcb viewer", "open the board layout"

Quick start

kicad-pcb-viewer view path/to/board.kicad_pcb

This starts the server, opens a Hydrogen webview tab in a non-VSCode pane, and renders the board. Pan with click-drag, zoom with the scroll wheel, toggle layers in the sidebar, click Fit to reset, click Flip to mirror.

Subcommands

CommandPurpose
view <path> [--port N]Start server + open webview tab. Default.
serve <path> [--port N]Start server only (no tab).
shutdown [--port N]Stop a running viewer.
installDeploy this SKILL.md to ~/.claude/skills/kicad-pcb-viewer/.
versionPrint version.

Default port: 9100. The server is single-instance per port.

HTTP API (AI-drivable)

Everything the UI does is also reachable via HTTP. Useful for scripting view changes, taking annotated screenshots, or driving from other tools.

MethodPathAction
GET/stateCurrent phase, file, stats, bbox, layer visibility.
GET/boardFull parsed board JSON (footprints, pads, traces, vias, zones, gr).
GET/layersPer-layer info: idx, kind, alias, visible.
POST/layersBody: {name: bool, โ€ฆ} โ€” toggle layer visibility.
POST/loadBody: {path: "..."} โ€” open a different .kicad_pcb.
POST/fitReset view to fit the board.
POST/flipMirror board horizontally (B-side viewing).
GET/consoleRecent UI-side console messages (forwarded).
POST/evalBody: {code: "..."} โ€” queue a JS snippet for the UI.
GET/eval/pendingUI poller calls this to get pending snippets.
POST/shutdownGraceful exit.

Example: get stats for a board without opening a tab.

kicad-pcb-viewer serve board.kicad_pcb --port 9100 &
sleep 1
curl -s http://127.0.0.1:9100/state | jq '.stats'
curl -s -X POST http://127.0.0.1:9100/shutdown

Example: hide every layer except Edge.Cuts for a board-outline screenshot.

curl -X POST http://127.0.0.1:9100/layers -H 'Content-Type: application/json' \
  -d '{"F.Cu":false,"B.Cu":false,"F.SilkS":false,"B.SilkS":false,"Edge.Cuts":true}'

What it parses

The s-expression parser handles:

  • (footprint โ€ฆ) with (at โ€ฆ) placement, rotation, and child pads
  • (pad โ€ฆ) shapes: rect, roundrect, circle, oval (drilled or SMD)
  • (segment โ€ฆ), (arc โ€ฆ) copper traces with width
  • (via โ€ฆ) with size + drill
  • (zone โ€ฆ) filled polygons (post-fill state if present, else outline)
  • (gr_line/arc/circle/rect/poly โ€ฆ) top-level graphics
  • (fp_line/arc/circle/rect/poly โ€ฆ) footprint graphics (silk, fab, courtyard)
  • (layers โ€ฆ) definitions and (pad-side) (layers โ€ฆ) lists with *.Cu, *.Mask, *.Paste wildcards expanded

What it does NOT do (yet):

  • Schematic rendering (.kicad_sch)
  • Net highlighting / cross-probing
  • Real-time sync if the file changes on disk (call POST /load to reload)
  • Refdes / value labels overlaid on footprints

Pane placement

The viewer ALWAYS opens in a non-VSCode pane. If a non-VSCode pane already exists in the workspace, the tab joins that pane; otherwise the VSCode pane is split vertically and the tab lands in the new pane. Never as a tab inside the VSCode pane itself. See skills/app-creator ยง4.

Files in this app

  • bin/kicad-pcb-viewer โ€” Python CLI entry point.
  • src/parser.py โ€” s-expression tokenizer + per-layer extractor.
  • src/server.py โ€” HTTP server with all endpoints.
  • src/ui.html โ€” single-file frontend (SVG render, pan/zoom, layer toggles).
  • docs/icon.svg โ€” brand-teal favicon (also embedded as the tab icon).
  • samples/ โ€” checked-in reference boards for testing.

Repo

adom-inc/kicad-pcb-viewer (private). Releases attach a single-file zipapp kicad-pcb-viewer-linux that runs on any Python 3.10+ system.

Sub-Skills
?
What are Sub-Skills?

Sub-skills are community-contributed AI skill extensions for this component. They teach AI assistants about specific tools, configurators, or workflows.

Examples:

  • A manufacturer’s configuration tool for a motor controller
  • A community-written design guide for an amplifier circuit
  • An automated test/validation script for a sensor module

How to add one: Click Add Sub-Skill, provide the URL to your skill and a brief description. Submissions are reviewed by the Adom team before going live.

No sub-skills yet. Be the first to contribute one!

๐Ÿ”Ž How Claude finds this page (discovery snippet)

This page opts into Adom Wiki auto-discovery. When a user working in Claude Code mentions any of the trigger phrases below, Claude can proactively suggest this page. The pitch is exactly what Claude will say.

Pitch
"Open a .kicad_pcb in a Hydrogen webview โ€” flat 2D render, layer toggles, pan/zoom โ€” without launching KiCad on the desktop."
Triggers
"view kicad pcb", "view .kicad_pcb", "open .kicad_pcb", "preview pcb", "show me the board", "what does this pcb look like", "render the board", "render kicad board", "pcb viewer", "2d pcb viewer", "inspect pcb", "inspect board layers", "view board layers", "toggle pcb layer", "show pcb silk", "show f.cu", "show b.cu", "flip pcb", "back side of board", "pcb without kicad", "kicad-pcb-viewer"