A free, fast diff checker for text and code that runs entirely in your browser. Paste two versions and see every added, removed and changed line — with the exact words highlighted — instantly. No signup, no upload, nothing leaves your device.
Live site: https://diffhero.app
License: source-available, not open source. No LICENSE file means all rights reserved by default — this repo is public so the "nothing you paste is uploaded" claim is checkable, not an invitation to redistribute or relaunch it as your own product.
- Syntax highlighting — a dependency-free lexer for ~17 languages (JS/TS, Python, JSON, HTML/XML, CSS, SQL, YAML, Go, Java, PHP, and a generic C-like mode), chosen per page and switchable in-tool. Composes cleanly with the word-level diff.
- Word-level highlighting — an LCS line diff plus a token-level pass, so a changed line shows exactly which words differ.
- Collapse unchanged regions — long identical runs fold to a click-to-expand ("Show N unchanged lines"), so differences stay front and centre in big files.
- Split & unified views with line-number gutters; the choice is remembered.
- Similarity %, addition/deletion counts, and jump-to-change navigation (buttons + Alt+↑/↓).
- File drag-and-drop (local
FileReader— nothing is uploaded), Load example, swap, clear, and a wrap toggle. - Formatting-aware JSON — both sides are parsed and re-printed before diffing, so formatting noise disappears and only real value changes show.
- Ignore whitespace / ignore case, and CRLF-safe line-ending handling.
- Share link (both inputs encoded in the URL), Copy result, and
Download
.diff(standard unified patch with@@hunks). - Fast on large files — common prefix/suffix are trimmed before the diff, so a small edit in a big file only compares the changed middle.
- Private by design — every comparison runs client-side; there is no backend.
- Image diff (added 2026-07-21) — pixel-level comparison of two uploaded
images on a
<canvas>, adjustable sensitivity, changed regions highlighted over the original. Same client-side-only guarantee as the text tools.
No dependencies, no build tooling beyond Node (used only to generate the static pages).
node engine/build.mjs # regenerate the whole static site
node scripts/dev-server.mjs # preview at http://localhost:4173Edit and rebuild:
site.config.mjs— name, domain, and (optional) analytics/ad IDs.pages.mjs— the diff tool markup + the long-tail landing pages (one row per format/language). Add a row, file its slug inGROUPS, rebuild, done. A slug that isn't in a group fails the test suite, because the navigation rail renders fromGROUPSand the page would otherwise vanish from site nav.content.mjs— home, about, privacy, terms, contact.assets/—style.css(design tokens + components) andapp.js(the whole client-side engine: diff, highlighting, views, share/export).
There are two page shells, chosen per page type in engine/build.mjs and built
in engine/template.mjs. Neither is a centred column — a single 1080px .wrap
holding a hero and a stack of sections was the shape of every page here until
July 2026, and it is what made the site read as templated.
| Shell | Used by | Shape |
|---|---|---|
app |
homepage + the 50 diff/ pages |
Tool-first workbench: dense header strip, the tool edge-to-edge across the content column, then the supporting prose in a measured column with the contextual links in the margin. |
docs |
guides, articles, about/privacy/terms/contact, translated pages | Reading layout: navigation rail, a ~70ch measure, and a sticky on-page contents rail generated from the page's own <h2>s. |
Both are full-bleed with a persistent left rail and a 46px top bar. The rail is
site navigation, in the same position on every page; the in-content
relatedLinks() window stays curated and contextual. Below 980px the rail
reflows below the content via flex order, so the tool still opens the page
on a phone.
Everything else — index.html, the diff/ pages, sitemap.xml, robots.txt,
llms.txt — is generated by engine/build.mjs from the files above.
The site is pre-built — the static HTML is committed, so there is no build
step. Just point a static host at the repo root. Config for the two hosts that
serve the extensionless clean URLs the canonicals use (/diff/text-compare) is
included: _headers (Cloudflare Pages and Netlify) and netlify.toml.
- Cloudflare dashboard → Workers & Pages → Create → Pages → Connect to Git → pick this repository.
- Build settings: Framework preset: None, Build command: (leave empty),
Build output directory:
/. Save and deploy. - Custom domains → add
diffhero.app(andwwwif you want it), and Cloudflare provisions HTTPS automatically.
Cloudflare Pages serves text-compare.html at /diff/text-compare and
redirects the .html form to it, matching the canonicals with no extra config.
Add new site → import this repo. netlify.toml sets publish = repo root and no
build command; "Pretty URLs" handles the clean paths. Add the domain under
Domain management.
To turn them on, set GA_ID / ADSENSE_PUB / ADSENSE_SLOT in
site.config.mjs, run node engine/build.mjs, and commit the regenerated files.
The single ad slot then renders (it's a reserved placeholder until configured, so
turning it on causes no layout shift).
If you edit
site.config.mjs,pages.mjs, orcontent.mjs, runnode engine/build.mjsand commit the regenerated HTML — the host does not build for you.
© Diffhero. All rights reserved.