The Whole Bill Lands On One (m+1)×(n+1) Table
The trouble began with a perfectly ordinary paste: two texts of 2000 lines each went in, the page raised no error and showed no progress bar, but it clearly paused. Paused where? On the two-dimensional table that LCS (longest common subsequence) dynamic programming has to fill. Call the line counts of the two inputs m and n; the area is (m+1)×(n+1). 2000 lines against 2000 lines is 4004001 cells, and the measured heap growth was 31.3MB, roughly 8.2 bytes per cell.
I timed the same input in two places. In Node the median was 85.4ms; inside Chrome 154 lineDiff took 112.7ms while the whole doDiff reached 176.8ms, and the extra part is the price of assembling the result DOM. That is the only genuinely expensive line item in the text diff tool; reading whitespace, cutting lines and stitching strings are all small change.
| Input size | Node median | Heap used | Outcome |
|---|---|---|---|
| 2000 lines vs 2000 lines | 85.4ms | growth 31.3MB | result returned |
| 4000 lines vs 4000 lines | 402.5ms | grows with the square of the line count | result returned |
| 6000 lines vs 6000 lines | 844ms | 280.8MB | passes even with the heap capped at 512MB |
| 8000 lines vs 8000 lines | 1093.4ms | 773.4MB | passes on the default heap, exit=134 under 512MB |
| 10000 lines vs 10000 lines | browser 2478.9ms | jsHeapLimit 3586MB | still returns, no crash |
Look at the price of doubling the line count: 85.4ms to 402.5ms is 4.7 times. Area alone should only go up 4 times, and the remainder belongs to the allocator moving house: arrays keep growing, copying the heap keeps getting dearer, so the slope of the curve only steepens. It never flattens on its own.
Time Is Set By Area, Not By How Many Lines You Edited
Two paired runs make the point. A 3000 line text with only 5 lines edited walks the inner loop 9000000 times, measures 149.0ms, and ends with 3005 diff lines. A 300 line text with 200 lines edited loops only 90000 times, finishes in 0.8ms, yet reports 500 diff lines. The heavily edited pair was nearly two hundred times faster, because the algorithm knows only m×n; the diff line count is a by-product tallied after the work is done. So to judge whether a comparison will be slow, look at how many lines each side has and ignore how much of it you rewrote.
Change One Punctuation Mark And The Whole Line Turns Red
Take a 5 line sample and swap the full stop at the end of line 3 for an exclamation mark. The statistics the page hands you are 1 line added and 1 line removed; both columns drop 12 .diff-line nodes, and the background of the entire line in the left column is rgba(255, 80, 80, 0.12). The decisive check is inside the line: childElems=0, and that red bar holds no span and no mark. The granularity of this tool stops at the line. Which character moved is never announced; you have to spot it yourself by eye across two lines. Do not expect word level highlighting; measured, it does not even highlight one character.
The Statistics Are Two Numbers And The Third Branch Is Dead Code
The template for the line above the result area sits at L91 and its output format is fixed: N lines added, M lines removed. Across 18 test cases I gathered every type value that lineDiff returns and found only equal, removed and added; the block at L79-85 that handles modified can never be reached, so it is dead code. Any reading in which you could see how many lines were edited simply does not exist in the interface: edit one line and it can only be read as one line removed plus one line added, each counter stepping once.
Whitespace Is Never Trimmed, But The Browser Eats Line Endings First
Cutting lines uses split('\n') and no trimming at either end, so every whitespace character takes part in the comparison: "foo " against "foo" comes back as 1 line added and 1 line removed; a\nb against a\nb\n differ by one trailing line break yet report 1 line added and 0 lines removed; a line holding only spaces against a truly empty line is 1 removed and 1 added. Such differences are invisible to the eye, but they do consume table area and they do turn red.
Line endings are a separate matter. Assign "a\r\nb" to a textarea and reading it back already gives "a\nb" with hasCR=false: at the moment the page collects the value, at L45, CRLF and a lone CR have been normalized into LF. So for the same pair of texts the page reports 0 lines added and 0 lines removed, while feeding those two strings straight into lineDiff in Node gives 1 removed and 1 added. The reminder to normalize line endings before comparing is wasted on the browser path, because the difference is erased long before you see it. It exists only when you bypass the page and call the function yourself.
A 400px Result Area Holds 17 Lines On Screen
The render layer is plain arithmetic: the line box of .diff-line stands 22.390625px tall, the container declared max-height:400px yields clientHeight=400, and the division gives 17 lines. A 1000 line half edited input produces 3000 divs, scrollHeight=33586px, and the two columns stay exactly equal in height. One 1000 line comparison has to be scrolled through that 400px box round after round, which is how the bottleneck moves off the algorithm and onto your wheel.
Clearing Is Not Destroying And There Is No Copy Entry At All
There are two buttons only: compare and clear. Grepping the template for copy, clipboard, copy, download and execCommand returns zero hits, so to take the diff with you the only way is to select the text and copy it manually; there is no export and no download. What happens when clear is clicked is also worth knowing: it only sets the result area to display:none, leaving the statistics text and 3 line nodes in the DOM until the next comparison destroys them for real.
Fig. 1: Double the lines and time doubles by area. The 6000 line point was measured with the heap capped at 512MB; at the same 512MB, 8000 lines end in exit=134.
Three Questions People Ask
Q: Can files of several thousand lines be compared at all?
They can, but the two ceilings differ, which turns the loose claim in the article on online text comparison that a few thousand lines are fine into measured numbers. On the Node side 2000 lines take 85.4ms, 4000 lines take 402.5ms, and 8000 lines take 1093.4ms while eating 773.4MB. Cap the heap at 512MB and 6000 lines still need 844ms yet pass, while 8000 lines fail with exit=134; that heap out of memory condition is not something try/catch can trap, the process is just gone. On the browser side jsHeapLimit reads 3586MB and 10000 lines still come back at 2478.9ms. What binds you is never the algorithm but the heap handed to that one call.
Q: Why did one comma turn the entire line red?
Because results are emitted line by line: a full stop swapped for an exclamation mark is still 1 line added and 1 line removed, and nothing inside the line carries a character level marker. If you want to see only that one spot, you have to look for it inside the line yourself.
Q: The output is long, so is there a way to export the diff?
There is not. Measured, the only buttons are compare and clear, with neither copy nor download; and clear is only hiding, since the nodes stay in the DOM until the next comparison destroys them.