惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
Check Point Blog
有赞技术团队
有赞技术团队
博客园 - 三生石上(FineUI控件)
博客园_首页
博客园 - 【当耐特】
WordPress大学
WordPress大学
月光博客
月光博客
博客园 - 叶小钗
S
SegmentFault 最新的问题
雷峰网
雷峰网
H
Help Net Security
宝玉的分享
宝玉的分享
A
About on SuperTechFans
IT之家
IT之家
J
Java Code Geeks
Hugging Face - Blog
Hugging Face - Blog
D
DataBreaches.Net
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 聂微东
T
The Blog of Author Tim Ferriss
B
Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Y
Y Combinator Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
How I Built a Blazingly Fast, Privacy-First Batch Image C...
Sapianyi · 2026-05-24 · via DEV Community
Cover image for How I Built a Blazingly Fast, Privacy-First Batch Image Converter in the Browser Using OPFS and Web Workers

Sapianyi

The Problem with Modern Web Tools

Most online image converters follow a flawed pattern: you upload your files to a cloud server, their backend processes them, and you download them back. If you are handling hundreds of images, this layout introduces massive network bottlenecks. More importantly, it completely breaks user data privacy.

I wanted to build a batch image conveyor that processes hundreds of files instantly, supports next-gen formats (WebP, AVIF, QOI), and ensures that zero bytes of user data ever leave the local machine.

But doing this purely on the client side brings two massive boss-level challenges:

  1. UI Freezing: Image compression is CPU-intensive. Running it on the main thread makes the browser completely unresponsive.

  2. Out of Memory (OOM) Crashes: Keeping raw pixel arrays for 100+ high-res images in the browser's JavaScript heap will instantly crash the tab.

Here is how I solved this architecture using modern Web APIs

The Architecture: Continuous Streaming Pipeline

To bypass the browser's RAM and CPU limitations, I built a decoupled streaming architecture. Dev.to supports Mermaid.js rendering natively, so here is exactly how the data flows from your desktop back to your download folder:

graph TD
    Input[User Input: Drag & Drop / Files] -->|Stream Raw Bytes| OPFS[(OPFS Sandbox Disk)]
    OPFS -->|Sequential Read| Pool[WorkerPool Manager]

    subgraph Multi-Threaded Core (Backpressure Capped < 200MB)
        Pool -->|Payload 1| W1[Worker 1: WebP/Lanczos3]
        Pool -->|Payload 2| W2[Worker 2: Nearest/QOI]
        Pool -->|Payload N| WN[Worker N: AVIF WASM]
    end

    W1 -->|Compressed Blobs| Zip[fflate ZIP Archiver]
    W2 -->|Compressed Blobs| Zip
    WN -->|Compressed Blobs| Zip

    Zip -->|Continuous Binary Stream| Download[Instant Local Download]

    style Multi-Threaded Core fill:#121214,stroke:#39ff14,stroke-width:2px
    style OPFS fill:#1f2937,stroke:#58a6ff,stroke-width:1px
    style Download fill:#065f46,stroke:#10b981,stroke-width:2px

Enter fullscreen mode Exit fullscreen mode

  1. Eliminating RAM Bloat with OPFS (Origin Private File System)
    Instead of loading dropped files directly into memory, my pipeline instantly intercepts the stream and writes the raw binary data into the Origin Private File System (OPFS) sandbox.
    OPFS acts as a fast, isolated virtual disk inside the browser. This allows the application to accept a folder with 500+ items without consuming more than a few megabytes of actual RAM.

  2. Multi-Threading with a Custom Web Worker Pool
    To keep the frame rate at a buttery-smooth 60fps, all conversion and scaling tasks are delegated to a pool of background Web Workers. The number of active workers scales dynamically based on the user's CPU thread count (navigator.hardwareConcurrency).
    Each worker loads target codecs and executes compression in total isolation from the UI.

  3. Implementing Strict Backpressure
    If you feed 100 workers at once, the browser will still crash due to rapid memory allocation. To counter this, I implemented a custom Backpressure mechanism inside the WorkerPool.
    The pool tracks total active byte allocation. If the memory footprint of "in-flight" images approaches 200MB, the pipeline pauses reading from OPFS. As soon as a worker finishes compressing an image and releases its buffer, the pipeline pushes the next asset forward.

  4. Streaming ZIP Archive Compilation
    Once compressed, storing finished assets back to memory to create a ZIP file would defeat the whole purpose. Instead, the architecture streams individual compressed files into fflate on the fly, packaging them into a continuous Blob stream that triggers an instant local download.

Why not a full WebAssembly monolith?

A common question for this kind of heavy-lifting utility is: "Why didn't you just compile a native C++ or Rust image processing library directly into a single WebAssembly (WASM) binary?"

While WASM is incredibly fast, using it as a monolithic backend inside the browser has critical trade-offs for this specific architecture:

  • Native Browser Strengths: Modern browsers already have hyper-optimized, native, hardware-accelerated pipelines for rendering and encoding formats like WebP. Wrapping JS APIs inside a Worker Pool lets us use these native engines for free without the penalty of huge WASM binary overhead.
  • OPFS Threading Sync: Working closely with the Origin Private File System, generating local sandboxed URLs, and handling dynamic runtime cancellations is significantly easier and safer through asynchronous JavaScript/TypeScript Workers.
  • Hybrid Approach: Instead of a full-WASM monolith, I chose a hybrid ecosystem. JavaScript handles orchestration, pipeline state, and native codecs, while WebAssembly is injected strictly where JS is too slow — specifically inside background workers for heavy Lanczos3 image resampling (via Pica) and advanced AVIF encoding.

The Tech Stack Inside

  • Vite + TypeScript: Fast building and type-safe core pipelines.
  • Pica: Industrial-grade Lanczos3 resampling filter (WASM accelerated).
  • @jsquash/avif: Multi-threaded, industrial-grade AVIF encoding algorithms compiled to WASM.
  • fflate: High-speed, memory-efficient binary ZIP compression.
  • StreamSaver: Low-overhead client-side streaming downloads.

Performance Benchmarks

Testing the pipeline on an 8-core machine yielded impressive results, proving that browser storage can compete with native desktop tools:

  • 100 high-res images (10MB each): ~45 seconds
  • 500 mobile assets (2MB each): ~2 minutes
  • Max RAM Usage: Stays under 200MB at all times
  • Disk Usage: Streamed directly to OPFS, zero memory leak

Check it out!

The project is completely free, open-source, and has no trackers, cookies, or ads.

I would love to hear your thoughts on this architecture! How are you handling heavy asset manipulation on the client side? Let's discuss in the comments!