<?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: thisran</title>
    <description>The latest articles on DEV Community by thisran (@khaithisran).</description>
    <link>https://dev.to/khaithisran</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%2F4141373%2F78fb263b-c004-4a8f-9379-85da668946bc.jpg</url>
      <title>DEV Community: thisran</title>
      <link>https://dev.to/khaithisran</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/khaithisran"/>
    <language>en</language>
    <item>
      <title>How We Built a Zero-Backend, In-Browser PDF Engine to Eliminate Cloud SaaS Bloat</title>
      <dc:creator>thisran</dc:creator>
      <pubDate>Fri, 16 Oct 2026 04:23:15 +0000</pubDate>
      <link>https://dev.to/khaithisran/how-we-built-a-zero-backend-in-browser-pdf-engine-to-eliminate-cloud-saas-bloat-16g0</link>
      <guid>https://dev.to/khaithisran/how-we-built-a-zero-backend-in-browser-pdf-engine-to-eliminate-cloud-saas-bloat-16g0</guid>
      <description>&lt;p&gt;As cloud infrastructure bills explode and tech giants pour billions into expanding hyperscaler capacity, modern B2B SaaS has quietly standardized an absurd architectural pattern: charging enterprise seats $20 to $40 per month to execute basic byte manipulations on remote servers.&lt;/p&gt;

&lt;p&gt;Consider what actually happens when a user clicks "Rotate Page" or "Merge Two Documents" on a legacy cloud-based PDF platform like Adobe Acrobat Web or DocuSign:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A 40MB contract containing sensitive personal identification information (PII), proprietary financial terms, or executive signatures is serialized into a multi-part form payload.&lt;/li&gt;
&lt;li&gt;The payload traverses high-latency consumer broadband, undergoes TLS termination at an API gateway, and lands in an ephemeral Amazon S3 bucket.&lt;/li&gt;
&lt;li&gt;An AWS Lambda worker or containerized microservice pulls the payload, spins up a headless C++ engine or Ghostscript subprocess, parses the document tree, flips a single integer in a page object’s dictionary (&lt;code&gt;/Rotate 90&lt;/code&gt;), and serializes it back to S3.&lt;/li&gt;
&lt;li&gt;The server generates a pre-signed URL, and the client downloads the updated 40MB file over the WAN.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This workflow burns compute cycles, racks up egress costs, introduces compliance vulnerabilities (GDPR, HIPAA, SOC 2), and completely fails the moment a user’s internet drops. &lt;/p&gt;

&lt;p&gt;When we architected &lt;strong&gt;&lt;a href="https://handlemyfile.com" rel="noopener noreferrer"&gt;HandleMyFile&lt;/a&gt;&lt;/strong&gt;, we set out to prove that this client-server round-trip is completely unnecessary. Modern client devices—from M-series MacBooks to budget Android phones—ship with multi-core processors, hardware-accelerated SIMD units, and multi-gigabyte memory pools. By combining WebAssembly (WASM), Web Workers, and the native Web Crypto API, we can execute complex PDF manipulation, document signing, client-side OCR, and lossless compression locally inside the browser sandbox at zero marginal compute cost.&lt;/p&gt;

&lt;p&gt;Here is the architectural breakdown of how we built a zero-backend, 100% private in-browser document workstation.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Underlying Anatomy of a PDF
&lt;/h3&gt;

&lt;p&gt;To understand why remote processing is an antipattern for simple document tasks, you have to look at the PDF specification (ISO 32000-1). &lt;/p&gt;

&lt;p&gt;A PDF is essentially an indexed graph of indirect objects terminated by a cross-reference (&lt;code&gt;xref&lt;/code&gt;) table or stream. The structural skeleton looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%PDF-1.7
1 0 obj &amp;lt;&amp;lt; /Type /Catalog /Pages 2 0 R &amp;gt;&amp;gt; endobj
2 0 obj &amp;lt;&amp;lt; /Type /Pages /Kids [3 0 R] /Count 1 &amp;gt;&amp;gt; endobj
3 0 obj &amp;lt;&amp;lt; /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 4 0 R &amp;gt;&amp;gt; endobj
4 0 obj &amp;lt;&amp;lt; /Length 55 &amp;gt;&amp;gt; stream
...stream bytes...
endstream endobj
xref
0 5
0000000000 65535 f 
0000000009 00000 n 
...
trailer &amp;lt;&amp;lt; /Size 5 /Root 1 0 R &amp;gt;&amp;gt;
startxref
562
%%EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you merge two documents, you do not need to decode and re-encode high-resolution image streams or recalculate font raster matrices. You simply need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parse the indirect object dictionaries across both source buffers.&lt;/li&gt;
&lt;li&gt;Re-index the object identifiers to avoid collision in the target namespace.&lt;/li&gt;
&lt;li&gt;Update the root &lt;code&gt;/Pages&lt;/code&gt; array to point to the newly indexed page objects.&lt;/li&gt;
&lt;li&gt;Append an incremental update or rebuild the &lt;code&gt;xref&lt;/code&gt; table at the end of the byte stream.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Running this in a remote Python or Ruby container on AWS ECS wastes millions of CPU cycles on network I/O serialization. The browser's native JavaScript engine can parse, mutate, and rebuild these cross-reference structures in less time than it takes to complete a single TLS handshake.&lt;/p&gt;




&lt;h3&gt;
  
  
  System Architecture: Moving Compute to the Edge of the Edge
&lt;/h3&gt;

&lt;p&gt;To make client-side PDF processing production-viable for 100MB+ documents without freezing the user interface, we designed a zero-backend pipeline decoupled across isolated threads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────────────────────────────────────────────────────────┐
│                   Main Browser Thread                  │
│  - React UI / DOM Updates                              │
│  - Canvas Preview Layer                                │
│  - Drag &amp;amp; Drop Event Handling                          │
└──────────────────────────┬─────────────────────────────┘
                           │ Zero-Copy Transfer
                           │ (Transferable Objects / SharedArrayBuffer)
                           ▼
┌────────────────────────────────────────────────────────┐
│                   Dedicated Web Worker                 │
│  ┌──────────────────┐  ┌─────────────────────────────┐ │
│  │  pdf-lib / WASM  │  │      Tesseract.js WASM      │ │
│  │  Document Parser │  │      Local Optical OCR      │ │
│  └─────────┬────────┘  └──────────────┬──────────────┘ │
│            │                          │                │
│            ▼                          ▼                │
│  ┌───────────────────────────────────────────────────┐ │
│  │           Web Crypto API Sandbox Subsystem        │ │
│  │        (SHA-256 Hashing, Ephemeral Keys)          │ │
│  └───────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  1. Zero-Copy Thread Boundary Offloading
&lt;/h4&gt;

&lt;p&gt;Executing heavy byte manipulation on the browser's main execution context drops frames and degrades the user experience. We isolate all document operations inside long-lived Web Workers.&lt;/p&gt;

&lt;p&gt;Instead of passing file payloads via standard structured cloning (which performs an expensive, blocking memory copy), we transfer ownership of the underlying &lt;code&gt;ArrayBuffer&lt;/code&gt; directly to the worker context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Main Thread dispatching to Worker&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PROCESS_DOCUMENT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="c1"&gt;// `buffer.byteLength` is now 0 on the main thread (zero memory allocation overhead)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Byte Manipulation via &lt;code&gt;pdf-lib&lt;/code&gt; &amp;amp; PDF.js WASM
&lt;/h4&gt;

&lt;p&gt;For structural edits (splitting, merging, page reordering, vector signing), we leverage compiled byte parsers (&lt;code&gt;pdf-lib&lt;/code&gt; and custom WebAssembly glue routines). The engine loads the raw binary array into memory, traverses the object tree, injects user vector signatures or rotation flags, and re-emits a compressed binary blob directly into the client's memory allocation pool.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Client-Side OCR via Tesseract.js WASM
&lt;/h4&gt;

&lt;p&gt;Optical Character Recognition has historically been one of the biggest excuses for shipping documents to remote infrastructure. By compiling Tesseract’s native C++ engine to WebAssembly using Emscripten, we load quantized OCR models (under 2MB compressed) directly into client IndexedDB storage. The worker pulls image canvas buffers from PDF rasterizations, runs multi-threaded SIMD inference locally, and outputs selectable text layers without a single byte ever touching a third-party server.&lt;/p&gt;




&lt;h3&gt;
  
  
  Concrete Implementation: Non-Blocking Local Merge Engine
&lt;/h3&gt;

&lt;p&gt;Here is a practical look at how our dedicated Web Worker implements non-blocking, multi-file merging. Notice the use of &lt;code&gt;ignoreEncryption&lt;/code&gt; handling, safe indirect object copying, and direct memory export:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pdf-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Merges multiple raw PDF ArrayBuffers completely in-memory
 * without allocating duplicate memory or blocking the main event loop.
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;mergePDFsLocally&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pdfBuffers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;ArrayBuffer&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Blob&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Initialize the target document context&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mergedDoc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;pdfBuffers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Load binary stream with resilient error boundaries&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sourceDoc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pdfBuffers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
      &lt;span class="na"&gt;ignoreEncryption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;updateMetadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; 
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Extract page indices and transfer ownership of indirect objects&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pageIndices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sourceDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPageIndices&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;copiedPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mergedDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copyPages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sourceDoc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pageIndices&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Append mutated page objects into the consolidated catalog tree&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;copiedPages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;mergedDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Serialize the new cross-reference table and write the EOF marker&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mergedBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mergedDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;useObjectStreams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Return an immutable binary Blob ready for immediate disk download&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;mergedBytes&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/pdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By setting &lt;code&gt;useObjectStreams: true&lt;/code&gt;, the engine packs non-stream objects into compressed object streams, often yielding a merged file size smaller than the sum of its inputs—all within a single-digit millisecond timeframe.&lt;/p&gt;




&lt;h3&gt;
  
  
  Performance Benchmark: Cloud SaaS vs. In-Browser WASM
&lt;/h3&gt;

&lt;p&gt;To quantify the operational advantages, we benchmarked standard document operations on a mid-tier client machine (MacBook Air M2, 16GB RAM) running Chrome 124 over an average 50 Mbps residential broadband connection.&lt;/p&gt;

&lt;p&gt;We compared the traditional client-to-cloud round-trip against the local execution model implemented in HandleMyFile:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Architectural Metric&lt;/th&gt;
&lt;th&gt;Commercial Cloud SaaS (Adobe / SmallPDF)&lt;/th&gt;
&lt;th&gt;HandleMyFile Client-Side WASM&lt;/th&gt;
&lt;th&gt;Performance Delta&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cold Start / Latency (30MB File)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1,840ms (TLS + S3 Upload + Worker Pull)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;18ms (Zero Network Transit)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;102x faster&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Page Rotation Execution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~2,100ms end-to-end&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;11ms in-memory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;190x faster&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Merge 3x 15MB PDFs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~4,200ms (Multiple file uploads)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;240ms (Worker processing)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;17.5x faster&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Privacy &amp;amp; Compliance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PII transits WAN &amp;amp; persists in cloud storage&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Air-gapped in browser RAM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Zero exposure&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Operational Infrastructure Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High (Compute, S3 IOPS, Egress bills)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.00 / Zero server compute&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100% cost reduction&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Offline Capability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Fails immediately without network&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅ Fully operational via Service Worker&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Infinite resilience&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Overcoming Client-Side Sandboxing Challenges
&lt;/h3&gt;

&lt;p&gt;A local-first, in-browser architecture is not without its engineering hurdles. When you abandon server clusters, you take on browser-specific constraints:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Memory Ceilings and V8 Garbage Collection Spikes
&lt;/h4&gt;

&lt;p&gt;A tab's V8 heap limit typically caps out between 2GB and 4GB depending on the platform. If a user drops twenty 80MB scanned documents into the browser, holding multiple uncompressed raster bitmaps in RAM will cause the tab to crash. &lt;/p&gt;

&lt;p&gt;To solve this, we implement chunked stream processing and immediate object disposal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We read files via &lt;code&gt;ReadableStream&lt;/code&gt; interfaces.&lt;/li&gt;
&lt;li&gt;We avoid converting entire files to base64 strings (which introduces a 33% memory footprint penalty).&lt;/li&gt;
&lt;li&gt;Once pages are extracted or rendered to canvas, we explicitly dereference the source &lt;code&gt;ArrayBuffer&lt;/code&gt; objects to allow the V8 garbage collector to reclaim allocations before loading the next chunk.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. Font Subsetting and Vector Glyph Embedding
&lt;/h4&gt;

&lt;p&gt;One of the trickiest parts of drawing signatures locally is embedding custom vector typefaces. If you embed a full TrueType font (&lt;code&gt;.ttf&lt;/code&gt;) into a document every time someone types their name, the output file balloons by several megabytes. We implemented dynamic font subsetting: analyzing the exact glyphs used in the user's name, discarding the unused unicode character maps, and serializing only a fraction of the font table into the final document tree.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Paradigm Shift: Why Edge-Only Architecture Matters
&lt;/h3&gt;

&lt;p&gt;Every year, software engineering teams spend millions of dollars scaling serverless orchestration, managing Kubernetes clusters, and mitigating database vulnerabilities—often to do things the client device was already capable of doing faster, cheaper, and more privately.&lt;/p&gt;

&lt;p&gt;By treating the browser as a hardened, multi-threaded execution sandbox rather than a thin presentation layer, we completely eliminate server infrastructure costs. There are no AWS Lambda cold starts, no egress surcharges, no data breach liabilities, and no subscription paywalls for basic file actions.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Try the live tool here:&lt;/strong&gt; &lt;a href="https://handlemyfile.com" rel="noopener noreferrer"&gt;https://handlemyfile.com&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Open Discussion for Senior Engineers
&lt;/h3&gt;

&lt;p&gt;As WebAssembly runtimes mature and browsers gain direct access to hardware-accelerated WebGPU and WebAssembly SIMD primitives:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Where do you draw the line between client-side browser execution and edge serverless workers (Cloudflare Workers, Fastly Compute@Edge) in your stack?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What architectural barriers still prevent your team from adopting local-first, zero-backend processing for user files?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's discuss in the comments below.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webassembly</category>
      <category>privacy</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Stop Paying $240/yr to Rotate a PDF: Building an In-Browser Document Workstation with WASM (Engineering Case Study 2026)</title>
      <dc:creator>thisran</dc:creator>
      <pubDate>Wed, 14 Oct 2026 07:12:51 +0000</pubDate>
      <link>https://dev.to/khaithisran/stop-paying-240yr-to-rotate-a-pdf-building-an-in-browser-document-workstation-with-wasm-578h</link>
      <guid>https://dev.to/khaithisran/stop-paying-240yr-to-rotate-a-pdf-building-an-in-browser-document-workstation-with-wasm-578h</guid>
      <description>&lt;p&gt;Every software engineer has experienced the indignity of needing to sign an NDA, rotate a tenancy agreement, or compress a 50MB PDF—only to hit an Adobe or DocuSign paywall:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adobe: &lt;strong&gt;$239.88 / year&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;DocuSign: &lt;strong&gt;$480 / year&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Random online PDF tools: &lt;em&gt;"Upload your confidential tax audits to our unencrypted AWS S3 bucket in Northern Virginia. Don't worry, we promise to delete it in 2 hours ;)"&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Manipulating PDF byte trees, adding digital signature layers, and extracting text via OCR does not require a remote server infrastructure.&lt;/p&gt;

&lt;p&gt;We built &lt;strong&gt;&lt;a href="https://handlemyfile.com" rel="noopener noreferrer"&gt;HandleMyFile&lt;/a&gt;&lt;/strong&gt; as a 100% in-browser, client-side document workstation.&lt;/p&gt;




&lt;h3&gt;
  
  
  Multi-Threaded PDF Manipulation in Browser Memory
&lt;/h3&gt;

&lt;p&gt;Using &lt;code&gt;pdf-lib&lt;/code&gt; compiled to JavaScript and running inside Web Workers, we can merge, split, rotate, and compress multi-page documents without transferring a single byte over the network:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pdf-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;mergePDFsLocally&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pdfArrayBuffers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mergedPdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;pdfArrayBuffers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ignoreEncryption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;copiedPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mergedPdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copyPages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPageIndices&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nx"&gt;copiedPages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;mergedPdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pdfBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mergedPdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;pdfBytes&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/pdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Key Capabilities:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In-RAM PDF Compression:&lt;/strong&gt; Reduces bloated scans from 80MB to under 4MB for email attachments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client-Side E-Signatures:&lt;/strong&gt; Draw digital signatures on contracts without recurring DocuSign fees.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WASM OCR:&lt;/strong&gt; Extract text from blurry scanned documents locally via Tesseract WebAssembly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Air-Gapped Privacy:&lt;/strong&gt; Complete confidentiality for medical, legal, and financial records.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test the workstation:&lt;br&gt;
👉 &lt;strong&gt;HandleMyFile:&lt;/strong&gt; &lt;a href="https://handlemyfile.com" rel="noopener noreferrer"&gt;https://handlemyfile.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How do you handle document security and PDF workflows in your engineering team? Let's discuss!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webassembly</category>
      <category>privacy</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Compressing 4K Video in the Browser: Replacing $20/mo Cloud SaaS with WebCodecs &amp; WebGPU</title>
      <dc:creator>thisran</dc:creator>
      <pubDate>Sat, 10 Oct 2026 15:14:45 +0000</pubDate>
      <link>https://dev.to/khaithisran/compressing-4k-video-in-the-browser-replacing-20mo-cloud-saas-with-webcodecs-webgpu-2bnc</link>
      <guid>https://dev.to/khaithisran/compressing-4k-video-in-the-browser-replacing-20mo-cloud-saas-with-webcodecs-webgpu-2bnc</guid>
      <description>&lt;p&gt;Cloud video compressors are one of the most frustrating traps on the web:&lt;br&gt;
"Your video exceeds the 500MB free upload limit. Please upgrade to Pro for $19.99/mo to continue."&lt;/p&gt;

&lt;p&gt;Why are developers transferring gigabytes of raw 4K video over the internet just to re-encode H.264 / AV1 frames?&lt;br&gt;
Modern laptops have multi-core processors with dedicated hardware video encoders. With the standardized &lt;strong&gt;WebCodecs API&lt;/strong&gt; and &lt;strong&gt;WebGPU&lt;/strong&gt;, the browser can access native hardware acceleration to compress gigabytes of video locally in device RAM.&lt;/p&gt;

&lt;p&gt;We built the In-Browser Video Compressor on &lt;strong&gt;&lt;a href="https://solvemymedia.com/compress-video" rel="noopener noreferrer"&gt;SolveMyMedia&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;


&lt;h3&gt;
  
  
  Hardware-Accelerated Compression via WebCodecs
&lt;/h3&gt;

&lt;p&gt;Instead of running slow software transcoders on a single thread, WebCodecs exposes low-level access to the GPU hardware:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VideoEncoder&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;muxer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addVideoChunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Encoding error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;codec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;avc1.640028&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// H.264 High Profile&lt;/span&gt;
  &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1920&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;bitrate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="nx"&gt;_500_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 4.5 Mbps target&lt;/span&gt;
  &lt;span class="na"&gt;framerate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;hardwareAcceleration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prefer-hardware&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Benchmark: Cloud Compressor vs. WebCodecs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Commercial Cloud SaaS&lt;/th&gt;
&lt;th&gt;SolveMyMedia WebCodecs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Max File Size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;500 MB (Requires Pro for 4K)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Unlimited (Hardware bound)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Network Upload&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2.4 GB sent over Wi-Fi&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0 MB (Air-gapped)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Processing Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;14 minutes (Upload + Cloud Queue)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;42 seconds (Local GPU)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subscription Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$19.99 / mo&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.00 Free &amp;amp; Open&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Try it live:&lt;br&gt;
👉 &lt;strong&gt;Compressor:&lt;/strong&gt; &lt;a href="https://solvemymedia.com/compress-video" rel="noopener noreferrer"&gt;https://solvemymedia.com/compress-video&lt;/a&gt;&lt;br&gt;
👉 &lt;strong&gt;Complete Suite:&lt;/strong&gt; &lt;a href="https://solvemymedia.com" rel="noopener noreferrer"&gt;https://solvemymedia.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What has your experience been with WebCodecs support across Chromium and Safari? Share your insights below! 👇&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>webassembly</category>
      <category>showdev</category>
    </item>
    <item>
      <title>How We Built an In-Browser 0.4s Lossless Video Trimmer with WebAssembly (and Zero Backend Servers)</title>
      <dc:creator>thisran</dc:creator>
      <pubDate>Fri, 09 Oct 2026 10:26:50 +0000</pubDate>
      <link>https://dev.to/khaithisran/how-we-built-an-in-browser-04s-lossless-video-trimmer-with-webassembly-and-zero-backend-servers-31jg</link>
      <guid>https://dev.to/khaithisran/how-we-built-an-in-browser-04s-lossless-video-trimmer-with-webassembly-and-zero-backend-servers-31jg</guid>
      <description>&lt;p&gt;Modern web video tools have a glaring architectural flaw: &lt;strong&gt;we are still transferring hundreds of megabytes over the public internet to perform basic byte manipulation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you want to trim 3 seconds off an MP4 video clip today, the typical "online video editor" forces you through a cloud queue and slaps on a watermark.&lt;/p&gt;

&lt;p&gt;Slicing an MP4 file doesn't require cloud server farms—it literally just requires moving bitstream pointers in your browser's local RAM.&lt;/p&gt;

&lt;p&gt;So we built &lt;strong&gt;&lt;a href="https://solvemymedia.com/cut-video" rel="noopener noreferrer"&gt;SolveMyMedia&lt;/a&gt;&lt;/strong&gt;: a 100% in-browser, client-side video workstation powered by WebAssembly.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Architecture: Stream Copying via WebAssembly
&lt;/h3&gt;

&lt;p&gt;Re-encoding video in the browser CPU is slow and drains battery. The secret to instantaneous 0.4-second trimming is &lt;strong&gt;Lossless Stream Demuxing&lt;/strong&gt; (&lt;code&gt;-c copy&lt;/code&gt;). Instead of decoding every pixel frame, we copy the compressed H.264 / AAC bitstream directly between container boundaries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createFFmpeg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fetchFile&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ffmpeg/ffmpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ffmpeg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createFFmpeg&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;trimVideoInMemory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;startSeconds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;durationSeconds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ffmpeg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isLoaded&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ffmpeg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;ffmpeg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;writeFile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input.mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ffmpeg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-ss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;startSeconds&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-i&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input.mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-t&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;durationSeconds&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-c&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;copy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-avoid_negative_ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;make_zero&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;output.mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ffmpeg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;readFile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;output.mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video/mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Performance Benchmark:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;AWS Lambda + S3 Pipeline&lt;/th&gt;
&lt;th&gt;SolveMyMedia (In-Browser WASM)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Upload Transfer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;240 MB uploaded over HTTP&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0 MB (Air-gapped)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Processing Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;22.8 seconds (Upload + Queue)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.38 seconds (RAM)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Server Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~$0.0004 / execution + storage&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.00 (Zero Server Infrastructure)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Privacy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stored on third-party cloud disk&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Never touches a network socket&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Try it yourself:&lt;br&gt;
👉 &lt;strong&gt;Live Tool:&lt;/strong&gt; &lt;a href="https://solvemymedia.com/cut-video" rel="noopener noreferrer"&gt;https://solvemymedia.com/cut-video&lt;/a&gt;&lt;br&gt;&lt;br&gt;
👉 &lt;strong&gt;Full Media Suite:&lt;/strong&gt; &lt;a href="https://solvemymedia.com" rel="noopener noreferrer"&gt;https://solvemymedia.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>webassembly</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Why We Ditched the 7-Microservices Nightmare for a Unified Life OS: Architecture Lessons</title>
      <dc:creator>thisran</dc:creator>
      <pubDate>Thu, 08 Oct 2026 13:20:51 +0000</pubDate>
      <link>https://dev.to/khaithisran/why-we-ditched-the-7-microservices-nightmare-for-a-unified-life-os-architecture-lessons-3cjc</link>
      <guid>https://dev.to/khaithisran/why-we-ditched-the-7-microservices-nightmare-for-a-unified-life-os-architecture-lessons-3cjc</guid>
      <description>&lt;p&gt;The modern developer productivity stack is fundamentally broken:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One app for tasks ($10/mo)&lt;/li&gt;
&lt;li&gt;One app for calendar scheduling ($15/mo)&lt;/li&gt;
&lt;li&gt;One app for habit tracking ($8/mo)&lt;/li&gt;
&lt;li&gt;One app for personal finance &amp;amp; budget tracking ($14/mo)&lt;/li&gt;
&lt;li&gt;One app for journaling &amp;amp; notes ($12/mo)&lt;/li&gt;
&lt;li&gt;Forty-two browser tabs open simultaneously, draining 3.2 GB of RAM.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of keeping us organized, we have become full-time integration technicians managing fragile webhooks, broken syncs, and recurring subscription bills.&lt;/p&gt;

&lt;p&gt;We built &lt;strong&gt;&lt;a href="https://tranvas.com" rel="noopener noreferrer"&gt;Tranvas&lt;/a&gt;&lt;/strong&gt; to solve this fragmentation by creating a &lt;strong&gt;Unified Life Operating System&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Architecture: Unified Edge Graph
&lt;/h3&gt;

&lt;p&gt;Instead of synchronizing 5 disparate microservices, Tranvas consolidates daily planning, habit loops, personal finance ledgering, and goal milestones into a single high-performance edge platform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;LifeOSState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;habits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;HabitNode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;financeLedger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TransactionEntry&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;dailyPlanner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TimeBlock&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;goals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MilestoneTree&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Client-First Local Sync with Optimistic Edge Updates&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;syncStateOptimistic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LifeOSState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;localStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Instant sub-millisecond local response&lt;/span&gt;
  &lt;span class="nx"&gt;backgroundSyncQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Dispatched in idle frame via requestIdleCallback&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Technical Highlights:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 15 &amp;amp; Server Components:&lt;/strong&gt; Blazing-fast initial page loads with minimal client-side JavaScript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local-First Caching:&lt;/strong&gt; Sub-millisecond UI updates with background edge sync.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero SaaS Bloat:&lt;/strong&gt; No pitch decks, no aggressive enterprise demo calls. Just an ironclad system for running your daily life.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out the live platform:&lt;br&gt;
👉 &lt;strong&gt;Tranvas:&lt;/strong&gt; &lt;a href="https://tranvas.com" rel="noopener noreferrer"&gt;https://tranvas.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How do you manage your personal engineering workflow? Do you prefer specialized single-purpose apps or an all-in-one unified dashboard? Let's talk in the comments!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>architecture</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Running Local AI Speech-to-Text in Browser CPU with WebAssembly: Zero Server Uploads</title>
      <dc:creator>thisran</dc:creator>
      <pubDate>Thu, 08 Oct 2026 12:09:50 +0000</pubDate>
      <link>https://dev.to/khaithisran/running-local-ai-speech-to-text-in-browser-cpu-with-webassembly-zero-server-uploads-55n9</link>
      <guid>https://dev.to/khaithisran/running-local-ai-speech-to-text-in-browser-cpu-with-webassembly-zero-server-uploads-55n9</guid>
      <description>&lt;p&gt;Beaming confidential client interviews, confidential board meetings, or unreleased podcast recordings to remote cloud speech-to-text APIs is a massive privacy risk.&lt;/p&gt;

&lt;p&gt;Every major "AI Transcription" startup asks you to upload your audio files to their cloud S3 buckets. Once uploaded, your voice data sits on remote servers subject to data leaks or model retraining.&lt;/p&gt;

&lt;p&gt;With modern &lt;strong&gt;WebAssembly SIMD&lt;/strong&gt; and &lt;strong&gt;ONNX Runtime Web&lt;/strong&gt;, we can execute quantized Whisper transformer models &lt;strong&gt;100% locally inside your browser tab&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's why we created &lt;strong&gt;&lt;a href="https://solvemymedia.com/transcribe" rel="noopener noreferrer"&gt;SolveMyMedia Transcribe&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  In-Browser Neural AI via Transformers.js
&lt;/h3&gt;

&lt;p&gt;Audio is decoded into 16kHz float buffers in browser memory and passed directly to the local model runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pipeline&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@xenova/transformers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;transcribeLocalAudio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audioBlob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Model weights cached in IndexedDB after initial load&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transcriber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;automatic-speech-recognition&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Xenova/whisper-tiny.en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;device&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webgpu&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arrayBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;audioBlob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;transcriber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;chunk_length_s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;stride_length_s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 100% private, 0 bytes leave your machine&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Benchmark: Cloud Speech API vs. Local WASM
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Cloud Speech API (OpenAI / Rev)&lt;/th&gt;
&lt;th&gt;SolveMyMedia Local AI Transcribe&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Voice Privacy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stored on external cloud servers&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Air-gapped in client RAM&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Costs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$0.006 per minute&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.00 Unlimited&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Offline Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Fails without internet&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅ 100% Works in Airplane Mode&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Test it out:&lt;br&gt;
👉 &lt;strong&gt;Local AI Transcribe:&lt;/strong&gt; &lt;a href="https://solvemymedia.com/transcribe" rel="noopener noreferrer"&gt;https://solvemymedia.com/transcribe&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have you experimented with client-side AI inference in production? Let's discuss in the comments!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webassembly</category>
      <category>privacy</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Zero-Upload Image Processing: Compressing, Cropping, and Removing Backgrounds in Browser RAM</title>
      <dc:creator>thisran</dc:creator>
      <pubDate>Thu, 08 Oct 2026 05:55:53 +0000</pubDate>
      <link>https://dev.to/khaithisran/zero-upload-image-processing-compressing-cropping-and-removing-backgrounds-in-browser-ram-2d9n</link>
      <guid>https://dev.to/khaithisran/zero-upload-image-processing-compressing-cropping-and-removing-backgrounds-in-browser-ram-2d9n</guid>
      <description>&lt;p&gt;Why are we sending confidential family photos, passport scans, and client assets across oceans just to delete white pixels?&lt;/p&gt;

&lt;p&gt;Most "online image converters" are subscription traps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Upload your image to our AI engine..."&lt;/li&gt;
&lt;li&gt;You wait 30 seconds for a fake loading bar.&lt;/li&gt;
&lt;li&gt;"You have used 1 of 1 free monthly credits! Upgrade to Pro for $19.99/mo to download high-res."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Basic image manipulation—segmentation, canvas cropping, compression, and WebP encoding—can execute in &lt;strong&gt;sandboxed browser memory&lt;/strong&gt; at 60 frames per second using modern WebAssembly and Canvas APIs.&lt;/p&gt;

&lt;p&gt;That's why we created &lt;strong&gt;&lt;a href="https://helpmyimg.com" rel="noopener noreferrer"&gt;HelpMyIMG&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Parallel Client-Side Processing with OffscreenCanvas
&lt;/h3&gt;

&lt;p&gt;By offloading pixel manipulation to an &lt;code&gt;OffscreenCanvas&lt;/code&gt; inside a background Web Worker, the main thread remains butter-smooth and responsive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Client-side WebP compression inside a Web Worker&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;compressImageWorker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageBitmap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quality&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.82&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OffscreenCanvas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageBitmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;imageBitmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;willReadFrequently&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageBitmap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Native hardware-accelerated WebP encoding&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;convertToBlob&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/webp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;quality&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 70-85% size reduction in ~40ms with zero network I/O&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Benchmark: Cloud API vs. In-Browser WASM
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Commercial Cloud Image API&lt;/th&gt;
&lt;th&gt;HelpMyIMG In-Browser WASM&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Rate Limits&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;50 credits/month free tier&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Unlimited (Hardware bound)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Network Payload&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;12 MB photo uploaded to cloud&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0 KB (Air-gapped)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Processing Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4.2 seconds (HTTP round-trip)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;120 ms (Local SIMD WASM)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subscription Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$19.90 / mo&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.00 Free &amp;amp; Open&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Try it live:&lt;br&gt;
👉 &lt;strong&gt;Image Suite:&lt;/strong&gt; &lt;a href="https://helpmyimg.com" rel="noopener noreferrer"&gt;https://helpmyimg.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What's your preferred stack for browser-side image processing? Drop your comments below!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>webassembly</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Compiling FFmpeg to WebAssembly: Building a Zero-Backend Video &amp; Audio Converter</title>
      <dc:creator>thisran</dc:creator>
      <pubDate>Thu, 08 Oct 2026 00:09:32 +0000</pubDate>
      <link>https://dev.to/khaithisran/compiling-ffmpeg-to-webassembly-building-a-zero-backend-video-audio-converter-4e70</link>
      <guid>https://dev.to/khaithisran/compiling-ffmpeg-to-webassembly-building-a-zero-backend-video-audio-converter-4e70</guid>
      <description>&lt;p&gt;Converting MOV to MP4 or extracting audio from video has historically required either:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Downloading sketchy desktop software bundled with adware.&lt;/li&gt;
&lt;li&gt;Uploading files to cloud conversion websites with 100MB limits and queue timers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By compiling FFmpeg C code to WebAssembly with Emscripten, the entire transcoding pipeline runs directly inside the client's sandboxed browser process.&lt;/p&gt;

&lt;p&gt;We built the In-Browser Converter on &lt;strong&gt;&lt;a href="https://solvemymedia.com/convert-video" rel="noopener noreferrer"&gt;SolveMyMedia&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  In-Memory Media Transcode via FFmpeg WASM
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createFFmpeg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fetchFile&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ffmpeg/ffmpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ffmpeg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createFFmpeg&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;convertMediaInMemory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetFormat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ffmpeg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isLoaded&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ffmpeg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;ffmpeg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;writeFile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input_raw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ffmpeg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-i&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input_raw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-c:v&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;libx264&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-preset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ultrafast&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`output.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;targetFormat&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ffmpeg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;readFile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`output.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;targetFormat&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Features:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Convert MOV, MKV, AVI, WebM to MP4.&lt;/li&gt;
&lt;li&gt;Extract MP3 and WAV audio streams in 0.3 seconds.&lt;/li&gt;
&lt;li&gt;100% client-side privacy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test it live:&lt;br&gt;
👉 &lt;strong&gt;Converter:&lt;/strong&gt; &lt;a href="https://solvemymedia.com/convert-video" rel="noopener noreferrer"&gt;https://solvemymedia.com/convert-video&lt;/a&gt;&lt;br&gt;
👉 &lt;strong&gt;Extract Audio:&lt;/strong&gt; &lt;a href="https://solvemymedia.com/video-to-audio" rel="noopener noreferrer"&gt;https://solvemymedia.com/video-to-audio&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webassembly</category>
      <category>javascript</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Why Are SaaS Startups Charging $40/Mo for 1994 High School Math? Inside In-Browser QR Generation</title>
      <dc:creator>thisran</dc:creator>
      <pubDate>Wed, 07 Oct 2026 14:28:48 +0000</pubDate>
      <link>https://dev.to/khaithisran/why-are-saas-startups-charging-40mo-for-1994-high-school-math-inside-in-browser-qr-generation-5fkh</link>
      <guid>https://dev.to/khaithisran/why-are-saas-startups-charging-40mo-for-1994-high-school-math-inside-in-browser-qr-generation-5fkh</guid>
      <description>&lt;p&gt;If you search Google for a "free QR code generator," you will witness one of the most predatory business models on the modern internet:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A user generates a QR code for a cafe menu or business card.&lt;/li&gt;
&lt;li&gt;The website silently encodes a redirect URL pointing to their own server instead of the actual destination.&lt;/li&gt;
&lt;li&gt;14 days later, the destination link expires, displaying a giant ransom screen: &lt;em&gt;"Your dynamic QR code has expired! Upgrade to our Pro Plan for $45/month to unfreeze it."&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;A QR code is not a recurring cloud service.&lt;/strong&gt; It is &lt;strong&gt;ISO/IEC 18004&lt;/strong&gt;, a public-domain mathematical specification published in 1994. It is literally just basic geometry and Reed-Solomon Galois Field error correction.&lt;/p&gt;

&lt;p&gt;Paying monthly rent on a high school math equation is absurd.&lt;/p&gt;

&lt;p&gt;So we built &lt;strong&gt;&lt;a href="https://createmy-qr.com" rel="noopener noreferrer"&gt;CreateMyQR&lt;/a&gt;&lt;/strong&gt;: 37 client-side QR and barcode utilities running 100% in your browser's local JavaScript thread.&lt;/p&gt;




&lt;h3&gt;
  
  
  How Reed-Solomon Math Runs Client-Side
&lt;/h3&gt;

&lt;p&gt;A static QR code embeds raw data directly into the binary matrix pattern. There are zero servers involved, zero redirects, and zero expiration dates.&lt;/p&gt;

&lt;p&gt;Here is how we convert the boolean matrix into an infinite-resolution vector SVG path in pure client-side JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderQRToVectorSVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;matrix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cellSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;matrix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;pathD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Construct a single SVG compound path string for optimal DOM performance&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;matrix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;matrix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;matrix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;pathD&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`M&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;cellSize&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;cellSize&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;h&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cellSize&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;v&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cellSize&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;h-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cellSize&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;z `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" width="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" height="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;
    &amp;lt;path fill="#000000" d="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pathD&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" /&amp;gt;
  &amp;lt;/svg&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  The Feature Set
&lt;/h3&gt;

&lt;p&gt;Instead of running 20 microservices, the entire suite executes client-side:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;37 QR &amp;amp; Barcode Formats:&lt;/strong&gt; WiFi auto-connect (WPA3), vCard contact cards, WhatsApp message triggers, EAN-13, and Code-128 barcodes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infinite Vector SVG Export:&lt;/strong&gt; Export crisp vector paths suitable for large billboard printing without pixelation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;100% Privacy:&lt;/strong&gt; No telemetry, no link redirection, no cloud tracking.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out the live tools:&lt;br&gt;
👉 &lt;strong&gt;CreateMyQR:&lt;/strong&gt; &lt;a href="https://createmy-qr.com" rel="noopener noreferrer"&gt;https://createmy-qr.com&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Community Discussion:
&lt;/h3&gt;

&lt;p&gt;What other common SaaS utilities do you think should be returned to client-side computing? Share your thoughts below!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Stop Paying $240/yr to Rotate a PDF: Building an In-Browser Document Workstation with WASM</title>
      <dc:creator>thisran</dc:creator>
      <pubDate>Wed, 07 Oct 2026 07:09:38 +0000</pubDate>
      <link>https://dev.to/khaithisran/stop-paying-240yr-to-rotate-a-pdf-building-an-in-browser-document-workstation-with-wasm-4el</link>
      <guid>https://dev.to/khaithisran/stop-paying-240yr-to-rotate-a-pdf-building-an-in-browser-document-workstation-with-wasm-4el</guid>
      <description>&lt;p&gt;Every software engineer has experienced the indignity of needing to sign an NDA, rotate a tenancy agreement, or compress a 50MB PDF—only to hit an Adobe or DocuSign paywall:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adobe: &lt;strong&gt;$239.88 / year&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;DocuSign: &lt;strong&gt;$480 / year&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Random online PDF tools: &lt;em&gt;"Upload your confidential tax audits to our unencrypted AWS S3 bucket in Northern Virginia. Don't worry, we promise to delete it in 2 hours ;)"&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Manipulating PDF byte trees, adding digital signature layers, and extracting text via OCR does not require a remote server infrastructure.&lt;/p&gt;

&lt;p&gt;We built &lt;strong&gt;&lt;a href="https://handlemyfile.com" rel="noopener noreferrer"&gt;HandleMyFile&lt;/a&gt;&lt;/strong&gt; as a 100% in-browser, client-side document workstation.&lt;/p&gt;




&lt;h3&gt;
  
  
  Multi-Threaded PDF Manipulation in Browser Memory
&lt;/h3&gt;

&lt;p&gt;Using &lt;code&gt;pdf-lib&lt;/code&gt; compiled to JavaScript and running inside Web Workers, we can merge, split, rotate, and compress multi-page documents without transferring a single byte over the network:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pdf-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;mergePDFsLocally&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pdfArrayBuffers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mergedPdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;pdfArrayBuffers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ignoreEncryption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;copiedPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mergedPdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copyPages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPageIndices&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nx"&gt;copiedPages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;mergedPdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pdfBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mergedPdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;pdfBytes&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/pdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Key Capabilities:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In-RAM PDF Compression:&lt;/strong&gt; Reduces bloated scans from 80MB to under 4MB for email attachments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client-Side E-Signatures:&lt;/strong&gt; Draw digital signatures on contracts without recurring DocuSign fees.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WASM OCR:&lt;/strong&gt; Extract text from blurry scanned documents locally via Tesseract WebAssembly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Air-Gapped Privacy:&lt;/strong&gt; Complete confidentiality for medical, legal, and financial records.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test the workstation:&lt;br&gt;
👉 &lt;strong&gt;HandleMyFile:&lt;/strong&gt; &lt;a href="https://handlemyfile.com" rel="noopener noreferrer"&gt;https://handlemyfile.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How do you handle document security and PDF workflows in your engineering team? Let's discuss!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webassembly</category>
      <category>privacy</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Compressing 4K Video in the Browser: Replacing $20/mo Cloud SaaS with WebCodecs &amp; WebGPU</title>
      <dc:creator>thisran</dc:creator>
      <pubDate>Tue, 06 Oct 2026 19:30:58 +0000</pubDate>
      <link>https://dev.to/khaithisran/compressing-4k-video-in-the-browser-replacing-20mo-cloud-saas-with-webcodecs-webgpu-4od9</link>
      <guid>https://dev.to/khaithisran/compressing-4k-video-in-the-browser-replacing-20mo-cloud-saas-with-webcodecs-webgpu-4od9</guid>
      <description>&lt;p&gt;Cloud video compressors are one of the most frustrating traps on the web:&lt;br&gt;
"Your video exceeds the 500MB free upload limit. Please upgrade to Pro for $19.99/mo to continue."&lt;/p&gt;

&lt;p&gt;Why are developers transferring gigabytes of raw 4K video over the internet just to re-encode H.264 / AV1 frames?&lt;br&gt;
Modern laptops have multi-core processors with dedicated hardware video encoders. With the standardized &lt;strong&gt;WebCodecs API&lt;/strong&gt; and &lt;strong&gt;WebGPU&lt;/strong&gt;, the browser can access native hardware acceleration to compress gigabytes of video locally in device RAM.&lt;/p&gt;

&lt;p&gt;We built the In-Browser Video Compressor on &lt;strong&gt;&lt;a href="https://solvemymedia.com/compress-video" rel="noopener noreferrer"&gt;SolveMyMedia&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;


&lt;h3&gt;
  
  
  Hardware-Accelerated Compression via WebCodecs
&lt;/h3&gt;

&lt;p&gt;Instead of running slow software transcoders on a single thread, WebCodecs exposes low-level access to the GPU hardware:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VideoEncoder&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;muxer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addVideoChunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Encoding error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;codec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;avc1.640028&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// H.264 High Profile&lt;/span&gt;
  &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1920&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;bitrate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="nx"&gt;_500_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 4.5 Mbps target&lt;/span&gt;
  &lt;span class="na"&gt;framerate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;hardwareAcceleration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prefer-hardware&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Benchmark: Cloud Compressor vs. WebCodecs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Commercial Cloud SaaS&lt;/th&gt;
&lt;th&gt;SolveMyMedia WebCodecs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Max File Size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;500 MB (Requires Pro for 4K)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Unlimited (Hardware bound)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Network Upload&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2.4 GB sent over Wi-Fi&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0 MB (Air-gapped)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Processing Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;14 minutes (Upload + Cloud Queue)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;42 seconds (Local GPU)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subscription Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$19.99 / mo&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.00 Free &amp;amp; Open&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Try it live:&lt;br&gt;
👉 &lt;strong&gt;Compressor:&lt;/strong&gt; &lt;a href="https://solvemymedia.com/compress-video" rel="noopener noreferrer"&gt;https://solvemymedia.com/compress-video&lt;/a&gt;&lt;br&gt;
👉 &lt;strong&gt;Complete Suite:&lt;/strong&gt; &lt;a href="https://solvemymedia.com" rel="noopener noreferrer"&gt;https://solvemymedia.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What has your experience been with WebCodecs support across Chromium and Safari? Share your insights below! 👇&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>webassembly</category>
      <category>showdev</category>
    </item>
    <item>
      <title>How We Built an In-Browser 0.4s Lossless Video Trimmer with WebAssembly (and Zero Backend Servers)</title>
      <dc:creator>thisran</dc:creator>
      <pubDate>Mon, 05 Oct 2026 14:27:03 +0000</pubDate>
      <link>https://dev.to/khaithisran/how-we-built-an-in-browser-04s-lossless-video-trimmer-with-webassembly-and-zero-backend-servers-4edd</link>
      <guid>https://dev.to/khaithisran/how-we-built-an-in-browser-04s-lossless-video-trimmer-with-webassembly-and-zero-backend-servers-4edd</guid>
      <description>&lt;p&gt;Modern web video tools have a glaring architectural flaw: &lt;strong&gt;we are still transferring hundreds of megabytes over the public internet to perform basic byte manipulation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you want to trim 3 seconds off an MP4 video clip today, the typical "online video editor" forces you through this gauntlet:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Upload your 300MB video across transoceanic undersea cables to an AWS S3 bucket.&lt;/li&gt;
&lt;li&gt;Wait in a queue while a remote Docker container spins up.&lt;/li&gt;
&lt;li&gt;The server runs &lt;code&gt;ffmpeg -ss ... -to ...&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The server renders an export, slaps on a watermark, and demands $29.99/mo to download.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your local laptop or smartphone contains a multi-core processor running billions of operations per second. Slicing an MP4 file doesn't require cloud server farms—it literally just requires moving bitstream pointers in your browser's local RAM.&lt;/p&gt;

&lt;p&gt;So we built &lt;strong&gt;&lt;a href="https://solvemymedia.com/cut-video" rel="noopener noreferrer"&gt;SolveMyMedia&lt;/a&gt;&lt;/strong&gt;: a 100% in-browser, client-side video workstation powered by WebAssembly.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Architecture: Stream Copying via WebAssembly
&lt;/h3&gt;

&lt;p&gt;Re-encoding video in the browser CPU is slow and drains battery. The secret to instantaneous 0.4-second trimming is &lt;strong&gt;Lossless Stream Demuxing&lt;/strong&gt; (&lt;code&gt;-c copy&lt;/code&gt;). Instead of decoding every pixel frame, we copy the compressed H.264 / AAC bitstream directly between container boundaries.&lt;/p&gt;

&lt;p&gt;Here is the simplified implementation running inside device RAM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createFFmpeg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fetchFile&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ffmpeg/ffmpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ffmpeg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createFFmpeg&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;trimVideoInMemory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;startSeconds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;durationSeconds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ffmpeg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isLoaded&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ffmpeg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// 1. Mount video file into WASM virtual filesystem (MEMFS)&lt;/span&gt;
  &lt;span class="nx"&gt;ffmpeg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;writeFile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input.mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Stream copy mode: instantaneous demuxing without re-encoding&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ffmpeg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-ss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;startSeconds&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-i&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input.mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-t&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;durationSeconds&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-c&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;copy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-avoid_negative_ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;make_zero&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;output.mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Read output bytes directly from browser RAM&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ffmpeg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;readFile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;output.mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video/mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Performance Benchmark: Cloud vs. Client WASM
&lt;/h3&gt;

&lt;p&gt;We benchmarked trimming a 240MB 4K MP4 clip across a standard 50 Mbps home broadband connection:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;AWS Lambda + S3 Pipeline&lt;/th&gt;
&lt;th&gt;SolveMyMedia (In-Browser WASM)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Upload Transfer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;240 MB uploaded over HTTP&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0 MB (Air-gapped)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Processing Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;22.8 seconds (Upload + Queue)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.38 seconds (RAM)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Server Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~$0.0004 / execution + storage&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.00 (Zero Server Infrastructure)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Privacy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stored on third-party cloud disk&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Never touches a network socket&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  The Air-Gap Test
&lt;/h3&gt;

&lt;p&gt;Because computation happens entirely within the browser's sandboxed WebAssembly runtime, &lt;strong&gt;you can disconnect your Wi-Fi or turn on Airplane Mode, and the video trimmer still works flawlessly.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Try it yourself:&lt;br&gt;
👉 &lt;strong&gt;Live Tool:&lt;/strong&gt; &lt;a href="https://solvemymedia.com/cut-video" rel="noopener noreferrer"&gt;https://solvemymedia.com/cut-video&lt;/a&gt;&lt;br&gt;&lt;br&gt;
👉 &lt;strong&gt;Full Media Suite:&lt;/strong&gt; &lt;a href="https://solvemymedia.com" rel="noopener noreferrer"&gt;https://solvemymedia.com&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Questions for the Community:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Have you experimented with compiling existing C/C++ audio/video libraries to WASM?&lt;/li&gt;
&lt;li&gt;How do you balance client memory constraints when dealing with large 4K media in the browser?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's discuss in the comments below! 👇&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>webassembly</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
