Text Diff Checker
Compare two blocks of text and see exactly what changed β with word-level highlighting on edited lines, not just whole lines marked red or green. Unified or side-by-side view, runs entirely in your browser.
Getting a Cleaner Diff Out of Messy Input
- Turn on Ignore Whitespace before assuming two lines are actually different. A file re-indented from tabs to spaces, or re-wrapped by an editor, produces a wall of red-and-green noise that has nothing to do with real content changes.
- Sort JSON keys before diffing two API responses or config exports. Unordered key serialization is one of the most common sources of diffs that look meaningful but aren't β run both through the JSON Formatter's Sort Keys option first.
- Switch to side-by-side view for anything wider than a terminal line. Long lines wrap awkwardly in unified view; side-by-side keeps each version in its own column and is easier to scan for prose or long config values.
- Leave word-level highlighting on by default, but don't expect it on every changed line. It only applies to a clean single-line-for-single-line replacement β bigger restructuring still shows as whole-line changes, which is the more honest read anyway.
Features
How the Algorithm Decides What Counts as a Change
Line-level diffing sounds like it should be simple β just check whether each line matches β but naive line-by-line comparison breaks the instant a single line gets inserted early in a file, since every line after it now sits at a different index and appears to have "changed" even though nothing about its content did. The fix is the Longest Common Subsequence algorithm: it finds the longest run of lines that appear, in the same relative order, in both texts, and treats only what falls outside that shared backbone as an actual addition or deletion.
This is the same core idea behind the Unix diff command and Git's diff engine, which is why the output here reads the way any developer already expects a diff to read β a small, minimal set of changes rather than a cascade of false positives triggered by one early insertion.
The trade-off is cost: computing the longest shared subsequence takes time roughly proportional to the number of lines in the first text multiplied by the number of lines in the second, since the algorithm builds a table comparing every line of one against every line of the other. For a code file or a paragraph of prose that's effectively instant. It's also why the tool caps input at roughly two thousand lines per side β past that, the comparison table gets large enough that a browser tab can start to feel sluggish before it finishes, and a clear warning is more useful than a frozen page.
Word-Level Highlighting Has an Honest Boundary
Whole-line diffing tells you that a line changed but not what changed inside it β a single-character typo fix looks identical, in a plain line diff, to rewriting the entire sentence. Word-level highlighting closes that gap by running a second comparison pass, this time at the word level, whenever exactly one deleted line sits immediately next to exactly one added line β a clean, unambiguous replacement.
That scope is deliberate. If three lines got merged into one, or one line got split into three, there's no single old line to meaningfully compare against a single new one β any pairing would be a guess dressed up as an answer. Those cases display as plain block-level changes instead, which is a more honest result than word-level highlighting that's confidently wrong.
In practice this covers the majority of real edits, because most line changes are exactly that shape β a variable renamed, a default value adjusted, a typo fixed, a single argument added to a function call. Renaming greet(name) to greet(name, greeting) shows the inserted parameter highlighted on its own, with the rest of the line rendered as unchanged context, instead of forcing a reader to scan the full line character by character to spot what actually moved.
Whitespace, Case, and Blank Lines Are Three Different Kinds of Noise
It's tempting to lump these together as one "ignore formatting" switch, but each one solves a different problem and can hide a different kind of real change if left on by default. Ignore Whitespace trims leading and trailing space from each line β exactly what's needed after a re-indent, but it won't touch a line that's simply gone entirely.
Ignore Case is the right tool when comparing something like environment variable names or SQL keywords where casing conventions vary between environments but the underlying value doesn't. Ignore Blank Lines removes empty lines from consideration entirely, which is the setting most likely to be a mistake to leave on permanently β blank-line placement genuinely matters in some file formats, like YAML block scalars, where it can change what the content means. Turning all three on by habit trades away real signal for a quieter-looking diff.
A common real case: comparing a working .env.example against a teammate's local.env to figure out which variables are missing. Whitespace and case usually don't matter there, but a blank line separating two config sections might be the only thing telling you a whole block was accidentally deleted rather than just reordered β worth leaving visible rather than filtering out by default.
Choosing Between Unified and Side-by-Side
Both views come from the exact same underlying diff β switching between them never changes what counts as a change, only how it's laid out. Unified view lists every line in file order in a single column with a plus or minus marker, which reads naturally for short files or a handful of scattered edits, and matches what a Git pull request diff or a patch file looks like.
Side-by-side view puts the original in a left column and the modified version in a right column, row-aligned so a replaced line sits directly across from what it replaced β closer to how a word processor's track-changes view or a legal document redline reads, and generally easier to follow for prose, contracts, or config files with long values that wrap awkwardly in a single-column layout.
Code review workflows tend to favor unified view because it matches what a pull request diff already looks like, keeping the two representations consistent in a reviewer's head. Anyone comparing two drafts of written content β a blog post, a set of terms, a policy document β often gets more out of side-by-side, since reading two versions of a sentence stacked vertically is harder to track than reading them across from each other.
What a Line-Based Diff Won't Tell You
A diff built on line matching has real limits worth knowing before trusting it completely. Moved blocks β a function relocated to a different part of the file with no other edits β show up as a deletion at the old location and an addition at the new one, not as a "moved" annotation, since LCS has no concept of relocation, only of presence in a shared subsequence.
It's also strictly a text tool: binary files, images, and compiled output have no meaningful line-by-line representation to compare, so they're outside what this β or any text diff β can usefully show. For structured data specifically, a raw text diff can also surface noise that a format-aware comparison wouldn't, which is exactly why sorting JSON keys first, or running code through the HTML Beautifier before diffing markup, consistently produces a cleaner result than comparing two differently-formatted originals as-is.