A powerful, local-first web application for pixel-perfect comparison of websites, supporting live iframes, proxied content, and screenshots. Perfect for:
- π¨ Visual regression testing
- π± Responsive design QA
- π Before/after comparisons
- π Cross-browser testing
- π» Local development vs production
Status: Only the Live (proxy) + Overlay mode is fully tested and recommended right now. Other modes (Live iframe, Screenshot; Side-by-side) are available and generally work, but are considered experimental.
- Live (iframe): Direct iframe embedding for
localhostandfile://URLs - Live (proxy): Server-side proxy to bypass X-Frame-Options restrictions
- Screenshot: Puppeteer-based screenshots for sites that block all iframe methods
- Overlay: Stack two pages with opacity/swipe/blend controls
- Onion Skin: Adjust opacity to see through layers
- Swipe: Drag a handle to reveal differences
- Difference Blend: Mix-blend-mode for visual diff
- Side-by-Side: Compare pages next to each other with synchronized scrolling
- β
Compare
https://URLs,http://localhost, andfile://paths - β Adjustable viewport width (320px - 3840px) with range slider
- β Synchronized scrolling in both overlay and side-by-side modes
- β Collapsible control drawer for maximum comparison area
- β Deep-linking support (share comparisons via URL)
- β Natural page scrolling (no height constraints)
- β Responsive design with touch support
- Node.js 16.0.0 or higher
- npm 8.0.0 or higher
# 1. Clone the repository
git clone https://github.com/Softinator-TechLabs/diffnator.git
cd diffnator
# 2. Install dependencies (includes Puppeteer + Chromium)
npm install
# 3. Start the server (includes web server + proxy)
npm start
# The app will be available at http://localhost:8086First-time setup: The npm install will download Chromium (~170-250MB) for Puppeteer. This is normal and required for screenshot functionality.
Using npm:
npm start # Start the server (web server + proxy + screenshot service)
npm stop # Stop all running server processes
npm restart # Stop and restart the server
npm run dev # Same as start (development mode)
npm run dev:visible # Start with visible browser (for debugging security checks)
npm run health # Check if server is runningUsing the CLI helper (easier):
./diffnator.sh start # Start everything
./diffnator.sh stop # Stop all services
./diffnator.sh restart # Restart all services
./diffnator.sh status # Check health
./diffnator.sh dev:visible # Debug mode with visible browser- Enter two URLs in the input fields
- Select a Render mode:
Live (iframe)for direct embeddingLive (proxy)for sites that block iframesScreenshotfor full compatibility
- Choose Overlay or Side-by-side comparison
- Adjust viewport width using the slider
- Click Refresh to reload
| URL Type | Live (iframe) | Live (proxy) | Screenshot |
|---|---|---|---|
http://localhost:3000 |
β Direct | β Proxied | β Captured |
https://example.com |
β Proxied | β Captured | |
file:///path/to/file.html |
β Served | β Served |
Notes:
file://URLs are automatically served via HTTP to bypass browser security- Screenshot mode uses a default 2000ms wait for content to load (adjustable via Wait field)
- Live modes support animations, hover effects, and interactive elements
- Onion (opacity): Slide the opacity control to blend pages
- Swipe: Drag the vertical handle to reveal differences
- Difference Blend: See visual differences highlighted
- Default: Smooth synced scrolling in Live (proxy) overlay. You can scroll while your mouse is over the viewport.
- Interact selector: choose who is interactive in overlay β
None,URL A (default), orURL B. - With
None, overlay captures scroll and forwards to both pages (best for performance). - With
URL AorURL B, clicks and inputs go to the selected page; the other still syncs scroll. - Note: In Live (iframe), interactivity/scroll sync may vary by site. Use Sideβbyβside for full interactivity across both pages simultaneously.
- Click β° button to toggle control drawer
- Drag swipe handle in overlay mode
# Run with visible Chrome (for CAPTCHA/security checks)
HEADLESS=false npm start
# Use custom port
PORT=9000 npm startShare comparisons by copying the URL:
http://localhost:8086/?u1=https://example.com&u2=http://localhost:3000&w=1440&ra=proxy&rb=proxy&mode=sbs
Parameters:
u1/u2: URLs to comparew: Viewport width (px)dpr: Device pixel ratio (1, 1.5, 2, 3)wait: Wait time before screenshot (ms)ra/rb: Render mode per URL (iframe,proxy,screenshot)mode: Comparison mode (overlay,sbs)om: Overlay mode (onion,swipe,blend)opacity: Opacity percentage (0-100)swipe: Swipe position percentage (0-100)
Compare production vs staging environments:
u1=https://staging.example.com
u2=https://example.com
render=proxy
mode=overlay
om=blend
Test different viewport widths:
u1=http://localhost:3000
u2=http://localhost:3000
w=375 (then adjust to 1440)
mode=sbs
Compare a saved HTML file with live site:
u1=file:///Users/you/Downloads/saved-page.html
u2=https://example.com
render=iframe
mode=overlay
- Frontend: Vanilla JavaScript, CSS Grid/Flexbox
- Backend: Express.js + Puppeteer
- Proxy System: Server-side fetch with HTML rewriting
- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
- Optimized scroll sync with
requestAnimationFrame - Debounced event handlers (50ms)
- Efficient DOM updates
Cause: Some sites block server-side access (e.g., Revolut, banking sites).
Solution: Switch to Screenshot mode which uses a headless browser.
Cause: Page needs more time to load dynamic content.
Solution: Increase the Wait (ms) value (try 3000-5000ms for heavy sites).
Cause: Site detects headless browser.
Solution: Run with visible Chrome to manually solve:
HEADLESS=false npm startCause: Another service is using the port (default: 8086).
Solution: Use a different port:
PORT=9000 npm startCause: Puppeteer keeps browser instances open.
Solution: Restart the server periodically using npm restart, or reduce concurrent screenshot requests.
Cause: Images reference external CDNs with CORS restrictions.
Solution: The app automatically proxies external assets in Live modes. If issues persist, try Screenshot mode.
Symptoms:
TimeoutError: Navigation timeout of 45000 ms exceededECONNREFUSEDerrors when trying to proxy localhost- Puppeteer warnings about deprecated versions
Solutions:
- Update dependencies (run in your project directory):
npm install puppeteer@latest cross-env --save-
Fix npm scripts for Windows (already updated in latest version):
- The
stopandrestartcommands now work cross-platform - Use
npm startinstead ofnpm run devon Windows
- The
-
Firewall/Network issues:
- Allow Node.js through Windows Firewall
- If comparing localhost URLs: Ensure the local server is actually running on that port
- DiffNator supports ANY localhost port (e.g.,
http://localhost:8000,http://localhost:5173, etc.) - Test your local server independently first: open it in a normal browser
- Common ports: 3000 (React), 5173 (Vite), 8000 (Django), 4200 (Angular)
- DiffNator supports ANY localhost port (e.g.,
- Check antivirus isn't blocking Chromium/Puppeteer
- Try increasing timeouts in Settings (Wait: 3000-5000ms)
-
Test with external URLs first:
URL A: https://example.com
URL B: https://google.com
If this works, the issue is with your local server setup, not DiffNator.
Examples of localhost URLs that work:
http://localhost:3000(React default)http://localhost:5173(Vite default)http://localhost:8000(Python/Django)http://localhost:4200(Angular)http://127.0.0.1:ANY_PORT
The ECONNREFUSED error means there's no server listening on that port - start your local dev server first!
- Windows-specific Node.js setup:
# Run as Administrator if you see permission errors
npm install -g windows-build-toolsContributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
MIT License - see LICENSE file for details
Built with:
Made with β€οΈ for designers and developers who care about pixel perfection.
Quick Commands:
npm start- Start everything (web server + proxy + screenshots)npm restart- Restart all servicesnpm stop- Stop all services