<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged webassembly</title>
<link>https://coderlegion.com/tag/webassembly</link>
<description>Powered by Question2Answer</description>
<item>
<title>I Built a Browser-Based GIF Compressor That Keeps Your Files on Your Device</title>
<link>https://coderlegion.com/33069/i-built-a-browser-based-gif-compressor-that-keeps-your-files-on-your-device</link>
<description>&lt;p&gt;Animated GIFs are easy to share, but their file sizes can get out of hand quickly. A few seconds of animation may be too large for a chat message, a community post, or a website. The usual fix is to upload the file to an online converter, wait for a server to process it, and download the result. I wanted a simpler workflow that does not require sending the original GIF to a server.&lt;/p&gt;
&lt;p&gt;That is why I built GIF Compressor (&lt;a rel=&quot;nofollow&quot; href=&quot;https://gifcompressor.cc/&quot;&gt;https://gifcompressor.cc/&lt;/a&gt;), a free tool that processes animated GIFs locally in the browser.&lt;/p&gt;
&lt;p&gt;What it does&lt;/p&gt;
&lt;p&gt;You can drag in a GIF, choose a compression preset, and compare the original and compressed file sizes before downloading. The presets favor image quality, a balance between quality and size, or the smallest file. If you need more control, you can adjust compression strength, image dimensions, palette colors, and optimization level.&lt;/p&gt;
&lt;p&gt;The tool also supports batches of up to 20 GIFs, with a 100 MB limit per file. The same settings can be applied across a batch, which is useful when preparing reaction GIFs, documentation assets, or animations for a website.&lt;/p&gt;
&lt;p&gt;For specific upload limits, I added pages that attempt to reduce a GIF toward 10 MB or 1 MB. These use progressively stronger settings and show whether the target was actually reached; no compression tool can guarantee a particular size for every source GIF without potentially unacceptable quality loss.&lt;/p&gt;
&lt;p&gt;Why browser-side processing?&lt;/p&gt;
&lt;p&gt;The compression engine runs through WebAssembly in your browser. The GIF stays on your device while it is processed. Besides the privacy benefit, this removes the upload-and-wait step, especially noticeable for larger files or slower connections.&lt;/p&gt;
&lt;p&gt;There is still a trade-off: GIF compression is not magic. Reducing colors or dimensions can make a file much smaller, but it can also change how the animation looks. I recommend starting with the quality-focused preset, previewing the result, and only increasing compression when the file is still too large.&lt;/p&gt;
&lt;p&gt;Try it&lt;/p&gt;
&lt;p&gt;GIF Compressor is available at &lt;a rel=&quot;nofollow&quot; href=&quot;https://gifcompressor.cc/&quot;&gt;https://gifcompressor.cc/&lt;/a&gt;. It is free to use, and there is no account needed for the compression workflow. I would appreciate feedback from other developers and creators: which GIF-size limits or batch workflows are most frustrating for you today?&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/33069/i-built-a-browser-based-gif-compressor-that-keeps-your-files-on-your-device</guid>
<pubDate>Fri, 30 Oct 2026 09:51:36 +0000</pubDate>
</item>
<item>
<title>RetroPlay: a browser hub for classic games, running entirely on WebAssembly</title>
<link>https://coderlegion.com/26609/retroplay-a-browser-hub-for-classic-games-running-entirely-on-webassembly</link>
<description>&lt;pre&gt;&lt;code&gt;I've been putting together a side project for the last while and finally feel okay showing it around properly: RetroPlay, a browser hub for old-school games that all run through WebAssembly. No installers, no accounts, no &quot;download our launcher first&quot; — you open a page and you're already playing.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Repo: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/MahanKenway/RetroPlay&quot;&gt;https://github.com/MahanKenway/RetroPlay&lt;/a&gt; Live: &lt;a rel=&quot;nofollow&quot; href=&quot;https://mahankenway.github.io/RetroPlay/&quot;&gt;https://mahankenway.github.io/RetroPlay/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;How it actually started&lt;/p&gt;
&lt;p&gt;&lt;em&gt;It began as basically a Doom WAD launcher&lt;/em&gt;  one of those &quot;let's see if I can get Doom running in a browser tab&quot; weekend experiments that a lot of people have tried at some point. Mine actually worked well enough that I didn't stop after the weekend. I kept poking at it, then kept adding to it, and at some point it quietly stopped being &quot;a Doom thing&quot; and turned into something bigger: a small curated collection of separate game runtimes sitting behind one shared hub.&lt;/p&gt;
&lt;p&gt;Every one of those runtimes is its own beast. Different engine, different save system, different input model, different everything under the hood. They just happen to live under the same roof and share the same visual shell, so from the outside it feels like one coherent product instead of eight unrelated experiments duct-taped together.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What's actually playable right now&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is the part I care about the most, honestly, because I got tired of retro gaming sites that advertise ten consoles and a thousand games and then, the second you click play, ask you to upload a ROM file you obviously don't own and never will. That model always felt dishonest to me. So the rule I set for RetroPlay from day one was simple: if a game is marked &quot;Ready&quot; in the catalog, it comes bundled with free, legally distributable content, and it just runs. No uploads, no accounts, no asking the player to go find something sketchy on the internet first.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Here's what's actually sitting in the Ready column right now:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Doom / Freedoom catalog — the original launcher that started this whole thing, still very much alive and still the entry point for a lot of people&lt;br&gt;
C-Dogs SDL — a top-down run-and-gun that's more fun than it has any right to be for something this old-school&lt;br&gt;
GNU FreeDink — the weird little action-adventure title that basically nobody remembers exists anymore, which is exactly why I wanted it in there&lt;br&gt;
OpenTyrian2000 — a vertical shooter that scratches a very specific itch if you grew up on that genre&lt;br&gt;
LibreQuake — a Quake-style first-person shooter running entirely on free data, no Quake WAD required&lt;br&gt;
OpenTTD — and yes, this means the full transport strategy experience, timetables and all, sitting in a browser tab&lt;br&gt;
FreeRCT — theme-park building in the classic RollerCoaster Tycoon spirit, without touching any actual RCT assets&lt;br&gt;
Neverball — 3D ball-rolling physics puzzles, and I want to stress this is real gameplay captured from the actual runtime, not some rendered concept screenshot pretending to be gameplay&lt;br&gt;
On top of that there are two entries I deliberately left visible instead of quietly hiding, because I'd rather be upfront about what's finished and what isn't. ECWolf is there as an engine-only host — it plays Wolfenstein-3D-engine games, but you have to point it at your own legally-owned game data, so it's not a one-click bundled experience. OpenResident is even more of a side experiment than that: it's basically a &quot;can WebGL2 and WebAssembly actually handle something in this ballpark&quot; technical probe, not a playable Resident Evil game, because the upstream engine it's built on doesn't ship with any actual game data to run. I could've just deleted both from the catalog to make the project look more polished, but that felt dishonest too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why WebAssembly, and why this isn't &quot;just another emulator site&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Most of what's in RetroPlay isn't emulated in the classic ROM-emulator sense at all. These are the actual open-source game engines  C-Dogs, FreeDink, OpenTTD, Neverball, and the rest — compiled with Emscripten directly to WebAssembly and dropped straight into the browser. That's a meaningfully different thing from running a ROM through an emulator core, and it's the whole reason licensing ended up being such a big part of how this project is structured. I only bring in engines and asset packs that are genuinely free to redistribute, whether that's Freedoom, OpenGFX/OpenSFX/OpenMSX for OpenTTD, or the clean-room content that ships with FreeRCT. Nothing commercial gets repackaged or smuggled in anywhere, on purpose.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How it's actually built, for anyone who cares about that side of it&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The hub itself is intentionally boring on the frontend side plain HTML, CSS and ES modules, no framework doing anything clever. Each game runtime lives on its own separate page and loads its own WASM build independently, so nothing steps on anything else's dependencies or global state. That separation was a deliberate choice; it made adding new runtimes way less risky than if everything shared one big bundle.&lt;/p&gt;
&lt;p&gt;On the infrastructure side, GitHub Actions handles the whole pipeline: it installs a pinned Emscripten SDK, compiles every engine module, copies the static frontend into a dist/ folder, and ships the finished artifact to GitHub Pages. There's no backend server involved at any point — this is a static site from top to bottom, which also means hosting costs are basically zero and there's nothing to keep patched or worry about going down.&lt;/p&gt;
&lt;p&gt;If you want to poke around locally without doing a full rebuild, it's genuinely just:&lt;/p&gt;
&lt;p&gt;git clone &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/MahanKenway/RetroPlay.git&quot;&gt;https://github.com/MahanKenway/RetroPlay.git&lt;/a&gt;&lt;br&gt;
cd RetroPlay&lt;br&gt;
python3 -m http.server 4180 --directory dist&lt;br&gt;
That spins up an already-built artifact. Actually rebuilding the engines from source is a bit more involved — you need a pinned Emscripten toolchain plus the per-game build scripts that live in scripts/, run in the same order CI uses so you're testing what actually ships.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Where this is going next&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I'm slowly adding more free-content runtimes whenever I come across an open-source engine that feels like it deserves a slot — I'm not trying to rush this into being some giant catalog overnight, quality and licensing sanity matter more to me than raw numbers. Touch controls are also an ongoing project on my end, because I want the mobile experience to actually feel good and playable, not just technically function if you squint at it on a small screen.&lt;/p&gt;
&lt;p&gt;If anyone here has built something similar, or knows of an open-source engine with genuinely free redistributable content that you think deserves a spot in the catalog, I'd honestly love to hear about it in the comments. That's a big part of why I'm posting this here instead of just letting it sit quietly in the repo where nobody stumbles across it.&lt;/p&gt;
&lt;p&gt;To be clear about the intent: this is a non-commercial, preservation-minded project. Nothing here is being sold, there's no paywall, no ads shoved in your face, no account wall. It's just old games and classic engines staying playable in a normal browser tab without a ton of friction getting in the way.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26609/retroplay-a-browser-hub-for-classic-games-running-entirely-on-webassembly</guid>
<pubDate>Wed, 09 Sep 2026 11:31:37 +0000</pubDate>
</item>
<item>
<title>When WebAssembly Is Worth The Build Step In A Web Game</title>
<link>https://coderlegion.com/25912/when-webassembly-is-worth-the-build-step-in-a-web-game</link>
<description>&lt;p&gt;Most of the wasm material floating around is about the toolchain: install Emscripten, pass these flags, ship the .wasm. The decision that comes before it gets far less attention, and for this crowd it is the more interesting one, so here is the version I would have wanted to read first.&lt;/p&gt;
&lt;h3 id=&quot;what-a-module-actually-is&quot;&gt;What A Module Actually Is&lt;/h3&gt;
&lt;p&gt;WebAssembly is a binary instruction format, not a language you write. You compile C, C++ or Rust into a .wasm file, the browser validates it and turns it into real machine code, and that compile step is where the speed comes from. There is no warm-up period while a JIT works out which functions are hot, so a wasm function runs at full speed from its first call.&lt;/p&gt;
&lt;p&gt;What surprises people is how little a module can do on its own. It has no DOM, no canvas, no network, no audio. It gets a block of linear memory, a set of exported functions JavaScript can call, and a set of imported functions JavaScript hands to it. Everything it does to the outside world crosses that boundary. Sandbox with a bridge is the mental model, and nearly every wasm performance surprise traces back to it.&lt;/p&gt;
&lt;h3 id=&quot;the-boundary-is-the-whole-design&quot;&gt;The Boundary Is The Whole Design&lt;/h3&gt;
&lt;p&gt;Calling into a wasm function is fast. It is not free. Cross the boundary a few thousand times a frame with tiny calls and you can spend the entire advantage you compiled for.&lt;/p&gt;
&lt;p&gt;The pattern that works is a coarse boundary. Pass data in batches, do as much work as possible inside the module before returning, and share state through linear memory instead of copying values back and forth. Rather than asking the module for each entity's position one call at a time, let it write every position into a shared buffer and read the whole buffer once on the JavaScript side.&lt;/p&gt;
&lt;p&gt;This is also why a wasm rewrite of ordinary game logic often benchmarks slower than the JavaScript it replaced. The logic got faster and the plumbing got more expensive, and the plumbing won.&lt;/p&gt;
&lt;h3 id=&quot;two-toolchains-cover-almost-everything&quot;&gt;Two Toolchains Cover Almost Everything&lt;/h3&gt;
&lt;p&gt;For C and C++ it is Emscripten, which does much more than emit a binary. It ships a C library, maps OpenGL onto WebGL, simulates a filesystem, and generates the glue that loads the module and wires it to the page. Point it at an existing SDL or OpenGL game and with the right flags you get something playable without rewriting the source.&lt;/p&gt;
&lt;p&gt;For Rust the support lives in the language ecosystem. wasm32-unknown-unknown is a first-class target, wasm-bindgen generates bindings with real types instead of raw numbers, and macroquad or Bevy sit on top depending on how ambitious the project is. Rust is what most people reach for when they are starting fresh rather than porting.&lt;/p&gt;
&lt;p&gt;C#, Go and AssemblyScript all target wasm too, but for games those two paths have the deepest ecosystems and the most shipped titles behind them.&lt;/p&gt;
&lt;h3 id=&quot;your-engine-already-made-this-choice&quot;&gt;Your Engine Already Made This Choice&lt;/h3&gt;
&lt;p&gt;If you export from Unity or Godot, you are already shipping wasm. Both compile their C++ runtime to a module and drive a WebGL canvas from it, which explains the complaint everyone has: a trivial Unity web build is many megabytes because the engine runtime is the download, not your game. The load time you see is the browser compiling that binary, and streaming compilation is what hides most of it behind the download.&lt;/p&gt;
&lt;p&gt;We put the longer version together on our own site, &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.abratabia.com/webassembly/&quot;&gt;WebAssembly for web games&lt;/a&gt;, which goes through the toolchains, how each engine handles its web export, and the things wasm still cannot do without JavaScript holding its hand.&lt;/p&gt;
&lt;p&gt;Reach for wasm when you have a hot loop running thousands of times per frame, a large existing native codebase, or a workload like physics or a software rasterizer where fixed types and explicit memory genuinely pay. Skip it when the honest answer is that your game logic runs fine in JavaScript. A build step you did not need is a real cost, and the speedup you imagined is often a rounding error.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25912/when-webassembly-is-worth-the-build-step-in-a-web-game</guid>
<pubDate>Mon, 31 Aug 2026 11:53:33 +0000</pubDate>
</item>
<item>
<title>Fix The Time Step Before You Blame The Physics Engine</title>
<link>https://coderlegion.com/25547/fix-the-time-step-before-you-blame-the-physics-engine</link>
<description>&lt;p&gt;A lot of the browser game questions that land on here come down to physics behaving one way on the dev machine and another way on someone else's, and the engine is almost never the reason. This is the version of the explanation I wish someone had given me early, and it applies the same whether you are on Rapier, Cannon-es or Havok.&lt;/p&gt;
&lt;h3 id=&quot;the-fixed-step-is-the-whole-trick&quot;&gt;The Fixed Step Is The Whole Trick&lt;/h3&gt;
&lt;p&gt;A physics engine advances the world one step at a time. Each step it applies forces, finds contacts, resolves them, and writes new positions and velocities. If you hand it the real elapsed frame time, that step is a different size on every machine, and the numerical integration that turns force into velocity into position produces a different answer each run. Determinism goes, and replays, lockstep multiplayer and reproducible bug reports go with it.&lt;/p&gt;
&lt;p&gt;Keep the step fixed, usually at 1/60th of a second, and decouple it from rendering. Accumulate real elapsed time in a buffer, run as many whole physics steps as fit in it, and leave the remainder for the next frame. When the renderer is faster than the physics rate, interpolate between the last two physics states so the motion still looks smooth. A slow frame then runs several small steps to catch up instead of one giant one, which is exactly what stops a fast projectile from tunneling straight through a thin wall.&lt;/p&gt;
&lt;h3 id=&quot;body-type-is-the-decision-people-get-wrong&quot;&gt;Body Type Is The Decision People Get Wrong&lt;/h3&gt;
&lt;p&gt;Every body is dynamic, kinematic or static. Dynamic bodies are moved by forces and pushed by collisions. Kinematic bodies are moved by your code, shove dynamic bodies out of the way and ignore forces entirely. Static bodies never move and cost the least.&lt;/p&gt;
&lt;p&gt;Picking wrong is the most common setup mistake I see. A moving platform written as a dynamic body drifts the moment the player lands on it. A player character written as dynamic gets shoved around by every crate it brushes. Most platforms want to be kinematic, most scenery wants to be static, and dynamic belongs to the objects you actually want the simulation to decide for you.&lt;/p&gt;
&lt;p&gt;The rest is a handful of properties: mass, moment of inertia, linear and angular velocity, restitution for bounce, friction for grip. Forces apply continuously, like gravity every step. Impulses are instantaneous, like the kick of a jump. Mix those two up and you get a character that either floats or launches into orbit.&lt;/p&gt;
&lt;h3 id=&quot;collision-shapes-are-approximations-on-purpose&quot;&gt;Collision Shapes Are Approximations On Purpose&lt;/h3&gt;
&lt;p&gt;The engine does not use your visual mesh. A 10,000 polygon character is far too expensive to test against every other body every step, so you swap it for a capsule, a box or a convex hull. That is a feature, not a compromise. A sphere against a sphere is one distance comparison. A convex hull pair needs GJK. A triangle mesh is the slowest of the lot, and it belongs on static terrain and almost nowhere else.&lt;/p&gt;
&lt;h3 id=&quot;what-to-reach-for&quot;&gt;What To Reach For&lt;/h3&gt;
&lt;p&gt;The engines split into pure JavaScript and WebAssembly. Cannon-es is the readable JS option from the pmndrs community, comfortable to roughly 500 to 1,000 active bodies at 60 fps, with use-cannon for React. Rapier is Rust compiled to WASM, SIMD accelerated and several times faster than its 2024 releases, with joints, character controllers and ray casting. Havok's free WebAssembly build ships as the default plugin in Babylon.js 6 and later and is dramatically faster than the old Ammo.js path.&lt;/p&gt;
&lt;p&gt;The part that matters more than the ranking is that a physics engine computes positions and rotations and draws nothing at all. Any of them pairs with any renderer, whether that is Three.js, Babylon.js, PixiJS or raw WebGL, and your code is the bridge that copies transforms across once per frame. The &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.abratabia.com/game-physics/&quot;&gt;full web physics engine comparison&lt;/a&gt; covers the 2D options and the older ports too.&lt;/p&gt;
&lt;h3 id=&quot;the-takeaway&quot;&gt;The Takeaway&lt;/h3&gt;
&lt;p&gt;Get the time step right before touching anything else. Fix the step, accumulate the remainder, interpolate for rendering, then choose body types deliberately and keep collision shapes simple. Do those three and the engine you pick becomes a performance decision instead of a correctness one.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25547/fix-the-time-step-before-you-blame-the-physics-engine</guid>
<pubDate>Thu, 27 Aug 2026 12:05:16 +0000</pubDate>
</item>
<item>
<title>Pixlane Media</title>
<link>https://coderlegion.com/22694/pixlane-media</link>
<description>&lt;p&gt;Hi Coder Legion,&lt;/p&gt;
&lt;p&gt;I'm the maker behind &lt;strong&gt;Pixlane Media&lt;/strong&gt;. I started it as a hobby project while teaching myself more C++ and WebAssembly. At first it was just a couple of utilities I personally needed; over time, those small experiments became a broader browser toolkit.&lt;/p&gt;
&lt;p&gt;The main design choice is &lt;strong&gt;local-first processing&lt;/strong&gt;. When browser support allows it, images and documents are handled on the device instead of being uploaded to another server. The current collection includes image compression and resizing, background removal, OCR and document scanning, PDF merge/compression, and several on-device AI experiments.&lt;/p&gt;
&lt;p&gt;It is currently free to use, with no account required for the tools. I am sharing it as a product I actively maintain, not as a finished answer. I would genuinely value practical feedback from people who build and use web tools:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Does the landing page explain the local-first idea quickly enough?&lt;/li&gt;
&lt;li&gt;Which tool feels immediately useful, and which one feels unnecessary?&lt;/li&gt;
&lt;li&gt;Is the mobile experience comfortable for real document or image work?&lt;/li&gt;
&lt;li&gt;Is there a browser-based media or PDF workflow you would expect but cannot find?&lt;/li&gt;
&lt;li&gt;If you notice a slowdown, which browser, device and tool were you using?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can try it here: &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://pixlane.media/&quot;&gt;pixlane.media&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Even a short “this part was clear / this part was confusing” reply would help me decide what to improve next. I am especially interested in feedback about privacy messaging, WebAssembly performance, and the balance between offering many tools and keeping the interface easy to scan.&lt;/p&gt;
&lt;p&gt;Thanks for taking a look.&lt;/p&gt;
</description>
<category>Launches</category>
<guid isPermaLink="true">https://coderlegion.com/22694/pixlane-media</guid>
<pubDate>Wed, 15 Jul 2026 02:42:30 +0000</pubDate>
</item>
<item>
<title>Pytml – Running Python in the Browser Without a Backend</title>
<link>https://coderlegion.com/21627/pytml-running-python-in-the-browser-without-a-backend</link>
<description>&lt;p&gt;Pytml is a lightweight (~15KB) JavaScript library that acts as a bridge between Python and HTML via WebAssembly. It allows developers to embed and execute Python code directly in HTML pages using a single  tag — no server, no complex JavaScript, no setup required.&lt;/p&gt;
&lt;p&gt;The library leverages Pyodide (CPython compiled to WebAssembly) to run Python code locally in the user's browser. It automatically handles print() and input() redirection to the DOM, making it perfect for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Interactive educational tutorials&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Classroom coding demos&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rapid prototyping of Python code in web pages&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pytml is open-source (Apache 2.0), listed on OpenForge.gov.in (India's official open-source platform), and has received a Zenodo DOI for academic citation.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;http://pytml.js.org&quot;&gt;Website&lt;/a&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;http://github.com/nodex-ar/pytml&quot;&gt;Github&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/21627/pytml-running-python-in-the-browser-without-a-backend</guid>
<pubDate>Tue, 30 Jun 2026 17:21:20 +0000</pubDate>
</item>
<item>
<title>How to Build AI Tools That Don't Leak Corporate Data (Using WebAssembly &amp; Next.js)</title>
<link>https://coderlegion.com/20403/how-to-build-ai-tools-that-dont-leak-corporate-data-using-webassembly-next-js</link>
<description>&lt;p&gt;The AI boom has created a massive problem for B2B startups: Data Leaks.&lt;/p&gt;
&lt;p&gt;Every day, startup founders upload unreleased pitch decks, and agencies upload NDA-protected Enterprise RFPs (Request for Proposals) to public AI chatbots to get quick summaries. What they don't realize is that by dragging and dropping a PDF into a standard cloud AI, they are feeding highly confidential intellectual property into remote servers.&lt;/p&gt;
&lt;p&gt;As developers, we can do better. We can build AI tools that give users the power of LLMs without compromising the privacy of their source documents.&lt;/p&gt;
&lt;p&gt;Recently, I expanded &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.pdfpro.co.in/&quot;&gt;PDF Pro AI&lt;/a&gt;—a local-first document workspace—with two new tools: an RFP &amp;amp; Pitch Deck Analyzer and an Insurance Policy Analyzer.&lt;/p&gt;
&lt;p&gt;Here is how I architected a 100% private AI extraction pipeline using WebAssembly, React, and Next.js, ensuring the user's PDF never leaves their computer.&lt;/p&gt;
&lt;p&gt;The Vulnerability in Standard AI PDF Tools&lt;br&gt;
Most &quot;Chat with PDF&quot; tools follow a dangerous architecture for enterprise data:&lt;/p&gt;
&lt;p&gt;User uploads Confidential_RFP.pdf.&lt;br&gt;
The file is saved to an AWS S3 bucket.&lt;br&gt;
A python backend reads the PDF and creates vector embeddings.&lt;br&gt;
The backend sends chunks to OpenAI/Anthropic.&lt;br&gt;
The risk is enormous. The file is sitting in cloud storage. If the database is breached, or the developer misconfigures their S3 bucket, corporate data is leaked.&lt;/p&gt;
&lt;p&gt;The Solution: WebAssembly (WASM) Text Extraction&lt;br&gt;
To fix this, we need to extract the text from the PDF before anything touches a network request.&lt;/p&gt;
&lt;p&gt;Enter WebAssembly. By compiling Mozilla's pdf.js into Wasm, we can run a high-performance PDF rendering and extraction engine directly inside the user's Chrome or Safari browser.&lt;/p&gt;
&lt;p&gt;Instead of uploading a file, the user simply drops the file into a React component. The file is loaded into their local RAM as an ArrayBuffer, parsed by WebAssembly, and the text strings are extracted.&lt;/p&gt;
&lt;p&gt;Here is what the local extraction function looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import * as pdfjsLib from 'pdfjs-dist';

// Point to the WebAssembly worker
pdfjsLib.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjsLib.version}/build/pdf.worker.min.mjs`;

async function extractTextLocally(file) {
  // Load the file into local RAM (No network upload!)
  const arrayBuffer = await file.arrayBuffer();
  
  // Parse the PDF using the local Wasm engine
  const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
  let fullText = '';
  
  // Extract text page by page
  for (let i = 1; i &amp;lt;= pdf.numPages; i++) {
    const page = await pdf.getPage(i);
    const textContent = await page.getTextContent();
    const pageText = textContent.items.map(item =&amp;gt; item.str).join(' ');
    fullText += pageText + '\n\n';
  }
  
  return fullText;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Secure AI Handoff&lt;br&gt;
Once we have the raw text extracted locally, we can safely send just the text to our Next.js API route via a secure POST request.&lt;/p&gt;
&lt;p&gt;The physical .pdf file (which might contain metadata, hidden layers, or signatures) is completely discarded. It never touches our server.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Inside our Next.js Route Handler (app/api/analyze/route.ts)
export async function POST(req) {
  const { text, documentType } = await req.json();

  // We write a strict system prompt instructing the AI how to behave
  const prompt = `
    You are a highly critical Venture Capitalist evaluating a startup pitch deck.
    Analyze the following text and return a JSON object containing the &quot;readinessScore&quot;,
    &quot;valueProposition&quot;, and any critical &quot;redFlags&quot;.
    
    Text: ${text.substring(0, 15000)}
  `;

  // Send the prompt to the LLM (Gemini / OpenAI)
  const response = await fetch(&quot;https://api.llm-provider.com/generate&quot;, {
    method: 'POST',
    body: JSON.stringify({ prompt })
  });

  const data = await response.json();
  return NextResponse.json(data);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why This Architecture Wins B2B Users&lt;br&gt;
By separating the extraction layer (Local WebAssembly) from the analysis layer (Cloud LLM API), we achieved a massive security upgrade.&lt;/p&gt;
&lt;p&gt;When an agency uses our &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.pdfpro.co.in/tools/rfp-pitch-deck-analyzer&quot;&gt;RFP &amp;amp; Pitch Deck Analyzer&lt;/a&gt;, they know their NDA-protected RFP isn't being saved to a random database.&lt;/p&gt;
&lt;p&gt;When a user runs their Health Insurance policy through our &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.pdfpro.co.in/tools/rfp-pitch-deck-analyzer&quot;&gt;Insurance Policy Analyzer&lt;/a&gt; to find hidden exclusions, they know their medical history isn't being retained for training data.&lt;/p&gt;
&lt;p&gt;As developers, it's our responsibility to protect our users' data. Stop uploading files to the cloud when the browser is already powerful enough to do the heavy lifting locally.&lt;/p&gt;
&lt;p&gt;Rahul Banerjee is the creator of &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.pdfpro.co.in/&quot;&gt;PDF Pro&lt;/a&gt;, a privacy-first suite of over 20+ PDF utilities and AI Document Analyzers powered by WebAssembly.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/20403/how-to-build-ai-tools-that-dont-leak-corporate-data-using-webassembly-next-js</guid>
<pubDate>Fri, 12 Jun 2026 12:30:20 +0000</pubDate>
</item>
<item>
<title>Why I Made Privacy the Core Feature of PDF Pro — and What Happened to Signups.</title>
<link>https://coderlegion.com/19425/why-i-made-privacy-the-core-feature-of-pdf-pro-and-what-happened-to-signups</link>
<description>&lt;p&gt;When I first launched &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.pdfpro.co.in/&quot;&gt;PDF Pro AI&lt;/a&gt;, I had a hypothesis: people are tired of uploading their sensitive data to random cloud servers just to perform basic file operations.&lt;/p&gt;
&lt;p&gt;We live in an era where data breaches are daily news, yet developers still casually rely on external API endpoints for simple tasks like file compression or document merging. Think about how many times a user uploads a highly confidential bank statement, a signed NDA, or an employment contract to a random &quot;Free PDF Tools&quot; website.&lt;/p&gt;
&lt;p&gt;Where does that data go? Who is looking at it? When is it actually deleted?&lt;/p&gt;
&lt;p&gt;As a developer, I realized that if I wanted to build a tool people actually trusted, I needed to eliminate the server entirely. Here is why prioritizing privacy through &quot;Edge Computing&quot; and client-side processing isn't just an ethical choice—it is a massive competitive advantage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Problem with the &quot;Upload to Process&quot; Model&lt;/strong&gt;&lt;br&gt;
For the last decade, the standard architecture for SaaS tools has been:&lt;/p&gt;
&lt;p&gt;User uploads a file via an HTML form.&lt;br&gt;
The server receives it, temporarily stores it in an S3 bucket, and triggers a background job.&lt;br&gt;
A heavy Python or Node.js process does the manipulation.&lt;br&gt;
The server returns a download link to the user.&lt;br&gt;
This architecture is fundamentally flawed for two reasons:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It's expensive&lt;/strong&gt;: You have to pay for the ingress bandwidth, the compute time for the background job, and the storage buckets.&lt;br&gt;
&lt;strong&gt;It's a privacy nightmare&lt;/strong&gt;: You are legally responsible for safeguarding the user's data while it sits on your server.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Solution: Shifting Compute to the Browser&lt;/strong&gt;&lt;br&gt;
When architecting the core engine for &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.pdfpro.co.in/&quot;&gt;PDF Pro AI&lt;/a&gt;, I made a strict rule: Zero Cloud Uploads.&lt;/p&gt;
&lt;p&gt;Instead of relying on a backend server, I utilized WebAssembly (WASM). By compiling heavy C++ and Rust libraries down to WASM, modern web browsers can now execute incredibly complex operations natively, using the end-user's own hardware.&lt;/p&gt;
&lt;p&gt;When a user visits PDF Pro to extract pages from a document or compress a massive 50MB presentation, the entire operation happens entirely inside their laptop's RAM.&lt;/p&gt;
&lt;p&gt;This creates a win-win scenario:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The User wins:&lt;/strong&gt; Their documents never leave their device. They don't have to trust me to delete their files, because I never received them in the first place. Their privacy is mathematically guaranteed by the architecture.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I win&lt;/strong&gt;: Because the user is providing the compute power, my AWS server costs are essentially zero. The tool scales infinitely without requiring me to provision more backend instances.&lt;br&gt;
The Takeaway for Developers&lt;br&gt;
We are rapidly moving toward a future where &quot;serverless&quot; doesn't just mean AWS Lambda—it means literally pushing the workload entirely to the client's browser.&lt;/p&gt;
&lt;p&gt;If you are building tools that manipulate documents, images, audio, or video, I strongly urge you to investigate WASM libraries (like &lt;code&gt;pdf-lib&lt;/code&gt;, &lt;code&gt;ffmpeg.wasm&lt;/code&gt;, or &lt;code&gt;sharp&lt;/code&gt; client-side ports).&lt;/p&gt;
&lt;p&gt;Build products that respect your users' data by default. Make privacy the foundation of your architecture, rather than an afterthought.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Rahul Banerjee is a software engineer and the creator of &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.pdfpro.co.in/&quot;&gt;PDF Pro AI&lt;/a&gt;, a privacy-first document workspace featuring 100% local PDF manipulation tools and secure AI contract analysis.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/19425/why-i-made-privacy-the-core-feature-of-pdf-pro-and-what-happened-to-signups</guid>
<pubDate>Mon, 01 Jun 2026 13:54:19 +0000</pubDate>
</item>
<item>
<title>How I Built a 100% Local, Zero-Upload PDF Editor Using WebAssembly</title>
<link>https://coderlegion.com/19307/how-i-built-a-100-local-zero-upload-pdf-editor-using-webassembly</link>
<description>&lt;p&gt;I used to hate dealing with PDFs.&lt;/p&gt;
&lt;p&gt;Every time I needed to merge two pages of a bank statement or compress an identity document for a government portal, my only option was to Google &quot;free PDF compressor&quot; and upload my sensitive files to a random server halfway across the world.&lt;/p&gt;
&lt;p&gt;As a developer, that felt completely wrong. Why do my files need to leave my machine just to get compressed or merged?&lt;/p&gt;
&lt;p&gt;I decided to solve my own problem and build &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.pdfpro.co.in/&quot;&gt;PDF Pro AI&lt;/a&gt;—a 100% private, zero-upload document workspace. The rule was simple: all processing had to happen locally in the user's browser.&lt;/p&gt;
&lt;p&gt;Here is exactly how I architected a serverless PDF editor using WebAssembly, React, and Next.js, and why you should consider moving heavy computation to the client side.&lt;/p&gt;
&lt;p&gt;The Problem with Cloud PDF Tools&lt;br&gt;
Most free PDF tools use a standard architecture:&lt;/p&gt;
&lt;p&gt;You upload your file.&lt;br&gt;
The server spins up a heavy Python or Node.js process using libraries like PyPDF2 or Ghostscript.&lt;br&gt;
It does the manipulation.&lt;br&gt;
You download the result.&lt;br&gt;
The server (hopefully) deletes your file.&lt;br&gt;
This is slow, expensive to scale, and fundamentally a massive privacy risk. If a user is uploading a highly confidential NDA or a bank statement, they have to trust that your cron job actually deletes the file.&lt;/p&gt;
&lt;p&gt;I wanted to eliminate the server entirely.&lt;/p&gt;
&lt;p&gt;Enter WebAssembly (WASM) and pdf-lib&lt;br&gt;
To do heavy document manipulation without a backend server, I turned to WebAssembly.&lt;/p&gt;
&lt;p&gt;Specifically, I built the core engine around pdf-lib, a fantastic JavaScript library that works beautifully in modern browsers. Instead of relying on a server to crunch the bytes, pdf-lib leverages the user's own CPU to modify the PDF structure locally.&lt;/p&gt;
&lt;p&gt;Here is what the architecture looks like for merging two PDFs directly in the browser:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { PDFDocument } from 'pdf-lib';

async function mergePDFsLocally(files) {
  // Create a brand new, empty PDF document
  const mergedPdf = await PDFDocument.create();

  for (const file of files) {
    // Read the user's file as an ArrayBuffer directly from the file input
    const arrayBuffer = await file.arrayBuffer();
    
    // Load it into pdf-lib
    const pdf = await PDFDocument.load(arrayBuffer);
    
    // Copy all pages from the source PDF
    const copiedPages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
    
    // Append them to our new document
    copiedPages.forEach((page) =&amp;gt; mergedPdf.addPage(page));
  }

  // Serialize the final document to bytes
  const mergedPdfBytes = await mergedPdf.save();
  
  // Create a local blob URL for the user to download instantly
  const blob = new Blob([mergedPdfBytes], { type: 'application/pdf' });
  return URL.createObjectURL(blob);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Magic of the Blob URL&lt;br&gt;
Look closely at the last three lines of that code snippet. That is where the magic happens.&lt;/p&gt;
&lt;p&gt;When the PDF is finished merging, I convert the raw bytes into a Blob and generate an Object URL using URL.createObjectURL().&lt;/p&gt;
&lt;p&gt;This creates a temporary, local-only download link that looks something like blob:&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.pdfpro.co.in/5b8f&quot;&gt;https://www.pdfpro.co.in/5b8f&lt;/a&gt;.... The file never touched an S3 bucket. It never hit an API endpoint. The entire operation happened in the RAM of the user's laptop.&lt;/p&gt;
&lt;p&gt;The result? The merge happens in less than 50 milliseconds, and the user's privacy is mathematically guaranteed because the file literally cannot leak.&lt;/p&gt;
&lt;p&gt;Handling Compression with PDF.js&lt;br&gt;
Merging is relatively straightforward, but what about compressing?&lt;/p&gt;
&lt;p&gt;Compressing a PDF locally is notoriously difficult because you essentially have to re-render the document and strip out unoptimized metadata. For this, I combined pdf-lib with Mozilla's pdf.js worker.&lt;/p&gt;
&lt;p&gt;The workflow I built does the following:&lt;/p&gt;
&lt;p&gt;pdf.js reads the document and extracts the raw text and image assets on a background web worker thread (so the UI doesn't freeze).&lt;br&gt;
I compress the extracted images locally using HTML5  rendering.&lt;br&gt;
pdf-lib reconstructs the PDF using the newly compressed assets.&lt;br&gt;
It took a lot of trial and error to get the memory management right—large PDFs can easily crash a browser tab if you don't aggressively clear your ArrayBuffers for garbage collection—but the final product is incredibly fast.&lt;/p&gt;
&lt;p&gt;Why This Matters for Indie Hackers&lt;br&gt;
Moving heavy workloads to the browser isn't just about privacy; it is an incredible cost-saving hack for solo founders.&lt;/p&gt;
&lt;p&gt;Because PDF Pro doesn't rely on expensive cloud functions or heavy backend storage buckets for standard document editing, my AWS/Vercel bill for that specific feature is exactly $0.00. The users provide their own compute power. I just provide the WebAssembly instructions.&lt;/p&gt;
&lt;p&gt;If you are building a tool that manipulates images, audio, video, or documents, I highly recommend exploring WebAssembly and client-side processing before reaching for a server. Your users will love the privacy, and you will love the hosting bill.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Rahul Banerjee is a software engineer and the creator of PDF Pro AI, a privacy-first document workspace featuring local PDF tools and AI-powered contract analysis.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/19307/how-i-built-a-100-local-zero-upload-pdf-editor-using-webassembly</guid>
<pubDate>Sat, 30 May 2026 13:22:14 +0000</pubDate>
</item>
</channel>
</rss>