🔀 Diff Checker

Side-by-side or unified comparison for text, code, JSON, YAML, SQL and more. Up to 2 MB per side with virtual scrolling.

Ctrl+Enter
📏Capacity: Up to 2 MB / ~80,000 lines per side. Exact LCS diff for up to ~2,600 × 2,600 lines; fast hash-based diff above that. Inline word/char highlights for lines ≤ 400 characters. Virtual scrolling handles 50,000+ rows instantly.
Filter
Inline diff
Context

About this Diff Checker

A diff checker finds every addition, deletion, and change between two pieces of text. This tool uses LCS (Longest Common Subsequence) dynamic programming for exact results on files up to ~2,600 × 2,600 lines, automatically switching to a fast greedy hash-based algorithm for anything larger. Word-level and character-level inline highlighting pinpoints exactly which tokens changed within each modified line.

Capacity: Hard limit is 2 MB (~80,000 lines) per side. LCS diff (exact) runs below ~7 million cell-pairs; greedy diff (fast, slightly approximate) handles the rest. Inline diffing is enabled for lines under 400 characters. Virtual scrolling renders 50,000+ rows without any slowdown.

Features

↔ Split & Unified view

Toggle between side-by-side and classic unified patch format.

🔤 Word & char inline diff

Highlights exact words or characters that changed within modified lines.

📂 File upload & drag-drop

Upload any text file — JS, JSON, YAML, Python, SQL, Markdown and more.

⚡ Virtual scrolling

Handles 50,000+ diff rows smoothly via viewport-based rendering.

⬇ Download .diff patch

Export as standard unified patch — compatible with git apply.

🔧 Flexible options

Ignore whitespace, case, blank lines. Adjust context, wrap, line numbers.

🧭 Diff navigation

Jump to the previous or next change with one click.

🔒 100% private

All comparison runs locally in your browser — nothing is sent to a server.

Frequently Asked Questions

How large a file can I compare?

Up to 2 MB (~80,000 lines) per side. For files under ~2,600 lines each the tool uses exact LCS diff, guaranteed optimal. Above that it switches to a greedy hash-based algorithm which is very accurate for typical code files. For extremely large files with huge amounts of differences, splitting into smaller sections gives better accuracy.

What does 'Ignore Whitespace' do?

It collapses all consecutive whitespace (spaces, tabs) into a single space and trims leading/trailing whitespace before comparing. Two lines differing only in indentation or trailing spaces will be treated as identical.

What is the difference between Word and Character level diff?

Word level tokenises changed lines into words and punctuation marks, highlighting only the changed tokens — usually the easiest to read. Character level goes further and highlights individual characters, ideal for spotting single-character typos or subtle string mutations.

What is a .diff patch file?

A unified diff patch uses the standard format understood by git, svn, patch, and most code review tools. You can apply it with 'git apply changes.diff' to replay the changes onto the original file.

Why does my diff look approximate for very large files?

Above ~7 million line-pair cells, the tool switches from exact LCS (which would require hundreds of MB of memory) to a greedy hash-based algorithm. This finds most matching lines correctly but can occasionally miss optimal pairings in blocks with many identical lines. For precise diffs on large files, consider using git diff or the diff command locally.

Patch copied to clipboard!

Text and code diff: how the comparison is computed, and where it stops being exact

Comparing two versions of a file is not really a comparison problem, it is an alignment problem. Deciding which lines are equal is trivial; deciding which of the surviving lines in the new version correspond to which lines in the old one is what separates a readable diff from a wall of red and green. Every diff algorithm is an answer to that alignment question, and every answer trades accuracy against time and memory.

This tool answers it with the classic Longest Common Subsequence dynamic program, which is optimal: it produces the alignment with the fewest possible added and removed lines. That guarantee is not free. The DP table holds one 32-bit cell for every pair of lines, so the cost grows as the product of the two line counts, not their sum. The tool therefore caps exact LCS at 7,000,000 line-pair cells, roughly 2,600 lines against 2,600 lines.

Past that ceiling it falls back to a greedy hash-based pass that indexes every line of the right-hand document by content and walks the left-hand document once, taking the first still-available match. It runs in linear time and gets typical source files right, but because it never backtracks it can lock onto an early coincidental match and drag the rest of the alignment out of position. That is why very large diffs here are labelled fast rather than exact.

Before either algorithm runs, both inputs are normalised: CRLF and lone CR are rewritten to LF unconditionally. A Windows-authored file compared against a Unix-authored one will not light up as an all-lines-changed diff here, which is the single most common false positive in naive diff tools. Ignore whitespace, ignore case and ignore blank lines are separate opt-in filters layered on top of that.

Output is available as split (side-by-side) or unified rows, with word-level or character-level highlighting inside changed lines, adjustable context, hunk-to-hunk navigation, and export as a standard unified .diff patch. Each side accepts up to 2 MB, about 80,000 lines; the byte counter turns amber past 512 KB as an early warning that the comparison is about to get expensive.

How it works

Normalisation runs first and does more than clean up line endings. After the two documents are split into lines, the tool strips the longest common prefix and the longest common suffix before handing anything to the diff algorithm. A 40,000-line configuration file with one edited block in the middle only ever puts that block through the DP table, which is why files far larger than 2,600 lines still get exact LCS treatment as long as their differing region is small.

The LCS pass allocates a Uint32Array of (n+1) x (m+1) cells over the remaining middle band. At the 7,000,000-cell ceiling that array is about 27 MB; a naive 10,000-against-10,000-line comparison would need roughly 382 MB, which is why the ceiling exists at all rather than being a performance preference. When the band exceeds it, the greedy hash pass takes over: a Map from line text to its positions in the right document, one forward scan of the left, and a monotonically advancing cursor so matches can never cross.

Word and character highlighting inside a changed line is a second, independent LCS run on tokens rather than lines. Word mode tokenises with the pattern for a run of word characters or a single non-word character, so punctuation becomes its own token and a renamed identifier highlights cleanly. Two guards keep it cheap: lines longer than 400 characters get no inline highlight at all, and the token grid is abandoned above 80,000 cells, which in character mode means pairs longer than about 282 characters silently fall back to whole-line highlighting.

Rendering is virtualised. Rows are a fixed 22 pixels tall, and once a diff exceeds 300 rows only the slice inside the viewport plus twelve rows of buffer is mounted, which is what lets a 50,000-row result scroll smoothly. Turning on Wrap disables virtualisation entirely, because wrapped rows no longer have a predictable height, so a huge diff with Wrap enabled is dramatically heavier than the same diff without it.

Common uses

  • Review what a formatter, linter --fix run, or codemod actually changed before staging it, with character-level highlighting to catch the one line where it did something you did not intend.
  • Compare a rendered Kubernetes manifest or Helm output against the version currently deployed, where the interesting change is three characters inside a 900-line YAML document.
  • Diff two API responses or log excerpts pasted from different environments when neither is in version control and git diff is not an option.
  • Check a generated file (OpenAPI client, protobuf stub, lockfile) against its committed counterpart to see whether regeneration is a no-op.
  • Compare a .env or application.properties file between staging and production with Ignore Blank Lines on to answer the shape question before reading values.
  • Verify a copy-paste migration: paste the original and the rewritten version side by side and confirm nothing was dropped in the middle.
  • Produce a unified .diff patch for a reviewer who cannot access the branch, using the download button rather than reconstructing one by hand.
  • Isolate a single-character typo in a long string constant, regex, or URL by switching inline diff to Char level.
  • Compare two minified or single-line bundles by first running them through a formatter, since a one-line file is one line to any line-based diff.

Before you rely on the result

  • Ignore Blank Lines removes blank lines before comparing but the rendered text and line numbers are read back from the unfiltered original, so on inputs that contain blank lines the numbering can drift out of step. Use that option to answer whether two files are equivalent, not to quote a line number at someone.
  • In a block replacement, only the last removed line is paired with the first added line for inline highlighting. If you replace five consecutive lines with five others, four of them render as plain red and green rows and the one pairing that does get word-level marks is often between two unrelated lines.
  • A file that ends with a newline splits into one more (empty) trailing line than a file that does not, so a missing or added final newline legitimately shows up as a one-line difference. That is the tool reporting a real byte-level difference, not an artefact.
  • The Context setting affects display only. The exported .diff patch is always generated with three lines of context regardless of what the dropdown says, matching the conventional unified-diff default.
  • The patch is emitted with plain --- and +++ headers taken from the panel labels, with no diff --git line and no a/ b/ prefixes. Rename the labels to the file's real repository-relative path and apply it with git apply -p0; the default -p1 will strip the only path component and fail.
  • A greedy result on a very large file is approximate by design. Files with many identical lines (closing braces, repeated boilerplate blocks, generated fixtures) are exactly where its first-available-match rule goes wrong, so verify a suspicious large-file alignment with git diff before acting on it.
  • The collapsed rows that read as N unchanged lines hidden are static markers, not expandable folds. To see what is inside one, change Context to All lines and re-read the region.
  • Uploads are rejected if the first 4 KB contain a NUL byte, since that is the reliable signature of a binary file. Comparing compiled artefacts, images or archives is not supported, and pasting their bytes in as text will not work either.