AI Distraction Blocker Chrome Extension (2026)
AI distraction blocker Chrome extensions represent a powerful solution for developers and power users seeking to reclaim their attention in an increasingly noisy digital environment. Unlike traditional blockers that use static blocklists, AI-powered versions analyze page content in real-time, identifying contextually distracting elements and dynamically filtering them based on your work context.
Building one from scratch teaches you Manifest V3 architecture, Chrome Extension APIs, local model inference, and event-driven content scripts. skills that transfer directly to any browser extension project.
Understanding the Core Architecture
The foundation of an effective AI distraction blocker lies in its ability to understand page content and user intent. These extensions typically operate through three interconnected layers: a content analysis engine, a rules engine, and a user interface for configuration.
The content analysis layer uses JavaScript to scan DOM elements, extract text content, and apply machine learning models or heuristic algorithms to determine distraction probability. The rules engine then applies user-defined policies, deciding what to block, hide, or modify. Finally, the configuration UI allows you to customize behavior without touching code.
In Manifest V3, these three layers map to specific extension components:
| Layer | Component | File |
|---|---|---|
| Content analysis | Content script | content.js |
| Network-level blocking | Background service worker | background.js |
| Rules engine | declarativeNetRequest rules |
rules.json |
| Configuration UI | Options page or popup | options.html + options.js |
| Persistent storage | Chrome Storage API | accessed from any context |
Understanding which layer handles which concern prevents the most common architectural mistakes, like trying to intercept network requests from a content script or manipulating the DOM from a service worker.
Building a Basic Implementation
Here’s a minimal Manifest V3 structure for a distraction blocker:
// manifest.json
{
"manifest_version": 3,
"name": "AI Distraction Blocker",
"version": "1.0",
"permissions": ["activeTab", "storage", "scripting"],
"host_permissions": ["<all_urls>"],
"background": {
"service_worker": "background.js"
},
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_end"
}]
}
The content script is where the actual blocking happens. A basic implementation scans for common distraction patterns:
// content.js
const distractionSelectors = [
'[class*="social"]', '[class*="feed"]', '[class*="notification"]',
'[id*="sidebar-promotion"]', '.recommendations', '[data-ad]'
];
function blockDistractions() {
distractionSelectors.forEach(selector => {
document.querySelectorAll(selector).forEach(el => {
el.style.display = 'none';
});
});
}
// Run after page loads
document.addEventListener('DOMContentLoaded', blockDistractions);
This covers static HTML, but modern single-page applications render content dynamically. You need a MutationObserver to catch elements added after the initial load:
// content.js. with MutationObserver for SPAs
function blockDistractions() {
distractionSelectors.forEach(selector => {
document.querySelectorAll(selector).forEach(el => {
el.style.display = 'none';
});
});
}
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if (mutation.addedNodes.length > 0) {
blockDistractions();
}
});
});
observer.observe(document.body, { childList: true, subtree: true });
blockDistractions();
Without the observer, Twitter’s recommendation sidebar reappears every time you scroll because the site continuously injects new DOM nodes.
Adding AI-Powered Analysis
Static selectors work for known platforms, but true AI-powered blocking requires content understanding. You can integrate with APIs or run lightweight models in the extension context:
// Analyze page content for distraction patterns
async function analyzePageContent() {
const pageText = document.body.innerText.substring(0, 5000);
// Simple heuristic scoring (replace with API call for production)
const distractionKeywords = [
'buy now', 'limited time', 'trending', 'viral',
'breaking news', 'you won\'t believe'
];
let score = 0;
distractionKeywords.forEach(keyword => {
if (pageText.toLowerCase().includes(keyword)) {
score += 10;
}
});
return score > 30; // Threshold for blocking
}
This heuristic approach is a solid starting point. For more nuanced analysis, use a weighted scoring model that accounts for keyword frequency and position in the DOM:
async function scorePage() {
const patterns = [
{ regex: /you won't believe/i, weight: 15, label: 'clickbait' },
{ regex: /\d+ things (you|that)/i, weight: 10, label: 'listicle-bait' },
{ regex: /trending now/i, weight: 8, label: 'fomo' },
{ regex: /buy now|limited offer|flash sale/i, weight: 12, label: 'commerce' },
{ regex: /subscribe to our newsletter/i, weight: 5, label: 'subscription-nag' },
{ regex: /breaking:/i, weight: 6, label: 'news-bait' }
];
// Weight content near the top of the page more heavily
const aboveFold = document.body.innerText.substring(0, 2000);
const belowFold = document.body.innerText.substring(2000, 8000);
let score = 0;
patterns.forEach(({ regex, weight }) => {
const topMatches = (aboveFold.match(regex) || []).length;
const bottomMatches = (belowFold.match(regex) || []).length;
score += (topMatches * weight * 1.5) + (bottomMatches * weight);
});
return { score, shouldBlock: score > 40 };
}
Context-Aware Blocking Strategies
Effective distraction blocking adapts to your current context. A developer working on code needs different protection than someone reading news. Implement context awareness through:
- Time-based rules: Stricter blocking during work hours
- Tab detection: Identify coding environments, documentation, or IDE-like pages
- Focus mode: Manual override for deep work sessions
- Domain whitelisting: Always allow essential tools
// context-aware-blocking.js
const focusDomains = ['github.com', 'stackoverflow.com', 'docs.rs'];
function shouldApplyStrictBlocking() {
const hour = new Date().getHours();
const isWorkHours = hour >= 9 && hour <= 17;
const currentDomain = window.location.hostname;
const isProductivitySite = focusDomains.some(d => currentDomain.includes(d));
return isWorkHours || isProductivitySite;
}
Extend this with a “focus session” concept. a time-boxed period of maximum blocking triggered manually:
// Check if the user is in an active focus session
async function isInFocusSession() {
return new Promise(resolve => {
chrome.storage.local.get(['focusSession'], ({ focusSession }) => {
if (!focusSession) return resolve(false);
const expired = Date.now() > focusSession.endsAt;
if (expired) {
chrome.storage.local.remove('focusSession');
return resolve(false);
}
resolve(true);
});
});
}
// Start a 90-minute focus session from the popup
function startFocusSession(minutes = 90) {
chrome.storage.local.set({
focusSession: {
startedAt: Date.now(),
endsAt: Date.now() + minutes * 60 * 1000
}
});
}
During a focus session, the extension applies its most aggressive blocking profile. When the session expires, it reverts to standard rules automatically.
Chrome Extension API Integration
For deeper integration, use Chrome’s Extension APIs to create sophisticated blocking rules:
// background.js - Using declarativeNetRequest for efficient blocking
chrome.runtime.onInstalled.addListener(() => {
chrome.declarativeNetRequest.updateDynamicRules({
addRules: [
{
id: 1,
priority: 1,
action: {
type: 'block'
},
condition: {
urlFilter: '.*tracking.*',
resourceTypes: ['script', 'image']
}
}
],
removeRuleIds: [1]
});
});
declarativeNetRequest is more powerful than DOM manipulation for blocking because it intercepts requests before they reach the network, preventing trackers from loading at all. For a distraction blocker, you can block entire recommendation endpoints:
// Block YouTube's recommendation API calls
const youtubeBlockRules = [
{
id: 10,
priority: 2,
action: { type: 'block' },
condition: {
urlFilter: 'youtube.com/api/stats/watchtime',
resourceTypes: ['xmlhttprequest']
}
},
{
id: 11,
priority: 2,
action: { type: 'block' },
condition: {
urlFilter: 'suggestqueries.google.com',
resourceTypes: ['xmlhttprequest']
}
}
];
// Load rules from a JSON file for easier maintenance
fetch(chrome.runtime.getURL('rules.json'))
.then(res => res.json())
.then(rules => {
chrome.declarativeNetRequest.updateDynamicRules({
addRules: rules,
removeRuleIds: rules.map(r => r.id)
});
});
Storing rules in a JSON file rather than hardcoding them in background.js makes it easy to add rules without touching JavaScript logic.
Performance Considerations
Running AI analysis on every page requires careful performance management. Key optimizations include:
- Debouncing: Wait for page to stabilize before analysis
- Web Workers: Offload heavy computation from main thread
- Caching: Store analysis results per domain
- Selective scanning: Only analyze visible content
// Performance-optimized scanning
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
const safeScan = debounce(() => {
// Your analysis logic here
}, 1000);
Caching per-domain analysis results avoids re-running expensive scoring on every navigation within the same site:
const domainCache = new Map();
async function getBlockingDecision(domain) {
if (domainCache.has(domain)) {
return domainCache.get(domain);
}
const result = await analyzePageContent();
// Cache for 10 minutes
domainCache.set(domain, { result, expiresAt: Date.now() + 600_000 });
setTimeout(() => domainCache.delete(domain), 600_000);
return { result };
}
For truly intensive model inference. such as running a TensorFlow.js classification model. offload to a Web Worker to keep the page responsive:
// analysis-worker.js
self.onmessage = function(e) {
const { text } = e.data;
// Heavy computation happens here, off the main thread
const score = runModel(text);
self.postMessage({ score });
};
// content.js
const worker = new Worker(chrome.runtime.getURL('analysis-worker.js'));
worker.postMessage({ text: document.body.innerText.substring(0, 8000) });
worker.onmessage = ({ data }) => {
if (data.score > 40) applyBlockingRules();
};
Customization for Power Users
Beyond basic blocking, power users benefit from granular controls. Consider adding:
- Custom CSS injection: Replace distracting elements with productivity reminders
- Keyboard shortcuts: Toggle blocking without leaving your workflow
- Statistics tracking: Understand your distraction patterns over time
- Sync across devices: Import/export configurations
For custom CSS injection, the extension can overlay blocked areas with subtle reminders instead of simply hiding them:
function replaceWithReminder(element, message = 'Blocked for focus') {
const replacement = document.createElement('div');
replacement.style.cssText = `
background: #f0f0f0;
border: 1px dashed #ccc;
padding: 12px 16px;
color: #888;
font-size: 13px;
text-align: center;
border-radius: 4px;
margin: 4px 0;
`;
replacement.textContent = message;
element.replaceWith(replacement);
}
This is less jarring than blank spaces and serves as a gentle reminder of your focus goal rather than an invisible removal.
Advanced Features for Developer Workflows
Developers have unique needs when it comes to distraction blocking. IDE integrations, documentation browsing, and code research require careful handling to avoid accidentally blocking essential resources. Consider implementing developer-specific features:
// Developer-focused blocking rules
const developerExceptions = {
allow: [
'*.stackoverflow.com',
'*.github.com',
'*.readthedocs.io',
'*.mozilla.org',
'developer.mozilla.org',
'*.npmjs.com'
],
block: [
'*://*.youtube.com/feed',
'*://twitter.com/home',
'*://www.reddit.com/'
]
};
function checkDeveloperExceptions(url) {
return developerExceptions.allow.some(pattern =>
new RegExp(pattern.replace('*', '.*')).test(url)
);
}
Developers often research technical questions that lead through multiple tabs across GitHub, Stack Overflow, and package documentation. The extension should recognize these chains and apply a lighter touch when domain hopping follows a recognizable research pattern:
const RESEARCH_DOMAINS = new Set([
'github.com', 'stackoverflow.com', 'npmjs.com',
'pypi.org', 'crates.io', 'pkg.go.dev', 'rubygems.org',
'readthedocs.io', 'developer.mozilla.org', 'docs.python.org'
]);
function isResearchContext() {
return RESEARCH_DOMAINS.has(window.location.hostname.replace('www.', ''));
}
You can also add a “quick lookup” mode that temporarily relaxes blocking when the user opens a new tab directly from a developer tool (detected via the referrer header or tab opener API).
Privacy and Data Handling
When building AI-powered blocking features, consider privacy implications. Local processing keeps all data on the user’s machine, while API-based analysis sends content externally. For maximum privacy, run lightweight models client-side using WebAssembly or TensorFlow.js:
// Local privacy-focused analysis example
class PrivacyAwareAnalyzer {
constructor() {
this.localPatterns = this.loadLocalPatterns();
}
loadLocalPatterns() {
// Patterns defined locally - no external calls
return [
{ pattern: /clickbait|you won't believe/i, weight: 5 },
{ pattern: /buy now|limited offer/i, weight: 3 },
{ pattern: /subscribe|newsletter/i, weight: 2 }
];
}
analyze(text) {
let totalScore = 0;
this.localPatterns.forEach(({ pattern, weight }) => {
if (pattern.test(text)) totalScore += weight;
});
return totalScore;
}
}
If you need more sophisticated classification than keyword matching, TensorFlow.js enables local inference without any network calls:
// Load a pre-trained text classification model locally
async function loadModel() {
const model = await tf.loadLayersModel(
chrome.runtime.getURL('models/distraction-classifier/model.json')
);
return model;
}
async function classifyWithModel(text, model) {
// Tokenize and encode text
const input = encodeText(text);
const tensor = tf.tensor2d([input]);
const prediction = model.predict(tensor);
const score = prediction.dataSync()[0];
tensor.dispose();
return score;
}
Bundle pre-trained models into the extension package. A quantized MobileNet-derived text classifier can run in under 5MB, acceptable for an extension package size.
Testing Your Extension
Comprehensive testing ensures your blocker works across different scenarios. Use Chrome’s built-in testing capabilities:
// Example test case structure
const testCases = [
{
url: 'https://twitter.com/home',
expected: 'blocked',
description: 'Social media feed should be blocked'
},
{
url: 'https://github.com/features',
expected: 'allowed',
description: 'Developer resources should be accessible'
}
];
function runTests() {
testCases.forEach(({ url, expected, description }) => {
const result = shouldBlock(url);
console.log(`${description}: ${result === expected ? 'PASS' : 'FAIL'}`);
});
}
For automated end-to-end testing, use Playwright with the --load-extension flag to test your extension against real pages:
// playwright.config.js
const { chromium } = require('playwright');
async function testExtension() {
const pathToExtension = require('path').join(__dirname, 'extension');
const browser = await chromium.launchPersistentContext('', {
headless: false,
args: [
`--disable-extensions-except=${pathToExtension}`,
`--load-extension=${pathToExtension}`
]
});
const page = await browser.newPage();
await page.goto('https://twitter.com/home');
// Verify the feed sidebar is hidden
const feedVisible = await page.isVisible('[aria-label="Timeline: Trending now"]');
console.log(`Feed blocked: ${!feedVisible ? 'PASS' : 'FAIL'}`);
await browser.close();
}
This lets you run regression tests against live pages whenever you modify blocking rules, catching unintended side effects before shipping.
Options Page and User Configuration
A well-built extension needs a configuration interface. The options page stores user preferences in chrome.storage.sync so settings persist across devices:
// options.js
document.addEventListener('DOMContentLoaded', () => {
chrome.storage.sync.get(['blockingProfile', 'customDomains', 'workHours'], (settings) => {
document.getElementById('profile').value = settings.blockingProfile || 'standard';
document.getElementById('customDomains').value = (settings.customDomains || []).join('\n');
document.getElementById('workStart').value = settings.workHours?.start || '09:00';
document.getElementById('workEnd').value = settings.workHours?.end || '17:00';
});
});
document.getElementById('save').addEventListener('click', () => {
const customDomains = document.getElementById('customDomains').value
.split('\n')
.map(d => d.trim())
.filter(Boolean);
chrome.storage.sync.set({
blockingProfile: document.getElementById('profile').value,
customDomains,
workHours: {
start: document.getElementById('workStart').value,
end: document.getElementById('workEnd').value
}
}, () => {
document.getElementById('status').textContent = 'Settings saved.';
});
});
Use chrome.storage.sync (not localStorage) so settings carry over when the user logs into Chrome on a different machine.
Conclusion
Building an AI distraction blocker Chrome extension combines web development skills with behavioral understanding. Start with simple selector-based blocking, then layer in AI analysis as you refine your understanding of what constitutes distraction in your workflow. The key is creating a system that disappears into the background while protecting your attention when you need it most.
The progression from static selectors to heuristic scoring to local model inference maps naturally to increasing levels of sophistication. You can ship a useful v1 in an afternoon with CSS hiding and declarativeNetRequest rules, then iterate toward context-aware intelligence over time. Focus on your own workflow first. block the specific sites and UI patterns that pull you away from flow state. and generalize from there.
Try it: Estimate your monthly spend with our Cost Calculator.
Related Reading
- Chrome Extension Network Request Blocker: A Developer’s Guide
- Chrome JavaScript Blocker Extension: A Developer’s Guide
- Advanced Claude Skills with Tool Use and Function Calling
Built by theluckystrike. More at zovo.one
Find the right skill → Browse 155+ skills in our Skill Finder.