{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreiao5bp4o42ifd4bxtomvcmu3qoin27tvhyhys353lsfopugvpzlxi",
"uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mprdletaby62"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreigm2bc7awrxcgtizd673cudql2rmyhiuo3rvu4xzyi37ql5q62cju"
},
"mimeType": "image/webp",
"size": 160906
},
"path": "/faisal_haleem_a2bc3b80613/i-built-a-free-browser-based-file-converter-using-webassembly-no-uploads-ever-52fa",
"publishedAt": "2026-07-03T19:48:11.000Z",
"site": "https://dev.to",
"tags": [
"privacy",
"showdev",
"sideprojects",
"webdev"
],
"textContent": "Most online file converters have a dirty secret: your files get uploaded to a server you don't control, processed by someone else's machine, and sometimes stored for longer than you'd expect. For a quick GIF conversion or a PDF merge, that feels like overkill — and a genuine privacy risk if the file contains anything sensitive.\n\nSo I built utilize.internet — a collection of free file tools that run entirely in your browser using WebAssembly and the Canvas API. Your files never leave your device.\n\nHow it works\n\nThe key insight is that modern browsers are surprisingly capable of heavy computation. WebAssembly lets you run near-native code in the browser, which means tools like FFmpeg (for video) and pdf-lib (for PDF manipulation) can run client-side without any server involvement.\n\nThe architecture for every tool on the site follows the same pattern:\n\nUser selects file\n↓\nBrowser reads file into memory (FileReader / ArrayBuffer)\n↓\nWebAssembly / JS library processes it locally\n↓\nResult is offered as a download via Blob URL\n↓\nFile is revoked from memory when done\n\nNothing touches a server. No API calls for the actual processing. The only external requests are for Google Fonts (styling) and Google Analytics (visitor counting) — neither of which touches your file.\n\nThe tools\n\nHere's what's currently live:\n\nImage & Video\n\nVideo to GIF — powered by ffmpeg.wasm. Upload a video, drag two handles on a trim slider to select your clip, choose output width and FPS, convert. Uses the palettegen/paletteuse filter for noticeably better GIF color quality than a naive conversion.\nImage to GIF — gif.js running in a Web Worker. Upload multiple images, reorder them via a filmstrip UI, set frame delay, convert.\nCompress Image — Canvas API with a live quality slider. Shows before/after file size comparison in real time as you drag.\nImage Converter — Canvas API again. Batch convert between JPG, PNG, and WebP. Each file gets its own download link.\n\nPDF & Documents\n\nMerge PDF — pdf-lib. Drag in multiple PDFs, reorder them, merge into one.\nSplit PDF — pdf-lib. Extract a page range, or split every page into its own PDF (delivered as a ZIP).\nShrink PDF — Two modes: \"Balanced\" (rebuilds the PDF structure, keeps text selectable) and \"Maximum\" (re-renders every page as a JPEG via pdf.js, much smaller files for scanned documents).\nImages to PDF — pdf-lib. Combine JPGs/PNGs/WebPs into a PDF. Choose \"fit to image\" or standard page sizes (A4/Letter) with margin and auto-rotate options.\nPDF to JPG/PNG — pdf.js renders each page to canvas. Per-page download links plus a \"download all as ZIP\" option.\nWord & Excel to PDF — mammoth.js converts .docx to HTML, SheetJS converts .xlsx to an HTML table, then the browser's native print function (\"Save as PDF\") handles the actual PDF generation. This produces a real text-based PDF rather than a canvas image.\nPDF to Word (Lite) — pdf.js extracts text from each page, groups lines into paragraphs based on position/spacing, rebuilds as a .docx via the docx library. Clearly labeled as text-extraction only (no OCR, no layout preservation).\n\nThe interesting technical challenges\n\nffmpeg.wasm cross-origin worker issue\n\nThis was the biggest headache. ffmpeg.wasm 0.12 internally spawns a Web Worker for its processing thread. The worker script is a separate chunk file (814.ffmpeg.js). When loaded from a CDN, browsers block the worker construction because the worker script origin doesn't match the page origin.\n\nThe fix: self-host every ffmpeg file on the same domain. The library dynamically resolves the worker chunk relative to its own src attribute, so hosting ffmpeg.js at /tools/ffmpeg/ffmpeg.js makes it automatically look for /tools/ffmpeg/814.ffmpeg.js — same origin, no CORS issue.\n\nGIF quality\n\nA naive video-to-GIF conversion (just specifying an output format) produces washed-out colors because GIFs are limited to 256 colors per frame and FFmpeg's default palette selection isn't great. The fix is a two-pass filter chain:\n\nfps=15,scale=480👎flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse\n\npalettegen analyzes the entire clip to build an optimal 256-color palette. paletteuse then applies it. The difference in output quality is significant.\n\nZIP generation without a library\n\nFor tools that produce multiple files (Split PDF, PDF to JPG), I needed to bundle the output into a ZIP. Rather than adding another library dependency, I wrote a minimal inline ZIP writer (~100 lines) using the \"store\" method (no compression, just the ZIP container structure). For PDF and image files that are already compressed, adding a second compression pass rarely saves meaningful space anyway.\n\nShrink PDF — honest about limitations\n\nTrue PDF compression (Ghostscript-level) isn't achievable in a browser — it requires access to font subsetting, image resampling at the codec level, and other operations that need native binaries. Rather than pretend otherwise, the tool is upfront: \"Balanced\" mode removes structural redundancy but won't dramatically shrink a well-optimized PDF; \"Maximum\" mode converts to images (smaller, but text becomes unselectable). Users can make an informed choice.\n\nStack\n\nHosting: Hostinger (static files only — no server-side code)\nLibraries: ffmpeg.wasm 0.12, gif.js, pdf-lib, pdf.js, mammoth.js, SheetJS, docx — all self-hosted\nAnalytics: Google Analytics 4\nNo framework: plain HTML, CSS, and vanilla JS. Each tool is a single self-contained HTML file.\n\nThe \"no framework\" decision was deliberate. Every tool page needs to work as a standalone file served from a static host. A build step adds complexity and deployment friction that isn't justified for this use case.\n\nWhat's next\n\nQR Code Generator — a natural fit for a client-side tool\nFill PDF Forms — pdf-lib can read and fill AcroForm fields\nSign PDF — canvas signature pad + pdf-lib stamp\n\nIf you have suggestions for tools that would fit the same pattern (free, browser-based, privacy-first), feel free to drop them in the comments.\n\nThe site is at utilizeinternet.com — everything is free, no account needed.\n\nBuilt with WebAssembly, vanilla JS, and a strong preference for not storing other people's files.\n#webdev #javascript #webassembly #tutorial",
"title": "I Built a Free Browser-Based File Converter Using WebAssembly — No Uploads, Ever"
}