npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@laveez/ccvd

v0.1.2

Published

Claude Code Visual Diff: browse two versions of a site side by side with a live, exact pixel diff between them

Readme

ccvd

Claude Code Visual Diff

Browse two versions of a site side by side, with an exact pixel diff between them.

npm CI License: MIT Node.js 22.18+ Claude Code plugin


ccvd puts the before and after of a web change next to each other, with every changed pixel marked red in a pane between them. Scroll, click or type on one side and the other side does the same. When a view is worth keeping, one click saves it as an image. Claude Code runs the same engine without the window, to make before and after images for pull requests and to check its own UI work.

ccvd comparing two versions of a demo site

Contents


Quick Start

Try it on the bundled demo site:

npx @laveez/ccvd demo

Compare your own two versions, for example two local dev servers:

npx @laveez/ccvd http://localhost:8101 http://localhost:8102

ccvd opens the compare view in your browser. It needs Node.js 22.18 or newer and a Chromium-based browser: Google Chrome works, or install Playwright's Chromium with npx playwright-core install chromium.

To keep it installed, npm install -g @laveez/ccvd; the command is then ccvd, as in the examples below.


Claude Code Plugin

Install the plugin, and Claude knows when and how to use ccvd:

claude plugin marketplace add laveez/ccvd
claude plugin install ccvd@ccvd

Then ask for what you want to see:

  • "Show me main side by side with this branch."
  • "Compare the site before today's merges with now, page by page."
  • "Make before and after images of the pricing page at desktop and phone sizes for the PR."
  • "Check that this CSS refactor changed nothing visible."

The skill has Claude check out both versions, start a server for each, run ccvd, look at every diff before reporting, and clean up afterwards.


Live View

ccvd <before> <after>

| Part | What it does | |---|---| | Before, Diff, After | The two pages, and the diff between them: changed pixels red, the rest of the before page faded | | Linked | Mirrors clicks, typing and links to both sides. Off, only the side you use gets the input | | Scrolling | Always in sync: the side you scroll leads, the other follows to the same offset and is put back if it drifts | | Path field, back, forward, reload | Navigate both sides together | | Size | Fit to pane, or a preset from desktop 1920×1080 down to small phone 390×844, at 1x or 2x | | Diff | Shows or hides the diff pane. Hidden, the two pages share the width and ccvd stops its regular captures of pages that keep animating. Your browser remembers the choice | | Reduced motion | On by default: renders both sides with prefers-reduced-motion: reduce, so animated pages hold still. Turn it off to see the animations; ccvd suggests turning it back on when a page keeps animating | | Before + After, Before + Diff + After | Open the current view as one image in a new tab, or download it |

The panes stream while pages move, and the diff pane marks its image as out of date. Once both sides are still, ccvd takes lossless captures, diffs them, and the diff pane shows the changed-pixel count.


Images with ccvd shot

ccvd shot http://localhost:8101 http://localhost:8102 --path / --path pricing.html --size desktop --size phone

For every page and size, ccvd writes five PNGs to ccvd-shots/:

| File | Contents | |---|---| | pricing-1920x1080-before.png | The before page | | pricing-1920x1080-after.png | The after page | | pricing-1920x1080-diff.png | Changed pixels in red over the faded before page | | pricing-1920x1080-pair.png | Before and after, labelled, side by side | | pricing-1920x1080-triple.png | Before, diff and after, labelled |

Before, diff and after of the demo pricing page

Captures are full page by default, up to 16,384 px tall (--viewport for the visible part only), taken after the page has loaded, fonts are ready, lazy content has had a chance to load, and animations are frozen. --json prints each result with its changed and total pixels and both sides' HTTP status, so scripts and Claude can check the outcome.


Options

ccvd <before> <after> [options]        Open the live compare view
ccvd shot <before> <after> [options]   Save before, after and diff images
ccvd demo [options]                    Try the live view on a bundled demo site

<before> and <after> are http(s) addresses; host:port means http://host:port.

| Option | Applies to | Description | |---|---|---| | --size <size> | all | fit (live view default), a preset, or WIDTHxHEIGHT, with @2 for 2x. Repeatable for shot (default desktop) | | --port <port> | live, demo | Port for the view, default 2283 (ccvd on a phone keypad); the next free port if taken | | --no-open | live, demo | Print the view's address instead of opening it | | --path <path> | shot | Page to capture, resolved against each address like a link; repeatable. Without it, both addresses as given | | --viewport | shot | Capture only the viewport | | --out <dir> | shot | Folder for the images, default ccvd-shots | | --wait <ms> | shot | Extra wait after the pages settle, default 300 | | --json | shot | Print the results as JSON | | --browser <name> | all | chromium, chrome, msedge or the path to a Chromium-based browser | | --no-reduced-motion | all | Let pages animate. By default both sides render with prefers-reduced-motion: reduce |

Size presets: desktop 1920×1080, laptop 1440×900, small 1280×800, tablet 768×1024, phone 440×956, phone-small 390×844.


How It Works

flowchart LR
    subgraph Browser["Your browser"]
        UI["Compare view"]
    end
    subgraph ccvd["ccvd (Node)"]
        Server["Local server<br/>127.0.0.1:2283"]
        Session["Session<br/>sync, settle, diff"]
    end
    subgraph Chromium["Headless Chromium"]
        Before["Before page"]
        After["After page"]
    end
    UI <-- "frames, input<br/>(WebSocket)" --> Server
    Server <--> Session
    Session <-- "screencast, captures,<br/>input (CDP)" --> Before
    Session <-- "screencast, captures,<br/>input (CDP)" --> After

    style UI fill:#38608c
    style Session fill:#2d4a2d
    style Before fill:#4a3d2d
    style After fill:#4a3d2d
  • One engine for both sides. Both pages render in the same Chromium, each in its own browser context, at the same size and pixel ratio. Equal pages give equal pixels, so the diff has no noise to filter: any red is a real change.
  • Exact diff. A pixel counts as changed when any of its RGBA bytes differ. Changed pixels are pure red, as in roborazzi; unchanged ones show the before page faded toward white. Where only one page has content, the area counts as changed.
  • Calibration. test/baselines shows seven known changes, from one pixel to a layout shift, with the before, diff and after images ccvd makes of them. The tests check every one.
  • Live, then exact. While pages move, the panes show Chromium's screencast. When both sides stop changing for 150 ms, and after a scroll once both report the same offset, ccvd captures both as PNG, decodes and diffs them in Node, and streams the exact images and the diff. A page that keeps animating is captured every 750 ms at first, backing off to every 4 s, and never while you scroll, click or type. Exact images replace the streamed ones only when no newer frame arrived during the capture, so the panes never jump back.
  • GPU and fresh frames. The hidden browser draws on the GPU, several times cheaper than drawing in software for pages with blurs and animations. When the GPU cannot start, ccvd restarts the browser without it. The screencast has one frame on its way at a time and keeps only the newest one meanwhile, so the end of every scroll reaches the panes.
  • Input. Mouse, wheel and keys from the view go to the pages as Chromium input events. The side you last touched sets the scroll offset: its window scroll moves the other side, which is put back if it drifts on its own. The hidden browser scrolls without smooth or threaded scrolling, so a page's reported offset matches what it draws. While linked, the touched side's clicks, keys and link navigations happen on both.

Security

The view's server listens on 127.0.0.1 only, and the address ccvd prints carries a random token that the page controls and image exports require. Requests from other hosts or origins are refused, and the compared pages can only go to http(s) addresses. Chromium runs with its sandbox; where the sandbox cannot start, ccvd says so and runs without it. The compared pages cannot reach each other: they only report scroll positions, which ccvd checks are numbers from the top-level page. See SECURITY.md for details and how to report a vulnerability.


Limitations

  • Chromium only. Firefox and Safari rendering are not compared.
  • Linked clicks land at the same spot on both pages. Where the layouts differ, the click on the other side can hit something else; unlink to use one side at a time.
  • Window scroll syncs; scroll areas inside a page do not. The wheel scrolls them only on the side under the pointer.
  • A layout shift turns everything below it red, because the diff is exact. Look for where the red starts.
  • Full-page captures stop at 16,384 px, so endless feeds finish and images stay a sensible size.

Contributing

Contributions are welcome. See CONTRIBUTING.md for setup, checks and the release process, and AGENTS.md if you work with a coding agent.

git clone https://github.com/laveez/ccvd.git
cd ccvd
npm ci
npm run build
node dist/bin/ccvd.js demo

License

MIT