Building a high-performance document platform on the web comes with a tricky architectural dilemma: server-side processing causes slow 50MB file uploads and high VPS server costs, while client-side processing can freeze the main UI thread if not engineered properly.
When building Fillora PDF, we designed a hybrid architecture combining multi-threaded WebAssembly Web Workers for client-side PDF tasks with dedicated server APIs for complex Office conversions.
Here is how we structured the Fillora Browser SDK, decoupled heavy tasks into background workers, and achieved sub-second local processing.
🏗️ 1. The Fillora Hybrid Architecture
Instead of routing every single file interaction through an expensive HTTP POST pipeline, we separated tools into two execution paths:
A. Client-Side WebAssembly Worker Path (Local Browser Memory)
PDF page manipulation, compression, OCR, and image rendering execute 100% locally inside client-side Web Workers using dedicated WebAssembly engines:
-
fillora-pdf-core.wasm— High-speed binary PDF document modification. -
fillora-image-core.wasm— Local image compression and rendering. -
fillora-ocr-core.wasm— Optical character recognition text extraction. -
fillora-table-core.wasm— Structural table detection for document parsing.
B. Dedicated Server API Stream Path
Complex Office document transformations (DOCX, PPTX, XLSX) stream through our high-speed server conversion pipeline (/api/convert/office) to maintain pixel-perfect layout fidelity.
⚡ 2. Offloading Heavy Compute to Background Web Workers
To keep the browser UI running smoothly at 60 FPS, heavy array operations (like parsing PDF page trees or compressing raw image buffers) are offloaded to dedicated worker scripts:
-
pdf-modify-worker&compression-worker -
ocr-worker&table-detection-worker -
pdf-render-worker&image-processing-worker
Here is how the Fillora Browser SDK dispatches local document tasks asynchronously without blocking the main event loop:
import { PDFDocument } from 'pdf-lib';
async function processPdfLocally(fileBuffer, pageRotations) {
// 1. Parse raw PDF binary in memory
const pdfDoc = await PDFDocument.load(fileBuffer);
const pages = pdfDoc.getPages();
// 2. Apply page-level transformations
pageRotations.forEach((angle, index) => {
if (pages[index]) {
pages[index].setRotation(angle);
}
});
// 3. Serialize transformed Uint8Array output
const outputBytes = await pdfDoc.save();
return outputBytes;
}
Because processing occurs directly in browser RAM via typed arrays (Uint8Array), small-to-medium PDF modifications execute in under 100 milliseconds with zero server upload wait time.
🤖 3. Intelligent Task Routing with Fillora DocPilot
To streamline user experience, we built DocPilot — an embedded natural language assistant widget.
When a user drops a document and requests an action (e.g. "Compress this file" or "Convert to PDF"), DocPilot inspects the file MIME type and intent, automatically routing the request to either:
- Local WebAssembly Worker Execution (for instant local downloads), or
- Dedicated Conversion Endpoint (for Office documents).
🛠️ 4. What We Built
We packaged this engine into Fillora PDF, offering a complete document utility ecosystem:
- Client-Side PDF Tools: Merge, split, rotate, crop, organize, watermark, and compress PDFs locally.
- Webpage to PDF Reader: Convert clean web layouts into printable documents.
- Security Utilities: Client-side password encryption, unlock, and redacting.
- AI Writing & Grammar Assistant: Real-time writing analysis and tone polisher.
💬 Developer Discussion
How are you currently handling heavy client-side processing in your web apps? Have you implemented WebAssembly core modules or Web Worker threads?
Explore our live implementation at fillorapdf.com and let me know your thoughts in the comments below!
This article was originally published by DEV Community and written by Rick M.
Read original article on DEV Community