The Problem with Strikethrough in Audiobook Production
In audiobook production workflows, text modification is a frequent requirement. Creators repeatedly adjust content to match voiceover pacing and optimize listener comprehension. However, our research revealed that many production teams do not directly delete content when modifying source text—instead, they use strikethrough to mark changes.
This approach stems from valid concerns:
- Preserving modification history for comparison and traceability
- Maintaining the ability to revert to original text if changes affect the storyline
Despite its apparent intuitiveness, strikethrough-based comparison presents significant limitations.
Limitations of Strikethrough Markers
Through research and practical application, we identified the following drawbacks:
-
Poor Readability
Excessive strikethrough creates dense, fragmented text. Even with reduced opacity, sentences become discontinuous, severely impacting review and editing efficiency. -
Incomplete Change Recording
Strikethrough marks deleted content but fails to capture additions and modifications, resulting in incomplete change information. -
High Restoration Cost
To reference the original text, users must mentally reconstruct it by combining strikethrough-marked content with modifications—a process counter to natural human cognition.
Traditional strikethrough methods cannot satisfy the need for precise comparison and intuitive visualization in audiobook production.
Core Requirements: Making Changes More Creator-Friendly
We aimed to address three fundamental problems:
- Precise Difference Detection: Automatically distinguish between
original deletionsandcurrent additions - Intuitive Visualization: Replace strikethrough with visual indicators (such as color highlighting) that reveal modification locations without disrupting text flow
- Efficient Original Reference: Support side-by-side viewing with synchronized scrolling to keep corresponding paragraphs aligned
Solution: Diff-Based Text Comparison
We implemented a comprehensive text comparison solution with the following core principles:
- Use diff algorithms to compare original and current text
- Automatically identify deletions, modifications, and additions
- Leverage highlighting visualization and split-screen comparison for clear change visibility
The technical pipeline follows: diff computation → data cleaning → Tiptap decoration rendering → synchronized scrolling. The following sections detail each implementation step.
Implementation: End-to-End Pipeline Design
1. Difference Computation: Using diff-match-patch
The core challenge is accurately identifying content that exists in the original but not in the current version (deletions) and vice versa (additions). We adopted the mature diff-match-patch library, which generates minimal diff sets between two texts.
const dmp = new diff_match_patch()
const diffResult = dmp.diff_main(originalText, modifiedText)
dmp.diff_cleanupSemantic(diffResult)
The diff_main method returns a difference array where:
-1indicates deleted content1indicates added content0indicates unchanged content
Further optimization was applied to these results.
2. Data Cleaning: Making Diff Results Context-Aware
Raw diff results from the algorithm may contain excessive fragmented changes (like single punctuation modifications), which degrades readability. We implemented a processDiffList method to group results, deduplicate, and enrich context.
Core Logic:
- Grouping by Type: Separate differences into deletion groups (state=-1) and addition groups (state=1), ignoring unchanged content (state=0)
- Context Enrichment: Add surrounding context (adjacent sentences or punctuation) to each diff segment, helping users understand modification context
- Deduplication: Filter duplicate segments that were both deleted and added (such as temporary formatting changes), focusing on substantive modifications
// Process diff list and extract meaningful information
const analyzeDiffSegments = (diffSegments) => {
if (!diffSegments?.length) return { removed: [], added: [] }
const processed = { removed: [], added: [] }
let activeGroup = []
let activeState = null
// Step 1: Group by state (deletion/addition)
for (let i = 0; i < diffSegments.length; i++) {
const [state, content] = diffSegments[i]
if (state === 0) {
// Handle unchanged content, process current group and reset
if (activeGroup.length > 0) {
finalizeGroup(activeGroup, processed, content)
activeGroup = []
activeState = null
}
continue
}
// State change detected, process previous group
if (activeState !== null && state !== activeState) {
finalizeGroup(activeGroup, processed, '')
activeGroup = []
}
activeGroup.push({ state, content })
activeState = state
}
// Step 2-5: Enrich with context information
// ...
return processed
}
The final output produces two collections:
removed: Content that existed in the original but was deletedadded: Content that is new in the current version
3. Visual Rendering: Using Tiptap Decorations
After diff computation, the differences need to be visualized in the editor. We utilized Tiptap's Decorations feature, which adds styling markers dynamically without modifying the text itself.
Implementation Approach:
- Apply red background highlighting for original deletions
- Apply green background highlighting for current additions
- Highlighting does not alter text structure, avoiding interference with word count statistics or downstream processing
Code Example:
// Define decorations in Tiptap extension
import { Decoration } from '@tiptap/pm/view'
export const DiffHighlighter = Extension.create({
name: 'diffHighlighter',
addProseMirrorPlugins() {
return [
new Plugin({
props: {
decorations: () => {
const decorations = []
// Red highlighting for deletions
originalChanges.value.forEach(change => {
const { from, to } = locateTextPosition(change.keyword)
decorations.push(
Decoration.inline(from, to, { class: 'diff-deleted' })
)
})
// Green highlighting for additions
modifiedChanges.value.forEach(change => {
const { from, to } = locateTextPosition(change.keyword)
decorations.push(
Decoration.inline(from, to, { class: 'diff-added' })
)
})
return Decoration.set(decorations)
}
}
})
]
}
})
4. Interaction Optimization: Split-Screen Synchronized Scrolling
To allow simultaneous viewing of original and current text, we implemented a split-screen layout with synchronized scrolling—when the left panel scrolls to a certain position, the right panel automatically aligns to the corresponding location.
Synchronized Scrolling Implementation:
- Listen to scroll events on both editors
- Calculate scroll ratio based on content height
- Map the ratio to the other panel's scroll position, ensuring paragraph alignment
// Synchronized scrolling core logic
const synchronizeScroll = (sourceEditor, scrollPosition) => {
const sourceContentHeight = sourceEditor.scrollHeight - sourceEditor.clientHeight
const targetEditor = sourceEditor === leftPanel ? rightPanel : leftPanel
const targetContentHeight = targetEditor.scrollHeight - targetEditor.clientHeight
const scrollRatio = scrollPosition / sourceContentHeight
targetEditor.scrollTop = scrollRatio * targetContentHeight
}
Feature Highlights: Why This Replaces Strikethrough
- Uninterrupted Reading: Background highlighting replaces strikethrough, maintaining text continuity—voice actors no longer need to skip over strikethrough content
- Complete Change Recording: Both deletions and additions are displayed, supporting full modification history tracing
- Efficient Comparison: Split-screen with synchronized scrolling eliminates user effort in switching between versions
- Non-Destructive to Text Structure: Highlighting via decorations does not affect word count statistics or downstream processing (export, voice synthesis)
Technical Considerations
When implementing similar solutions, consider the following:
- Performance with Large Texts: Diff computation on lengthy documents requires optimization—consider web workers for background processing
- Real-Time Sync Accuracy: Synchronized scrolling must handle variable content lengths and maintain alignment precision
- Decoration Performance: Excessive decorations may impact editor performance—implement lazy rendering when necesary
- Edge Cases: Handle special characters, whitespace variations, and encoding differences during diff computation
Future Improvements
Planned enhancements include:
- Version History Tracking: Support for multiple modification checkpoints
- Diff Merge Suggestions: Assist users in quickly applying revisions
- AI Change Analysis: Detect logical or semantic inconsistencies in modifications
This implementation demonstrates how technical solutions can address practical workflow inefficiencies by transforming intuitive but problematic patterns (strikethrough markers) into sophisticated, creator-friendly tools.