Text Diff Checker – Compare Two Texts Online Free

Text Diff Checker

Compare two or three texts side-by-side and instantly spot additions, deletions and changes — character, word or line level. Pull text from URLs, save history, 3-way merge. 100% private.

Original Text
0 chars · 0 words · 0 lines
Modified Text
0 chars · 0 words · 0 lines
🔒 100% private — all comparison happens in your browser. URL fetch uses a public CORS proxy and is the only optional network call.

Recent comparisons

Fetch text from URL

Paste a public URL — we'll pull its raw text via a CORS proxy.

How to Use the Text Diff Checker

  1. Pick a mode — 2-Way Compare or 3-Way Merge (for combining two divergent edits over a common base).
  2. Add your texts — paste, type, drop a file, or fetch from a URL.
  3. Choose granularity — Character (finest), Word (best for prose), or Line (best for code).
  4. Fine-tune options — ignore case, whitespace, blank lines; toggle line numbers, word wrap, sync scroll.
  5. Click "Compare Now" (or enable Auto-compare) to render the diff in real time.
  6. Navigate — jump between changes, search inside the diff, switch views, copy or export as HTML / patch / JSON / PDF.
  7. Save & share — every comparison is auto-saved to your local history; click Share to copy a self-contained link.

What is a Text Diff Checker?

A text diff checker takes two (or three) pieces of text and highlights exactly what changed — what was added, what was removed, what stayed the same. Instead of squinting at two versions side-by-side, you get a clean colour-coded report.

Our checker uses the proven Myers diff algorithm under the hood and runs entirely in your browser. That means even huge documents, source code or sensitive contracts never leave your device — and the comparison is virtually instant.

Common Use Cases

✍️ Writers & EditorsTrack revisions between drafts, see what your editor changed, review co-author edits.
👨‍💻 DevelopersCompare two versions of a config, JSON, SQL or source snippet without opening Git.
📚 Students & ResearchersCheck rewrites, spot plagiarism, compare quotation sources line-by-line.
⚖️ Legal & ComplianceSpot every clause changed between two contract drafts or policy revisions instantly.
📈 SEO & MarketingCompare meta descriptions, headlines or ad copy variants to refine A/B tests.
🛠️ DevOps & SysAdminsDiff log files, server configs, environment variables and YAML manifests offline.
🌐 TranslatorsVerify what changed in a source text before re-translating only the deltas.
📝 QA & DocumentationCompare expected vs actual output, audit changes in manuals and release notes.

Why Choose Our Text Diff Checker

100% Private — local comparison; URL fetch is the only opt-in network call.
Instant Results — handles tens of thousands of lines smoothly.
3-Way Merge — Git-style merge of two branches over a common base with conflict markers.
🌐 URL Fetch — pull text from any public URL into either pane.
🕘 Diff History — last 10 comparisons saved locally and restorable in one click.
🌙 Dark Mode — full dark theme, persisted.
🔍 Search-in-Diff — find & jump between matches with prev/next controls.
⬆⬇ Jump Between Changes — skip straight from edit to edit.
🎯 3 Granularities — character, word, or line.
👁️ 3 View Modes — side-by-side, unified or inline.
🧠 Smart Options — ignore case / whitespace / blank lines.
📊 Live Stats — additions, deletions and similarity percentage.
📂 File Uploads — drop .txt, .md, .json, .html, .css, .js, .xml, .yaml, .ini and more.
🔄 Swap & Auto-Compare — flip texts; auto-diff as you type.
📥 5 Export Formats — HTML, unified .diff patch, plain text, JSON, PDF (print).
🔗 Shareable Links — encode the comparison into a single URL.
Fullscreen Mode — distraction-free reviewing.
📱 Fully Responsive — desktop, tablet and mobile.
⌨️ Keyboard Shortcuts — Ctrl/⌘+Enter compare, Ctrl/⌘+F search, Esc reset.
💯 Always Free — no sign-up, no watermark, no limits.

Frequently Asked Questions

Is my text uploaded anywhere?

No. Every comparison runs inside your browser. The only optional network call is "Fetch from URL", which routes through a public CORS proxy purely to retrieve that URL's content — nothing of yours is uploaded.

What is 3-way merge?

3-way merge is the technique Git uses. You provide a Base (common ancestor), Yours and Theirs. The tool merges both edits automatically where they don't overlap, and inserts standard <<<<<<</=======/>>>>>>> conflict markers where they do.

What's the difference between character, word and line diff?

Character compares every character — perfect for short strings. Word compares whole words and is best for prose. Line compares whole lines — ideal for source code, configs and logs.

Where is my history stored?

In your browser's localStorage only — the last 10 comparisons are kept on this device. No account, no cloud sync.

How big can my texts be?

Anything up to several megabytes works smoothly. Above ~10MB browsers slow down for any diff tool, so we cap file uploads at 10MB.

What does "Ignore whitespace" do?

It collapses runs of spaces and tabs so reformatted text or differently indented code isn't flagged as changed.

Can I share a comparison with a colleague?

Yes — click Share to copy a URL that encodes both texts and your view settings. Or Export as unified patch (.diff) to send a Git-style patch file.

Does it work on mobile?

Yes — the input panes stack vertically and all toolbars wrap responsively.

Which tools pair well with this?

Try our Word Counter, Case Converter, JSON Formatter, Markdown to HTML and PDF to Text — they pair beautifully with diff workflows.

'; downloadFile('text-diff-report.html', doc, 'text/html'); }// MERGE COPY/DOWNLOAD $('pct-td-copy-merge').addEventListener('click', function() { navigator.clipboard.writeText(els.mergeText.value || '').then(function() { toast('Merged text copied', 'ok'); }); }); $('pct-td-download-merge').addEventListener('click', function() { downloadFile('merged.txt', els.mergeText.value || '', 'text/plain'); });// SHARE els.shareBtn.addEventListener('click', function() { var payload; if (state.mode === '3way') payload = { m: '3', base: els.textBase.value, y: els.textYours.value, t: els.textTheirs.value }; else payload = { m: '2', a: els.textA.value, b: els.textB.value, v: state.view, g: state.gran }; try { var enc = btoa(unescape(encodeURIComponent(JSON.stringify(payload)))); if (enc.length > 6000) { toast('Texts too long to share via URL. Use Export instead.', 'err'); return; } var link = location.origin + location.pathname + '#td=' + enc; navigator.clipboard.writeText(link).then(function() { toast('Share link copied to clipboard', 'ok'); }).catch(function() { prompt('Copy this link:', link); }); } catch (e) { toast('Share failed', 'err'); } }); // Load from hash (function loadFromHash() { if (location.hash.indexOf('#td=') === 0) { try { var d = JSON.parse(decodeURIComponent(escape(atob(location.hash.substring(4))))); if (d.m === '3') { document.querySelector('[data-mode="3way"]').click(); els.textBase.value = d.base || ''; els.textYours.value = d.y || ''; els.textTheirs.value = d.t || ''; } else { els.textA.value = d.a || ''; els.textB.value = d.b || ''; updateMeta(els.textA, els.metaA); updateMeta(els.textB, els.metaB); if (d.v) { var vb = document.querySelector('[data-view="'+d.v+'"]'); if (vb) vb.click(); } if (d.g) { var gb = document.querySelector('[data-gran="'+d.g+'"]'); if (gb) gb.click(); } } setTimeout(function() { compare(true); }, 200); } catch (e) {} } })();// HISTORY function getHistory() { try { return JSON.parse(localStorage.getItem('pct-td-hist') || '[]'); } catch (e) { return []; } } function setHistory(arr) { try { localStorage.setItem('pct-td-hist', JSON.stringify(arr.slice(0, 10))); } catch (e) {} } function saveHistory() { var h = getHistory(); var item; if (state.mode === '3way') item = { mode: '3way', base: els.textBase.value, yours: els.textYours.value, theirs: els.textTheirs.value, when: Date.now() }; else item = { mode: '2way', a: els.textA.value, b: els.textB.value, when: Date.now(), sim: state.lastStats ? state.lastStats.sim : null, gran: state.gran }; h.unshift(item); setHistory(h); renderHistory(); } function renderHistory() { var h = getHistory(); if (!h.length) { els.historyList.innerHTML = '
No comparisons yet.
Run a compare and it lands here.
'; return; } var html = ''; for (var i = 0; i < h.length; i++) { var it = h[i]; var preview = (it.mode === '3way' ? (it.base || '') : (it.a || '')).slice(0, 80).replace(/[\r\n]+/g, ' '); var time = new Date(it.when).toLocaleString(); html += '
' + time + ' · ' + it.mode + '' + (it.sim != null ? '' + it.sim + '% sim' : '') + '
' + esc(preview) + '…
'; } els.historyList.innerHTML = html; } els.historyBtn.addEventListener('click', function() { renderHistory(); els.drawer.classList.add('pct-td-open'); els.drawerBd.classList.add('pct-td-open'); }); $('pct-td-drawer-close').addEventListener('click', function() { els.drawer.classList.remove('pct-td-open'); els.drawerBd.classList.remove('pct-td-open'); }); els.drawerBd.addEventListener('click', function() { els.drawer.classList.remove('pct-td-open'); els.drawerBd.classList.remove('pct-td-open'); }); $('pct-td-history-clear').addEventListener('click', function() { if (confirm('Clear all saved comparisons?')) { setHistory([]); renderHistory(); toast('History cleared', 'ok'); } }); els.historyList.addEventListener('click', function(e) { var del = e.target.closest('[data-del]'); if (del) { var i = parseInt(del.getAttribute('data-del'), 10); var h = getHistory(); h.splice(i, 1); setHistory(h); renderHistory(); e.stopPropagation(); return; } var item = e.target.closest('.pct-td-hist-item'); if (!item) return; var idx = parseInt(item.getAttribute('data-idx'), 10); var h = getHistory(); var it = h[idx]; if (!it) return; if (it.mode === '3way') { document.querySelector('[data-mode="3way"]').click(); els.textBase.value = it.base; els.textYours.value = it.yours; els.textTheirs.value = it.theirs; } else { document.querySelector('[data-mode="2way"]').click(); els.textA.value = it.a; els.textB.value = it.b; updateMeta(els.textA, els.metaA); updateMeta(els.textB, els.metaB); if (it.gran) { var gb = document.querySelector('[data-gran="'+it.gran+'"]'); if (gb) gb.click(); } } els.drawer.classList.remove('pct-td-open'); els.drawerBd.classList.remove('pct-td-open'); setTimeout(function() { compare(true); }, 50); });// JUMP BETWEEN CHANGES function getChangedRows() { return els.diffEl.querySelectorAll('[data-changed="1"]'); } var jumpCur = -1; $('pct-td-jump-next').addEventListener('click', function() { var rows = getChangedRows(); if (!rows.length) return; jumpCur = (jumpCur + 1) % rows.length; rows[jumpCur].scrollIntoView({ behavior: 'smooth', block: 'center' }); rows[jumpCur].style.outline = '2px solid #2563eb'; setTimeout(function() { rows[jumpCur].style.outline = ''; }, 1200); }); $('pct-td-jump-prev').addEventListener('click', function() { var rows = getChangedRows(); if (!rows.length) return; jumpCur = (jumpCur - 1 + rows.length) % rows.length; rows[jumpCur].scrollIntoView({ behavior: 'smooth', block: 'center' }); rows[jumpCur].style.outline = '2px solid #2563eb'; setTimeout(function() { rows[jumpCur].style.outline = ''; }, 1200); });// SEARCH IN DIFF $('pct-td-search-toggle').addEventListener('click', function() { els.searchbar.style.display = els.searchbar.style.display === 'none' ? 'flex' : 'none'; if (els.searchbar.style.display === 'flex') els.searchInput.focus(); }); $('pct-td-search-close').addEventListener('click', function() { els.searchbar.style.display = 'none'; clearSearchHl(); }); els.searchInput.addEventListener('input', function() { runSearch(); }); $('pct-td-search-next').addEventListener('click', function() { if (state.searchMatches.length) { state.searchIdx = (state.searchIdx + 1) % state.searchMatches.length; focusMatch(); } }); $('pct-td-search-prev').addEventListener('click', function() { if (state.searchMatches.length) { state.searchIdx = (state.searchIdx - 1 + state.searchMatches.length) % state.searchMatches.length; focusMatch(); } }); function clearSearchHl() { var hl = els.diffEl.querySelectorAll('.pct-td-hl'); for (var i = 0; i < hl.length; i++) { var sp = hl[i]; var t = document.createTextNode(sp.textContent); sp.parentNode.replaceChild(t, sp); } state.searchMatches = []; state.searchIdx = 0; els.searchCount.textContent = '0 matches'; } function runSearch() { clearSearchHl(); var q = els.searchInput.value; if (!q) return; var qLower = q.toLowerCase(); var walker = document.createTreeWalker(els.diffEl, NodeFilter.SHOW_TEXT, null, false); var nodes = []; var n; while ((n = walker.nextNode())) { if (n.nodeValue.toLowerCase().indexOf(qLower) !== -1) nodes.push(n); } var matches = []; for (var i = 0; i < nodes.length; i++) { var tn = nodes[i]; var text = tn.nodeValue; var lower = text.toLowerCase(); var start = 0; var pos; var frag = document.createDocumentFragment(); var lastEnd = 0; while ((pos = lower.indexOf(qLower, start)) !== -1) { if (pos > lastEnd) frag.appendChild(document.createTextNode(text.slice(lastEnd, pos))); var sp = document.createElement('span'); sp.className = 'pct-td-hl'; sp.textContent = text.substr(pos, q.length); frag.appendChild(sp); matches.push(sp); lastEnd = pos + q.length; start = lastEnd; } if (lastEnd > 0) { if (lastEnd < text.length) frag.appendChild(document.createTextNode(text.slice(lastEnd))); tn.parentNode.replaceChild(frag, tn); } } state.searchMatches = matches; state.searchIdx = 0; els.searchCount.textContent = matches.length + ' match' + (matches.length === 1 ? '' : 'es'); if (matches.length) focusMatch(); } function focusMatch() { var sp = state.searchMatches[state.searchIdx]; if (!sp) return; sp.scrollIntoView({ behavior: 'smooth', block: 'center' }); els.searchCount.textContent = (state.searchIdx + 1) + '/' + state.searchMatches.length; }// KEYBOARD SHORTCUTS document.addEventListener('keydown', function(e) { if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault(); compare(false); } if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'f' && els.resultEl.style.display !== 'none') { e.preventDefault(); $('pct-td-search-toggle').click(); } if (e.key === 'Escape' && root.classList.contains('pct-td-full')) { root.classList.remove('pct-td-full'); els.fullBtn.textContent = '⛶'; } });// 3-way refs (used in saveHistory for textBase/yours/theirs which exist in DOM) if (!els.textBase) els.textBase = $('pct-td-text-base'); if (!els.textYours) els.textYours = $('pct-td-text-yours'); if (!els.textTheirs) els.textTheirs = $('pct-td-text-theirs');// INIT updateMeta(els.textA, els.metaA); updateMeta(els.textB, els.metaB); renderHistory(); })();
Scroll to Top