AI YouTube Spoiler Blocker: Building a Personalized Content Shield

Inspiration

Picture this: your favorite soccer team just played an incredible match, but you missed it live. You're planning to watch the replay later tonight. In the meantime, you want to watch some YouTube videos—maybe a coding tutorial, maybe some music, anything really. But you can't. The moment you open YouTube, you know what's waiting: thumbnails screaming the final score, titles revealing who scored the winning goal, the algorithm doing exactly what it's designed to do. You're trapped between spoilers and completely avoiding a platform you use daily.

This was my reality, and it was incredibly frustrating. I didn't want to disconnect from YouTube entirely—I just needed a way to hide specific content temporarily. Traditional solutions like blocking channels or filtering keywords were too blunt. A video titled "Best Goals of the Week" might be perfectly fine most days, but it's a spoiler minefield right after a match I haven't seen yet. I needed something that understood context and nuance, not just pattern matching.

When I discovered Chrome's experimental support for Gemini Nano, the lightbulb went off. I could build an extension that uses on-device AI to intelligently detect spoilers based on topics I define. It would act as my personal content shield, analyzing video titles and blurring anything related to what I'm trying to avoid. Finally, I could open YouTube without fear and actually watch the videos I came for.

What I Learned

Building this extension taught me several valuable lessons about working with emerging web technologies and AI:

Working with On-Device AI: This was my first experience with the Chrome LanguageModel API and Gemini Nano. On-device AI is fast and private since no data leaves the browser, and it doesn't require API keys or backend infrastructure. However, it also has limitations compared to cloud-based solutions, and I had to work within those constraints.

The Importance of Prompt Engineering: Getting the AI to consistently return simple "Yes" or "No" answers required careful prompt design. I learned that being explicit about the expected output format and keeping the prompt focused was crucial for reliable results. My final prompt template directly asks if a video title relates to blocked topics and requests a single-word response.

YouTube's Dynamic DOM: YouTube's front-end is heavily dynamic, with content constantly being added and removed via JavaScript. I implemented a MutationObserver to watch for new video elements appearing on the page, which taught me about efficiently handling DOM changes without creating performance issues. I had to identify different video container selectors to ensure coverage across YouTube's interface.

Asynchronous Queue Management: Since AI inference takes time, I needed to build a queuing system that processes videos one at a time without blocking the user interface. The processOneItemFromQueue function recursively calls itself with appropriate delays to maintain steady processing without overwhelming the browser.

Caching Strategy: I implemented a decision cache using a Map to store AI decisions for video titles I'd already processed. This dramatically improved performance and reduced redundant AI calls when the same video appeared multiple times on a page.

How I Built It

The extension is structured into three main components:

1. The Blurring Engine (startBlurringEngine): This component uses CSS injection and DOM observation to identify video containers on YouTube and immediately apply a blur effect. It monitors for different types of YouTube video renderers to ensure comprehensive coverage. The moment a video element appears, it gets added to a processing queue and visually blurred as a precaution.

2. The AI Engine (initializeAi and processOneItemFromQueue): This is the heart of the extension. It initializes a Gemini Nano session and processes videos from the queue one at a time. For each video, it extracts the title, constructs a prompt asking whether the title relates to any blocked topics, and uses the AI's response to decide whether to keep the video blurred or reveal it. The AI session is created with self.LanguageModel.create() and responds to prompts asynchronously.

3. The Settings Interface (popup.html and popup.js): A simple but effective UI that lets users add and remove spoiler topics. The topics are stored using Chrome's storage API and sync in real-time with the content script through a storage change listener. When topics change, the extension clears its decision cache and re-scans all videos on the page.

The data flow works like this: User adds topic → Storage updated → Content script notified → Cache cleared → All videos re-queued → AI re-evaluates each video → Blur removed if safe.

Challenges I Faced

Challenge 1: YouTube's Landing Page Problem
The biggest technical hurdle was dealing with YouTube's homepage and sidebar. These areas often don't include proper video titles in the DOM until you hover over them or they come into view. My extension would try to process these elements but couldn't find titles, leading to failures.

My solution was to implement a failure counter that tracks consecutive "no title" failures. After hitting a threshold, the extension displays a user-friendly warning explaining that homepage content will remain blurred and suggesting users search for specific videos they want to watch. This turned a limitation into a feature—the aggressive blurring actually provides more protection on the landing page.

Challenge 2: Performance Optimization
Initially, I needed to ensure the extension wouldn't overwhelm the browser or AI session. I implemented careful queue management with appropriate delays between processing operations. Finding the right balance between responsiveness (processing videos quickly) and stability (not crashing the AI session) required testing different delay values. The current implementation uses 50ms delays for skipped items, 500ms for cache hits, and 1000ms for full AI evaluations.

Challenge 3: State Management Across Page Navigation
YouTube is a single-page application, so navigating between pages doesn't reload the content script. I had to ensure that the MutationObserver remained active and that the AI queue continued processing across navigation events. The event-driven architecture using setTimeout for continuous queue processing solved this—the processOneItemFromQueue function always schedules its next execution, creating a persistent processing loop.


This project showed me that emerging browser APIs like on-device AI can solve real, personal problems in ways that weren't possible before. The extension successfully solves my original problem: I can now open YouTube right after a major event without fear, browse the content I want to watch, and let the AI quietly handle the filtering in the background.

Built With

  • chrome-languagemodel-api-(gemini-nano)
  • chrome-storage-api
  • html/css
  • javascript
Share this project:

Updates