<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mauro Pellonara</title>
    <description>The latest articles on DEV Community by Mauro Pellonara (@mpellonara).</description>
    <link>https://dev.to/mpellonara</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4101318%2Fdc04a55c-ae51-43e7-b024-1452b95f3fad.png</url>
      <title>DEV Community: Mauro Pellonara</title>
      <link>https://dev.to/mpellonara</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mpellonara"/>
    <language>en</language>
    <item>
      <title>How I Built an Open-Source Browser Extension to Detect AI Slop on YouTube</title>
      <dc:creator>Mauro Pellonara</dc:creator>
      <pubDate>Sun, 30 Aug 2026 11:33:09 +0000</pubDate>
      <link>https://dev.to/mpellonara/how-i-built-an-open-source-browser-extension-to-detect-ai-slop-on-youtube-2pc1</link>
      <guid>https://dev.to/mpellonara/how-i-built-an-open-source-browser-extension-to-detect-ai-slop-on-youtube-2pc1</guid>
      <description>&lt;h1&gt;
  
  
  How I Built an Open-Source Browser Extension to Detect AI Slop on YouTube
&lt;/h1&gt;

&lt;p&gt;If you've searched YouTube lately for tech tutorials, history documentaries, or product reviews, you've almost certainly encountered them: &lt;strong&gt;"faceless" AI content farms&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;These channels follow an automated template:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scrape trending search terms.&lt;/li&gt;
&lt;li&gt;Prompt an LLM (like ChatGPT) to spit out a 1,500-word script filled with generic buzzwords and filler phrases (&lt;em&gt;"In today's fast-paced digital landscape..."&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;Feed the text into a synthetic voice generator.&lt;/li&gt;
&lt;li&gt;Overlay random stock b-roll clips.&lt;/li&gt;
&lt;li&gt;Upload 5 to 10 videos every single day.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result is what the internet has dubbed &lt;strong&gt;"AI Slop"&lt;/strong&gt;—content produced purely to capture algorithmic search impressions and ad revenue, without offering genuine human insight or research.&lt;/p&gt;

&lt;p&gt;Frustrated by wasting time clicking on these videos, I decided to build &lt;strong&gt;&lt;a href="https://mauropello.github.io/stop-the-slop/" rel="noopener noreferrer"&gt;Stop the Slop&lt;/a&gt;&lt;/strong&gt;, an open-source browser extension for Google Chrome and Mozilla Firefox that detects AI-generated scripts in real-time and overlays probability badges on thumbnails before you click.&lt;/p&gt;

&lt;p&gt;In this post, I'll walk through the architecture, technical hurdles of Manifest V3, and how I achieved &lt;strong&gt;sub-50ms thumbnail badging&lt;/strong&gt; with Cloudflare Workers and D1.&lt;/p&gt;




&lt;h2&gt;
  
  
  The System Architecture
&lt;/h2&gt;

&lt;p&gt;Here is how the entire system connects from browser tab to edge AI analysis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────────────────────────────────────────────────────────┐
│                   Browser (Client)                     │
│                                                        │
│  ┌───────────────────────┐   ┌──────────────────────┐  │
│  │ MAIN World Script     │   │ ISOLATED World Script│  │
│  │ (Extracts Captions)   │──&amp;gt;│ (UI Badges &amp;amp; Popup)  │  │
│  └───────────────────────┘   └──────────┬───────────┘  │
└─────────────────────────────────────────┼──────────────┘
                                          │
                         Batch Lookups &amp;amp; Transcripts
                                          │
                                          ▼
┌────────────────────────────────────────────────────────┐
│               Cloudflare Worker (Edge API)             │
│                                                        │
│   ┌─────────────────────┐      ┌────────────────────┐  │
│   │ Cloudflare D1 Cache │&amp;lt;────&amp;gt;│ Sapling AI API +   │  │
│   │ (Edge SQL Database) │      │ Heuristic Filters  │  │
│   └─────────────────────┘      └────────────────────┘  │
└────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  1. Extracting Captions in Manifest V3 (MAIN vs ISOLATED Worlds)
&lt;/h2&gt;

&lt;p&gt;YouTube is a complex Single Page Application (SPA) where navigation occurs without full page reloads.&lt;/p&gt;

&lt;p&gt;To get the video's script, we need access to YouTube's internal player caption tracks. However, content scripts in Chrome extensions typically run in an &lt;strong&gt;ISOLATED world&lt;/strong&gt;, where they can access the DOM but cannot read JavaScript variables or objects created by YouTube's page scripts.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution: Multi-World Scripts
&lt;/h3&gt;

&lt;p&gt;In our &lt;code&gt;manifest.json&lt;/code&gt;, we configure two content scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"content_scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"https://www.youtube.com/*"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"js"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"content-main.js"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"world"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MAIN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"run_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"document_start"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"https://www.youtube.com/*"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"js"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"content.js"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"world"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ISOLATED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"run_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"document_idle"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;content-main.js&lt;/code&gt; (MAIN world)&lt;/strong&gt;: Has direct access to &lt;code&gt;window.ytplayer&lt;/code&gt; and YouTube's player APIs. It grabs the active caption track URL or transcript payloads and posts a &lt;code&gt;window.postMessage&lt;/code&gt; event.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;content.js&lt;/code&gt; (ISOLATED world)&lt;/strong&gt;: Listens for the message, securely processes the transcript, manages thumbnail DOM badges, and talks to the background service worker and Cloudflare API.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  2. Fast Edge Caching with Cloudflare Workers + D1
&lt;/h2&gt;

&lt;p&gt;One major UX goal was: &lt;strong&gt;users shouldn't have to click into a video and wait 3 seconds to find out if it's AI slop.&lt;/strong&gt; We wanted badges directly on thumbnails across the YouTube Home feed, Search results, and sidebar recommendations.&lt;/p&gt;

&lt;p&gt;However, calling an AI detection model for 30 video thumbnails every time a user scrolls would cause unbearable latency and blow through API rate limits.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Caching Strategy
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;When any user analyzes a video, the transcript is evaluated and the resulting score is saved in a &lt;strong&gt;Cloudflare D1 edge database&lt;/strong&gt; keyed by YouTube &lt;code&gt;video_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;When the extension scans the current feed, &lt;code&gt;content.js&lt;/code&gt; gathers all visible video IDs and sends a single batch query:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;   POST /api/batch-lookup
   Body: { "videoIds": ["dQw4w9WgXcQ", "abc12345", ...] }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Cloudflare D1 answers in &lt;strong&gt;&amp;lt;50ms&lt;/strong&gt;, returning the cached verdicts for any community-analyzed videos.&lt;/li&gt;
&lt;li&gt;Thumbnail badges render instantly on screen without jitter or delay!&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  3. The Detection Pipeline: Combining Models &amp;amp; Heuristics
&lt;/h2&gt;

&lt;p&gt;AI detection is notoriously prone to false positives if relied upon blindly. To provide trustworthy results, Stop the Slop uses a hybrid pipeline:&lt;/p&gt;

&lt;h3&gt;
  
  
  A. Linguistic Classification
&lt;/h3&gt;

&lt;p&gt;Transcripts are processed through Sapling AI’s linguistic classifier, which evaluates perplexity and burstiness across the text to estimate the probability that the text was generated by a large language model.&lt;/p&gt;

&lt;h3&gt;
  
  
  B. Heuristic Pattern Scoring
&lt;/h3&gt;

&lt;p&gt;LLMs frequently fall back on predictable transition patterns and clichés, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;"In today's fast-paced digital era..."&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"It is important to remember that..."&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"Furthermore, let us delve into..."&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"In conclusion, by harnessing the power of..."&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We check the frequency of these stereotypical transition structures.&lt;/p&gt;

&lt;h3&gt;
  
  
  C. Visual Sentence Heatmap
&lt;/h3&gt;

&lt;p&gt;Rather than just showing an opaque percentage, Stop the Slop renders an interactive sentence breakdown inside the popup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Example of Highlighted Sentence Rendering --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sentence-item score-high"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"score-badge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;94%&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sentence-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;In today's fast-paced digital era, the landscape of modern technology is evolving at an unprecedented pace.&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This transparency allows users to see &lt;em&gt;why&lt;/em&gt; the video was flagged and make their own informed judgement.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Privacy-First by Design
&lt;/h2&gt;

&lt;p&gt;Browser extensions often have a terrible reputation for data harvesting. With Stop the Slop, I made strict architectural commitments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero User Telemetry&lt;/strong&gt;: No Google Analytics, no Mixpanel, no user IDs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Accounts or Logins&lt;/strong&gt;: The extension functions out of the box with zero signups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strictly Scoped Permissions&lt;/strong&gt;: Only requests access to YouTube tabs to read public caption tracks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approved by Mozilla AMO&lt;/strong&gt;: Received approval with strict &lt;code&gt;data_collection_permissions: ["none"]&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Lessons Learned Building for Manifest V3
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Service Worker Lifecycle&lt;/strong&gt;: In MV3, background service workers terminate when idle. Any in-memory state will be lost unless stored in &lt;code&gt;chrome.storage.local&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DOM Mutation Resilience&lt;/strong&gt;: YouTube updates its DOM structure frequently. Using generic CSS attribute selectors (&lt;code&gt;ytd-thumbnail&lt;/code&gt;, &lt;code&gt;a#thumbnail&lt;/code&gt;) combined with &lt;code&gt;MutationObserver&lt;/code&gt; debouncing keeps thumbnail badging smooth and resilient.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Browser Compatibility&lt;/strong&gt;: Maintaining simultaneous support for Chromium (&lt;code&gt;extension-chrome/&lt;/code&gt;) and Firefox (&lt;code&gt;extension-firefox/&lt;/code&gt;) requires keeping the core content scripts modular and adhering strictly to standard web extension APIs.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Try It Out &amp;amp; Get Involved
&lt;/h2&gt;

&lt;p&gt;Stop the Slop is 100% free and open-source under the MIT license.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chrome Web Store&lt;/strong&gt;: &lt;a href="https://chromewebstore.google.com/detail/stop-the-slop-%E2%80%94-ai-script/afifehnicpeokjjhpbicikfoalkemelg" rel="noopener noreferrer"&gt;Install for Chrome, Brave &amp;amp; Edge&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firefox Add-ons (AMO)&lt;/strong&gt;: &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/stop-the-slop-ai-video-detect/" rel="noopener noreferrer"&gt;Install on Firefox&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Official Website &amp;amp; Interactive Simulator&lt;/strong&gt;: &lt;a href="https://mauropello.github.io/stop-the-slop/" rel="noopener noreferrer"&gt;https://mauropello.github.io/stop-the-slop/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;a href="https://github.com/MauroPello/stop-the-slop" rel="noopener noreferrer"&gt;https://github.com/MauroPello/stop-the-slop&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're interested in browser extensions, edge computing, or open-source tools, I’d love your feedback, PRs, and a star on GitHub! 🌟&lt;/p&gt;

</description>
      <category>ai</category>
      <category>extensions</category>
      <category>opensource</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
