Free, offline, scriptable visual diff for PCB Gerber files — and schematic PDFs.
gerber-diff compares two revisions of a board's fabrication data and shows you
exactly what changed: per layer (or per schematic page), with a red/green
overlay, in a self-contained HTML report you can attach to a review or archive.
It runs entirely on your machine — nothing is uploaded — and the same engine
drives a command line you can wire into CI and a small desktop GUI.
Status: early alpha (v0.10). Gerber and schematic-PDF diff both work, via a CLI (
gdiff), a desktop GUI (gdiff-gui), and a reusable GitHub Action that comments the diff on pull requests.
The native viewer (gdiff-gui) — pick Before and After, then step through layers
(changed first) and compare each with overlay / Before / After / split / swipe /
onion, with pan and zoom, all in one window:
The self-contained HTML report it writes for sharing — a per-layer summary plus a colour-blind-safe orange (removed) / blue (added) / grey (unchanged) overlay, on a real KiCad board:
The launcher:
Good Gerber diff tools exist but are either closed/paid
(gerbdiff.com), tied to one EDA tool
(KiRi for KiCad), or thin wrappers around a
native viewer (GrbDiff over gerbv).
None of them are FOSS and cover Gerber + schematic in one lightweight,
cross-platform, scriptable package. That's the gap this fills.
- Compare two folders or zip archives of Gerber/drill files (fab packages work as-is), or two schematic PDFs — the mode is auto-detected.
- Excellon drill files diff natively: holes render as true circles at their tool diameter (a moved or resized hole shows as removed + added), with routed slots surfaced as a warning rather than silently skipped.
- PDF pages pair by text content, so an inserted or removed schematic sheet no longer makes every later page report as changed (order-based fallback for scanned PDFs).
- Automatic layer detection and pairing via
gerbonara'sLayerStack: layers pair by identity (top copper, bottom mask, …), so pairing survives a board being renamed between revisions — with a filename fallback for anything it doesn't recognise (drills, unusual layers). - Native raster rendering via
pygerber(Gerber) andpypdfium2(PDF) — no cairo / no system libraries, so it behaves the same on Windows, macOS and Linux. Layers render in parallel across CPU cores (~2.6× faster on an 18-layer board;--jobs 1for serial). - Colour-blind-safe overlay: blue = added, orange (hatched) = removed, grey = unchanged — meaning never relies on hue alone, and the changed region is marked.
- Self-contained HTML report with an interactive viewer per changed layer (split side-by-side with synchronized pan/zoom · swipe · onion-skin · Before/After · overlay), lead-with-the-answer table (changed-first, "only changed" filter, jump links), light/dark theme.
- A single-window native viewer (
gdiff-gui): pick Before and After up top (inputs stay editable, re-compare any time), with the layer-by-layer diff (changed first) filling the window below — overlay / Before / After / split / swipe / onion and pan-zoom, keyboard-driven (←/→ layers ·1–6modes ·+/-zoom ·Homefit). Settings persist defaults. Plus a CLI (gdiff, alsopython -m gerberdiff). --fail-on-diffexit code,--jsonmachine-readable summary, and--summary-mdMarkdown summary for CI.- Git-native:
gdiff v1.0 HEAD --git gerbers/diffs a directory as it exists at two refs (read-only, viagit archive— no checkout juggling). - GitHub Action that uploads the HTML report and posts/updates a PR comment.
- Accessible: keyboard-operable GUI (Tab + Enter, focus rings), colour-blind-safe
diff, and a co-registration warning when two exports don't share a datum. Screen-reader
users should use the
gdiffCLI +--json(Tkinter exposes no accessibility tree).
- Structural (net-level) schematic diff, beyond pixel diff.
Grab the latest build from the Releases page — nothing else to install:
GerberDiffSetup.exe— installer; adds a Start-menu (and optional desktop) shortcut. Recommended for most people.GerberDiff-portable.exe— single self-contained file; runs from anywhere, no install. Handy on a locked-down machine or a USB stick.
It is unsigned hobby software, so Windows SmartScreen shows "Windows protected
your PC / unknown publisher" on first run — click More info → Run anyway.
To confirm a build is intact, run GerberDiff.exe --selftest (prints OK).
The project uses uv for development, but it is a
standard pyproject.toml project, so plain pip works too.
# with uv (installs the right Python automatically)
uv sync
uv run gdiff --help
# or with pip, into a virtualenv
python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e .
gdiff --help# Compare two Gerber revisions (folders) and write a report
gdiff path/to/rev-old path/to/rev-new -o diff-report.html
# Compare two schematic PDFs (auto-detected), page by page
gdiff rev-old.pdf rev-new.pdf -o schematic-diff.html --dpi 200
# Higher resolution, fail if anything changed, and emit a JSON summary (for CI)
gdiff rev-old rev-new -o report.html --dpmm 40 --fail-on-diff --json diff.json
# Diff the gerbers/ directory as it exists at two git refs (no checkout needed)
gdiff v1.0 HEAD --git gerbers/
# Or launch the desktop GUI
gdiff-guiGet a layer-by-layer diff on every pull request that touches your fab outputs. The action runs the diff, uploads the self-contained HTML report as an artifact, writes a step summary, and posts (or updates) a PR comment:
name: gerber-diff
on:
pull_request:
paths: ["gerbers/**"]
permissions:
contents: read
pull-requests: write # for the PR comment
jobs:
diff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: Materialize the base revision
run: |
git worktree add /tmp/base ${{ github.event.pull_request.base.sha }}
- uses: Cimos/Gerber-Diff-Tool@main
with:
old: /tmp/base/gerbers
new: gerbers
fail-on-diff: "false" # set "true" to block merges on changesInputs: old, new (required), dpmm, dpi, threshold, fail-on-diff,
comment, report-artifact-name. If your Gerbers are generated rather than
committed, add your export step before the action and point old/new at the
two output folders.
The HTML report is self-contained — open it in any browser, no assets folder
required. --json writes per-layer counts and an overall any_changes flag.
gerbers ─▶ pairing ────▶ render each layer ─▶ align ─▶ pixel diff ─▶ HTML report
(gerbonara) (pygerber → PNG) (by bbox) (numpy XOR)
PDFs ────▶ pages ───────▶ render each page ──▶ invert ─▶ pixel diff ─▶ HTML report
(by index) (pypdfium2 → PNG) (numpy XOR)
The diff engine (gerberdiff.pairing, .diff, .report) has no GUI and no
renderer baked in — it is plain functions over dataclasses, which is what keeps
it testable and scriptable. Renderers live behind gerberdiff.render (Gerber)
and gerberdiff.pdfdiff (PDF) so they can be swapped without touching the diff
logic.
uv sync --extra dev
uv run pytest --cov=gerberdiff # 70+ tests, ~98% coverage
uv run ruff check . && uv run ruff format --check .CI runs ruff (lint + format) and the test suite with a coverage gate on
Ubuntu / Windows / macOS × Python 3.12 / 3.13 (see .github/workflows/ci.yml).
uv sync --extra build
uv run pyinstaller GerberDiff.spec --noconfirm # -> dist/GerberDiff/GerberDiff.exe
dist\GerberDiff\GerberDiff.exe --selftest # prints OK if the bundle is intactPushing a v* tag triggers .github/workflows/release.yml, which builds the
one-folder app, compiles the Inno Setup installer (GerberDiffSetup.iss) and a
portable one-file exe, and attaches both to a GitHub Release. Release history
lives in CHANGELOG.md.
gerber-diff is free and MIT-licensed. If it saved you from a bad board respin and you'd like to say thanks, you can buy me a coffee.
Found a bug or have an idea? Open an issue — the bug-report form asks for the few details (version, OS, what you compared) that make it fixable on the first pass.
MIT © 2026 Simon Maddison


