Chrome Extension Dual Pane Reader: Building Split-Screen Reading Tools
A Chrome extension dual pane reader transforms your browser into a powerful comparison and reading workstation. Whether you’re researching topics, translating documents, or reviewing code changes, the ability to view two web pages side by side within a single browser window dramatically improves productivity. This guide walks you through building a functional dual pane reader extension from scratch.
Why Build a Dual Pane Reader
The traditional approach of opening multiple browser windows and manually arranging them creates friction. A dedicated dual pane reader Chrome extension provides several advantages: synchronized scrolling between panes, unified controls, persistent layouts, and the ability to load content without leaving your current tab. Developers use these extensions to compare API documentation with implementation guides. Researchers compare source articles. Translators work with original and translated text simultaneously.
The core challenge lies in managing two independent document views while maintaining intuitive user controls. You need to handle URL loading, content extraction, scrolling synchronization, and responsive layout management—all while staying within Chrome’s extension architecture constraints.
Extension Architecture Overview
A dual pane reader extension consists of three main components working together. The popup interface provides the user controls for loading URLs into each pane. The content script runs within each pane to extract readable content and manage scroll synchronization. The background service worker coordinates communication between the popup and content scripts.
For the extension to function properly, you need the activeTab permission to access page content, scripting to inject content extraction code, and storage to remember user preferences. Here’s a minimal manifest configuration:
{
"manifest_version": 3,
"name": "Dual Pane Reader",
"version": "1.0.0",
"permissions": [
"activeTab",
"scripting",
"storage"
],
"action": {
"default_popup": "popup.html"
},
"background": {
"service_worker": "background.js"
}
}
Implementing the Popup Interface
The popup serves as the control center for your dual pane reader. Users need input fields for two URLs, a button to activate the split view, and controls for synchronization options. This HTML structure provides the foundation:
<!DOCTYPE html>
<html>
<head>
<style>
body { width: 400px; padding: 16px; font-family: system-ui; }
.pane-input { margin-bottom: 12px; }
label { display: block; font-size: 12px; margin-bottom: 4px; color: #666; }
input[type="url"] { width: 100%; padding: 8px; box-sizing: border-box; }
button { width: 100%; padding: 10px; background: #4285f4; color: white; border: none; cursor: pointer; }
.sync-option { margin-top: 12px; display: flex; align-items: center; gap: 8px; }
</style>
</head>
<body>
<h3>Dual Pane Reader</h3>
<div class="pane-input">
<label>Left Pane URL</label>
<input type="url" id="leftUrl" placeholder="https://example.com">
</div>
<div class="pane-input">
<label>Right Pane URL</label>
<input type="url" id="rightUrl" placeholder="https://example.org">
</div>
<button id="activateBtn">Open Dual Pane View</button>
<div class="sync-option">
<input type="checkbox" id="syncScroll" checked>
<label>Synchronize scrolling</label>
</div>
<script src="popup.js"></script>
</body>
</html>
When users click the activate button, the popup sends a message to the currently active tab instructing it to transform into split-view mode. The content script receives this message and重构s the page.
Content Script: The Core Reader Logic
The content script handles the heavy lifting of actually creating and managing the dual pane display. It must create two scrollable containers, inject content from the specified URLs, and handle scroll synchronization.
// content.js - runs in the active tab
let leftPane, rightPane;
let syncEnabled = true;
let isScrolling = false;
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'activateDualPane') {
createDualPaneLayout(message.leftUrl, message.rightUrl);
} else if (message.action === 'toggleSync') {
syncEnabled = message.enabled;
}
});
function createDualPaneLayout(leftUrl, rightUrl) {
// Clear existing content
document.body.innerHTML = '';
// Create container
const container = document.createElement('div');
container.style.display = 'flex';
container.style.height = '100vh';
container.style.overflow = 'hidden';
// Create left pane
leftPane = document.createElement('iframe');
leftPane.src = leftUrl;
leftPane.style.flex = '1';
leftPane.style.border = 'none';
// Create right pane
rightPane = document.createElement('iframe');
rightPane.src = rightUrl;
rightPane.style.flex = '1';
rightPane.style.border = 'none';
rightPane.style.borderLeft = '1px solid #ccc';
container.appendChild(leftPane);
container.appendChild(rightPane);
document.body.appendChild(container);
// Setup scroll synchronization
setupScrollSync();
}
function setupScrollSync() {
const handleScroll = (source, target) => {
if (isScrolling || !syncEnabled) return;
isScrolling = true;
const scrollRatio = source.scrollTop / (source.scrollHeight - source.clientHeight);
target.contentWindow?.postMessage({
type: 'syncScroll',
scrollRatio: scrollRatio
}, '*');
setTimeout(() => isScrolling = false, 50);
};
leftPane.addEventListener('load', () => {
const leftDoc = leftPane.contentDocument || leftPane.contentWindow.document;
leftDoc.addEventListener('scroll', () => handleScroll(leftDoc, rightPane));
});
window.addEventListener('message', (event) => {
if (event.data.type === 'syncScroll' && syncEnabled) {
isScrolling = true;
const targetDoc = rightPane.contentDocument || rightPane.contentWindow.document;
const scrollTop = event.data.scrollRatio *
(targetDoc.body.scrollHeight - targetDoc.body.clientHeight);
targetDoc.body.scrollTop = scrollTop;
setTimeout(() => isScrolling = false, 50);
}
});
}
This implementation uses iframes to load both URLs within a single page. The scroll synchronization works by calculating a scroll ratio—the current scroll position divided by the maximum scrollable distance—and applying that ratio to the other pane.
Handling Content Extraction
Loading raw web pages in iframes often results in cluttered displays with navigation, ads, and sidebars. A more sophisticated dual pane reader extracts only the main content using DOM manipulation. Here’s how to enhance the content script:
function extractMainContent(doc) {
// Remove unwanted elements
const removeSelectors = [
'nav', 'header', 'footer', 'aside',
'.sidebar', '.advertisement', '.ad',
'[role="banner"]', '[role="navigation"]'
];
removeSelectors.forEach(selector => {
doc.querySelectorAll(selector).forEach(el => el.remove());
});
// Find main content area
const article = doc.querySelector('article') ||
doc.querySelector('main') ||
doc.body;
return article.innerHTML;
}
You can apply this extraction before injecting content into the panes, creating a cleaner reading experience focused on actual article content rather than page chrome.
Advanced Features for Power Users
Beyond basic split-screen viewing, consider implementing these features for a more capable dual pane reader:
Content highlighting: Allow users to highlight text in one pane and automatically highlight corresponding content in the other. This requires semantic analysis of the content or position-based matching.
Side-by-side translation: Load a translation API to display original text alongside translations. You can implement this by fetching page content, sending it to a translation service, and rendering both versions.
Diff mode: For comparing code or structured content, implement a diff view that highlights additions, deletions, and modifications between the two panes using a library like diff-match-patch.
Bookmarking: Store pane configurations in Chrome’s storage API so users can quickly return to frequently compared resources.
// Save pane configuration
async function saveConfiguration(leftUrl, rightUrl) {
const config = {
leftUrl,
rightUrl,
timestamp: Date.now()
};
await chrome.storage.local.set({
savedConfigs: await getSavedConfigs().then(configs => [...configs, config])
});
}
Performance Considerations
Loading two full web pages simultaneously places significant memory demands on the browser. Optimize performance by implementing lazy loading—only load iframe content when the user specifically requests each pane. Use Chrome’s chrome.tabs API to retrieve tab information before creating iframes, and consider implementing a virtual scrolling approach for very long documents.
The dual pane reader pattern demonstrates how Chrome extensions can fundamentally transform browser behavior to serve specific workflows. By understanding the messaging system between extension components and the DOM manipulation techniques available in content scripts, you can build powerful productivity tools that rival standalone applications.
Related Reading
- Claude Code for Beginners: Complete Getting Started Guide
- Best Claude Skills for Developers in 2026
- Claude Skills Guides Hub
Built by theluckystrike — More at zovo.one