Text diff checker with word-level highlighting
Compare two versions of any text and see exactly what changed — line by line, and word by word inside the lines that changed. A line edited by one word highlights that one word instead of painting the whole line red and green, which is the difference between reading a diff and hunting through one. Side-by-side or unified, whitespace and case options, and a unified diff patch you can hand to “patch”. Nothing is uploaded or saved.
- Nothing is uploaded or saved
- Works offline
5 lines · 152 characters
5 lines · 145 characters
These change what counts as a difference, not what is shown — the lines below are always exactly what you pasted. Ignoring blank lines is the one exception: those lines are dropped from both sides, so the line numbering visibly skips them.
Three lines of context per hunk, the same as “diff -u”. Apply it with “patch -p1 < file.txt.patch” from the directory holding the file.
--- a/file.txt +++ b/file.txt @@ -1,5 +1,5 @@ # Release checklist -Run the test suite on Node 22. +Run the test suite on Node 24. Bump the version in package.json. +Write the changelog entry. Tag the commit and push the tag. -Announce the release in #general.
How it works
- 1
Paste both versions
The older text on the left, the newer one on the right. The comparison runs as you type — there is no Compare button, because watching the result change while you edit is the whole reason to open a page instead of running diff in a terminal. Swap sides if you pasted them the wrong way round; the result flips with them.
- 2
Read the two levels of highlighting
A removed line is tinted on the left, an added line on the right. Where a removed line and an added line line up, the words that actually changed are picked out inside them: load the sample and line 2 shows “22” marked on the left and “24” on the right, with the other twenty-eight characters of the line left alone. When a line was rewritten rather than edited — fewer than a third of its characters survive — it is marked changed as a whole instead, because scattered highlights across a rewritten sentence are harder to read than none.
- 3
Decide what counts as a difference
Four options change the comparison, not the display: ignore whitespace at the ends of lines, ignore whitespace everywhere, ignore case, ignore blank lines. The text shown is always exactly what you pasted — an ignored difference disappears from the result, not from the line. Ignoring blank lines is the one that also removes rows, so the line numbers visibly skip; that is the honest way to show that a line was passed over rather than matched.
- 4
Switch the view and fold away what did not change
Side by side is one grid, so both columns scroll together and stay aligned even when a long line wraps. Unified is the single-column form that git and patch print, with a “-” or “+” at the start of each changed line. In either view, a long run of unchanged lines collapses into a single marker naming how many are hidden, with three lines of context kept on each side of every change; click the marker to open the run. On a 500-line file with one edit, that is the difference between a page of scrolling and a screen.
- 5
Export a patch that actually applies
Copy or download a unified diff: “--- a/…”, “+++ b/…”, and “@@” hunk headers with three lines of context, including the “\ No newline at end of file” marker when a side does not end with one. Set the file name so the paths match your repository. Export is switched off while any comparison option is on — a patch built from a comparison that ignored case would not rebuild the right-hand text, and a patch that looks right and does not apply is worse than no export at all.
Frequently asked questions
What does word-level highlighting change, and why not character-level?
Most free diff tools compare whole lines only: change one word and the entire line goes red on one side and green on the other, leaving you to find the actual edit yourself. This page runs a second comparison inside each pair of changed lines, so “Run the test suite on Node 22.” against “…Node 24.” marks two characters and leaves the rest plain. Character-level would go further and mark the “2” and the “4” alone — which is right for a typo like “colour” against “color”, and wrong for anything larger, because a rewritten sentence turns into dozens of one-character fragments scattered across the line. Words are the unit people read, so words are the unit used here, with punctuation and the gaps between them treated as their own tokens: “config.json” splits into “config”, “.” and “json”, because the dot is often exactly where the change is.
The two files look identical on screen but the tool says they differ. Why?
Almost always something invisible. Trailing whitespace — a space or a tab sitting after the last visible character — is the usual culprit, and it shows up here as a highlighted blank at the end of the line; switch on “Ignore leading and trailing whitespace” to confirm that is all it was. Next most common is a character that looks like one you know and is not: a non-breaking space instead of a space, a curly quote instead of a straight one, an en dash instead of a hyphen, or a zero-width character carried in from a web page or a word processor. Those all survive copy and paste and none of them are visible. A tab against four spaces is the same story and reads identically at most indent widths. If you want the text cleaned rather than merely ignored, the Text Cleaner tool on this site strips them; line endings are the one cause this page cannot show you, for the reason in the answer below.
What does “@@ -1,5 +1,5 @@” in the exported patch actually mean?
It is the hunk header, and it says where this block of changes belongs. The part after the minus is the original file: start at line 1, and this hunk covers 5 of its lines. The part after the plus is the new file: start at line 1, covering 5 lines. Both counts include the unchanged context lines printed around the change, which is why they are usually larger than the number of “-” and “+” lines. That exact header is what the sample above exports. Two more rules matter when you read one: a count of 1 is written without the comma (“@@ -3 +3,2 @@”), and an empty range — a hunk that only adds lines to a file that had none — names the line before the insertion point, which is why inserting into an empty file produces “-0,0”.
Why does the page refuse very large inputs?
The limits are 5,000 lines and 400,000 characters per side, and beyond 1,200 single-line edits between them the tool switches to a coarser answer. Those are not arbitrary. Finding the smallest possible set of edits costs roughly the size of the input multiplied by the number of differences, so two ten-thousand-line files that differ everywhere is on the order of a hundred million operations — the tab stops responding, and you lose the text you pasted with no explanation of why. Refusing with a sentence costs you less than that does. When only the edit limit is passed, nothing is thrown away: the lines the two share at the start and the end are still matched, and the region between them is reported as one block that differs, with a note saying so.
How is the similarity percentage worked out?
Twice the number of identical lines, divided by the total number of lines on both sides. The sample above has 5 lines each side and 3 identical lines, so it scores 2 × 3 ÷ 10 = 60%. A line that changed by a single word counts as zero here, not as a near-match, which makes the figure deliberately stricter than what your eye reports — it answers “how much of this is untouched”, not “how similar do these feel”. Two empty inputs score 100%, because nothing about them differs.
Why is the patch export disabled when I ignore case or whitespace?
Because those options make lines count as equal when they are not. A patch is a set of instructions for turning the left text into the right text byte for byte, so if the tool decided “Total” and “total” were the same line, the patch would leave that line out — and applying it would produce a file that is not the right-hand text. It would look correct in the preview and fail on a real repository, possibly after half of it had already been applied. So the export is only offered for an exact comparison. The on-screen result is still perfectly useful with the options on; it just is not a patch.
How are trailing newlines handled, and why is there no CRLF option?
A newline at the end of a file is the end of the last line, not an extra blank line, so it is never shown as a row — a file that ends properly would otherwise appear to carry a phantom empty line at the bottom. It is still a real difference: when one side ends with a newline and the other does not, the page says so in a note above the result, and the exported patch records it the way diff does, with “\ No newline at end of file” written under the affected line. Windows line endings are a different matter, and the honest answer is that this page cannot see them. Every browser normalises carriage returns to line feeds as text enters a text box, so a file written with CRLF and the same file written with LF arrive here identical — pasting CRLF text above and getting no difference at all is that behaviour, not a bug in the comparison. That is also why there is no “ignore line endings” switch: there would be nothing left for it to ignore. Check line endings with “file”, with “git diff --stat”, or in your editor’s status bar.
Which algorithm is this, and why does another tool align the same change differently?
Myers’ diff algorithm — the O(ND) greedy variant described in his 1986 paper, written out in this page rather than pulled from a library, since a diff package would cost more of the page weight than the whole tool. It finds a minimum edit script: no shorter set of insertions and deletions turns one text into the other. What it does not guarantee is that the minimum is unique. Where several alignments have the same cost — most commonly around repeated or blank lines — one tool may show a block as moved down and another as moved up, and both are correct. If two diffs of the same pair disagree, check that the number of added and removed lines matches; that part will agree.
Does any of my text leave the browser?
No. The comparison, the word-level highlighting and the patch are all produced by JavaScript this page has already loaded, there is no upload of any kind, and nothing is written to localStorage — closing the tab leaves nothing behind. That matters more here than on most pages: the things people compare are contracts, config files, credentials and drafts that have not been published. The page does load Google’s ad script, as every page on this site does, but it is never given what you paste.