Visualise cumulative changes across multiple text revisions. Regions, words or characters that have been edited multiple times are highlighted with increasing intensity.
Unlike normal diff tools that compare two versions, deep-diffs can visualise where changes have been made in a document throughout multiple revisions over time — giving you a “heatmap” of editorial activity, particularly where details are being worked onor revised repeatedly.
- Legal document review — see which clauses have been contentious across redlining rounds
- Collaborative writing — identify paragraphs that have already been finessed
- Wikipedia-style editing — spot edit-war hotspots
- Code review — find code blocks that have been tweaked repeatedly
npm install @rossshannon/deep-diffsimport { deepDiffHtml, getDefaultStyles } from '@rossshannon/deep-diffs';
// A contract clause being refined over multiple revisions
const revisions = [
'The client shall pay the invoice.',
'The client shall pay the invoice within 30 days.',
'The client shall pay the invoice in full within 30 days.',
'The client shall pay the invoice in full within 30 business days.',
];
const html = deepDiffHtml(revisions);
const css = getDefaultStyles();Output:
The client shall pay the invoice<ins class="deep-diff"> <ins class="deep-diff">in full
</ins>within 30 <ins class="deep-diff">business </ins>days</ins>.Notice the nested <ins> tags — “within 30 days” was added first, then “in full” was inserted inside that region, then “business” was added. The nesting depth indicates how many times a region has been edited. The default CSS styles these with increasing background intensity, creating a visual heatmap of editorial activity. After multiple rounds of editing, a richer picture of the editorial process emerges.
Computes diff markers without rendering.
import { computeDeepDiff } from '@rossshannon/deep-diffs';
const { text, markers } = computeDeepDiff(revisions);
// text: the final revision text
// markers: array of { start, end, enabled } marker objectsOptions:
skipEmpty(boolean, defaulttrue) — skip empty revisions (useful for filtering vandalism)timeout(number, default1) — diff computation timeout in seconds
Renders text with markers as HTML.
import { computeDeepDiff, renderWithMarkers } from '@rossshannon/deep-diffs';
const { text, markers } = computeDeepDiff(revisions);
const html = renderWithMarkers(text, markers, {
tagName: 'mark',
className: 'changed'
});Options:
tagName(string, default'ins') — HTML tag for markersclassName(string, default'deep-diff') — CSS class for tags
Convenience function combining computeDeepDiff and renderWithMarkers.
import { deepDiffHtml } from '@rossshannon/deep-diffs';
const html = deepDiffHtml(revisions, { skipEmpty: true });Generates CSS for nested marker intensity.
import { getDefaultStyles } from '@rossshannon/deep-diffs';
const css = getDefaultStyles(5);
// Returns CSS with increasingly intense backgrounds for nested .deep-diff elements- Chain diffs — compute diffs between each consecutive revision pair
- Track markers — maintain a list of changed regions with
[start, end]positions - Transform markers — as new edits occur, existing markers shift, expand, or contract:
- Insert before marker → shift right
- Insert within marker → expand
- Delete before marker → shift left
- Delete within marker → contract (disable if fully subsumed)
- Accumulate — new insertions add new markers; nesting depth = change frequency
- Render — interleave tags at marker boundaries
This is essentially a simplified form of operational transformation — the same conceptual framework that powers real-time collaboration in Google Docs.
<script type="module">
import { deepDiffHtml, getDefaultStyles } from 'https://unpkg.com/@rossshannon/deep-diffs';
const style = document.createElement('style');
style.textContent = getDefaultStyles();
document.head.appendChild(style);
document.getElementById('output').innerHTML = deepDiffHtml(myRevisions);
</script>Full TypeScript support with bundled type definitions:
import {
deepDiffHtml,
computeDeepDiff,
type Marker,
type DeepDiffResult
} from '@rossshannon/deep-diffs';
const result: DeepDiffResult = computeDeepDiff(['v1', 'v2', 'v3']);
console.log(result.text); // string
console.log(result.markers); // Marker[]
const html: string = deepDiffHtml(['v1', 'v2'], {
tagName: 'mark',
className: 'highlight'
});- Character-indexed markers — This techique is tuned for tracking how details change in text over time. Large structural refactors (e.g., replacing or moving paragraphs) will lose some of the necessary context.
- No move detection — if text is cut and pasted elsewhere, it's treated as delete + insert, not a move.
- IBM History Flow — Wikipedia revision visualisation (author-coloured, not intensity-based)
- diff-match-patch — the underlying diff engine from Google Docs
- GitLens heatmaps — file-level age visualisation (not cumulative change count)
- Deep Diffs: Visually Exploring the History of a Document — the original paper on this technique
Original algorithm by Ross Shannon (2010). Modernised and published 2026.
MIT

