Projects
/

Resilient MV3 Processing Pipeline

Resilient MV3 Processing Pipeline

Chrome Extension · Manifest V3 · Service Worker · Offscreen Document · ffmpeg

A client-side architecture that implements a reliable, long-running processing pipeline within the strict execution constraints of Chrome Manifest V3.

Overview

This project explores a broader engineering problem than media processing itself: how to build a reliable, long-running client-side processing pipeline inside the highly constrained Chrome Manifest V3 (MV3) environment.

The system performs a multi-stage workflow involving network requests, CPU-intensive processing, inter-process communication, persistent state management, and file generation—all without any backend services. The specific workload is less important than the engineering challenges it exposed around browser architecture, resource management, and reliability.

Engineering Highlights

  • Designed a multi-context MV3 architecture
  • Eliminated large binary IPC by redesigning ownership boundaries
  • Built a reliable long-running browser processing pipeline
  • Optimized for predictable completion instead of maximum concurrency
  • Architecture driven by technical spikes rather than assumptions

Technical Spikes

Before designing the system, I built a series of focused experiments to answer the highest-risk technical questions.

QuestionResultImpact
Can the content script fetch media directly?❌ No — CORS limitations.Handover to service worker and offscreen document
Can WASM be loaded dynamically?❌ No — blocked by MV3 CSP.Bundle WASM with extension
Can long remux jobs execute in the Service Worker?❌No — lifecycle and DOM limitations.Introduced offscreen document
Can large binaries be transferred over runtime messaging?❌ Not reliably.Pass metadata instead of binary data

Architecture

The final design separates responsibilities across multiple browser contexts, each chosen for what it is best suited to do.

  • Service Worker — orchestrates jobs, scheduling, lifecycle management, and persistent state.
  • Content Script — discovers data from the active page.
  • Offscreen Document — performs long-running browser-side computation.
  • Popup UI — renders application state and accepts user actions.
Loading diagram...

Engineering Decisions

Designing Around MV3 Runtime Constraints

Manifest V3 intentionally limits long-running background execution. The Service Worker can be suspended at any time and lacks APIs required by browser-side computation.

Rather than fighting the platform, I moved compute-heavy work into an Offscreen Document while keeping orchestration inside the Service Worker.

This separation allows each component to operate within its strengths while avoiding lifecycle issues that would otherwise make the pipeline unreliable.

Moving Computation to the Data

My initial design transferred large binary payloads between extension contexts.

Although technically possible for small payloads, this quickly became unreliable for larger workloads due to serialization overhead and browser messaging limitations.

Instead of moving the data, I changed the architecture so that only lightweight metadata is passed between components.

Each execution context downloads and processes the data it owns.

This dramatically reduced inter-process communication, simplified component boundaries, and eliminated an entire class of reliability issues.

The lesson extends well beyond browser extensions:

Whenever possible, move computation to the data instead of moving data to computation.

Optimizing for Reliability Instead of Maximum Parallelism

One design decision initially felt counterintuitive: the processing pipeline intentionally allows only one active processing task at a time.

Early experiments showed that concurrent processing significantly increased memory pressure and made failures unpredictable.

Although higher concurrency improved theoretical throughput, it reduced real-world completion rates.

I chose predictable execution over maximum utilization.

For long-running user-facing workflows, consistent completion is often more valuable than peak performance.

This is a tradeoff I would make again.

Building a Resilient Job Lifecycle

Long-running browser tasks are vulnerable to interruptions:

  • browser contexts can disappear,
  • users can close tabs,
  • UI components can be destroyed,
  • background workers can be suspended.

To avoid inconsistent states, the Service Worker acts as the single source of truth for every job.

UI components simply render current state rather than owning it.

Jobs are also explicitly tied to the originating browser tab, allowing immediate cleanup if the user closes the source page. This prevents orphaned work, unnecessary network activity, and resource leaks.

The result is a pipeline that behaves predictably even when browser lifecycle events occur unexpectedly.

Key Engineering Takeaways

  • Validate unknowns - Technical spikes before architecture
  • Reduce IPC - Move computation to the data
  • Optimize reliability - Throughput isn't always the priority
  • Separate ownership - Each runtime has one responsibility