<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Nibbo]]></title><description><![CDATA[Nibbo]]></description><link>https://nibbo.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sat, 24 Oct 2026 13:31:11 GMT</lastBuildDate><atom:link href="https://nibbo.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Compress a PDF to Under 200 KB for an Online Application: What Actually Shrinks the File]]></title><description><![CDATA[Job portals, visa applications, university admissions and tax offices love a hard upload cap: "PDF only, maximum 200 KB". Your scanned degree certificate…]]></description><link>https://nibbo.hashnode.dev/compress-a-pdf-to-under-200-kb-for-an-online-application-what-actually-shrinks-the-file</link><guid isPermaLink="true">https://nibbo.hashnode.dev/compress-a-pdf-to-under-200-kb-for-an-online-application-what-actually-shrinks-the-file</guid><category><![CDATA[pdf]]></category><category><![CDATA[compression]]></category><category><![CDATA[forms]]></category><category><![CDATA[Productivity]]></category><dc:creator><![CDATA[Nibbo]]></dc:creator><pubDate>Thu, 22 Oct 2026 23:10:28 GMT</pubDate><content:encoded><![CDATA[<h1>Compress a PDF to Under 200 KB: What Actually Shrinks the File</h1>
<p>Job portals, visa applications, university admissions and tax offices love a hard upload cap: "PDF only, maximum 200 KB". Your scanned degree certificate is 4.8 MB. Here is how to get it under the limit without turning it into an unreadable grey smear, and why some PDFs refuse to shrink.</p>
<h2>First, figure out what the PDF is made of</h2>
<p>A PDF is a container. Its size comes from three things:</p>
<ol>
<li><strong>Embedded images</strong> — scans, photos, logos. In a scanned document this is 95%+ of the file.</li>
<li><strong>Fonts</strong> — a fully embedded font family can add 200–800 KB on its own.</li>
<li><strong>Everything else</strong> — text, vector drawings, form fields, metadata, sometimes an embedded thumbnail of every page.</li>
</ol>
<p>A text-only PDF exported from Word is usually 50–150 KB already. If yours is 5 MB, it's almost certainly images. That's good news, because images are the part you can compress hard.</p>
<h2>The math for scanned pages</h2>
<p>Say you have a 2-page scan and a 200 KB limit. That's ~95 KB per page, leaving a little for the PDF structure. A JPEG of an A4 page at 150 DPI is roughly 1240×1754 pixels. At quality 60–70 in grayscale, a text page lands at 60–120 KB. In colour, double that.</p>
<p>So the levers, in order of impact:</p>
<ul>
<li><strong>Resolution (DPI)</strong>: going from 300 to 150 DPI cuts pixels by 75%. 150 DPI is perfectly readable on screen and when printed at A4. 100–120 DPI is still legible for typed text.</li>
<li><strong>Grayscale</strong>: if colour doesn't matter (most certificates, IDs for internal checks, signed forms), dropping colour saves 40–60%.</li>
<li><strong>JPEG quality</strong>: 60–75 for documents. Text edges get slightly soft but remain readable.</li>
</ul>
<h2>Doing it in the browser</h2>
<p><a href="https://nibbo.org/en/compress-pdf-to-200kb/">Nibbo's "compress PDF to 200 KB" tool</a> takes the target size as the goal and works backwards: it re-encodes the embedded images at progressively lower resolution and quality until the file fits, then stops. It runs entirely in your browser tab, which matters here — the files people squeeze into 200 KB are diplomas, passports, bank statements and payslips. None of that should be uploaded to a random compression site.</p>
<ol>
<li>Open the tool and drop the PDF.</li>
<li>Run it. You'll see the resulting size and can preview pages.</li>
<li>Zoom to 100% on the smallest text (stamps, signatures, serial numbers) and make sure it's still legible.</li>
<li>Download.</li>
</ol>
<p>If 200 KB destroys readability, check whether the form actually allows multiple files; splitting a 4-page document into two uploads at 200 KB each doubles your budget. There are also presets for <a href="https://nibbo.org/en/compress-pdf-to-100kb/">100 KB</a>, 300 KB, 500 KB, 1 MB and up.</p>
<h2>When a PDF won't compress</h2>
<ul>
<li><strong>It's already text-only.</strong> Fonts and structure can't shrink much. Try re-exporting from the source app with "minimum size" or standard fonts.</li>
<li><strong>It contains a huge vector drawing</strong> (CAD exports, maps). Images aren't the problem, so image compression won't help. Rasterise that page to an image at 150 DPI.</li>
<li><strong>It's encrypted.</strong> Some tools can't rewrite a protected PDF. Remove the restriction first if you own the document.</li>
<li><strong>It's a PDF/A archive copy.</strong> PDF/A forbids some optimisations. If the portal doesn't require PDF/A, a normal PDF is fine.</li>
</ul>
<h2>A checklist before you hit "Submit"</h2>
<ul>
<li>Every page present, in order.</li>
<li>Smallest text legible at 100% zoom.</li>
<li>Signature and stamp visible.</li>
<li>File name sensible (<code>Lastname_Degree_Certificate.pdf</code>, not <code>scan0003.pdf</code>).</li>
<li>Size <em>under</em> the limit, not equal to it. Some portals count 200 KB as 200,000 bytes, others as 204,800. Aim for ~190 KB to be safe.</li>
</ul>
<h2>Bonus: start from a better scan</h2>
<p>If you're scanning again anyway, scan at 150–200 DPI in grayscale, not 600 DPI colour. You'll get a smaller, cleaner file from the start, and the compressor has less work to do. Phone "scan document" modes are fine too, as long as the page is flat and evenly lit — shadows and gradients cost bytes.</p>
]]></content:encoded></item><item><title><![CDATA[Converting a 3 GB Video Without a Server: Notes on WebCodecs, ffmpeg.wasm and Browser Memory]]></title><description><![CDATA[What it actually takes to move file conversion from the backend into the tab, and where the hard limits are]]></description><link>https://nibbo.hashnode.dev/converting-a-3-gb-video-without-a-server-notes-on-webcodecs-ffmpeg-wasm-and-browser-memory</link><guid isPermaLink="true">https://nibbo.hashnode.dev/converting-a-3-gb-video-without-a-server-notes-on-webcodecs-ffmpeg-wasm-and-browser-memory</guid><category><![CDATA[WebAssembly]]></category><category><![CDATA[FFmpeg]]></category><category><![CDATA[privacy]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[#WebCodecs ]]></category><dc:creator><![CDATA[Nibbo]]></dc:creator><pubDate>Thu, 22 Oct 2026 12:33:13 GMT</pubDate><content:encoded><![CDATA[<p>Most "free online converters" have the same architecture: an upload form, a queue, a fleet of workers running ffmpeg or LibreOffice, and a download link that expires "after 1 to 24 hours". It works, but it has costs you can see from the outside: upload time, rate limits ("2 free conversions per hour"), file size caps, and the fact that your passport scan now lives on someone else's disk for a while.</p>
<p>The browser of 2026 can do most of this work itself. This post walks through how a serverless, in-browser converter is put together, using <a href="https://nibbo.org">Nibbo</a> as the reference: a free toolbox that compresses and converts video, images, audio and PDFs on the user's device, with video files up to 3 GB.</p>
<h2>The architecture: a static site and the user's hardware</h2>
<p>A client-side converter is, from the network's point of view, a static website. The server ships HTML, JS and WebAssembly modules. After that, the file the user drops never goes anywhere: it is read with the File API, processed in the tab (ideally in a Worker), and handed back as a Blob for download.</p>
<p>That has a few nice side effects:</p>
<ul>
<li><strong>No queue.</strong> Every user brings their own CPU and GPU. Ten thousand concurrent users cost the same as one — a CDN bill.</li>
<li><strong>Offline after first load.</strong> Nibbo's own docs suggest a simple test: open a tool page, switch on airplane mode, and convert. If it still works, there is nowhere for the file to be uploaded to.</li>
<li><strong>Verifiable privacy.</strong> Open DevTools → Network, filter by <code>method:POST</code>, run a conversion. A server-side converter shows a request roughly the size of your file. A client-side one shows page assets and maybe a few KB of analytics.</li>
</ul>
<p>The hard parts are all on the client: codecs, memory, and performance on hardware you don't control.</p>
<h2>Two engines: WebCodecs and ffmpeg.wasm</h2>
<p>There are two realistic ways to transcode video in a browser today, and they have very different profiles.</p>
<h3>WebCodecs: the hardware path</h3>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/API/WebCodecs_API">WebCodecs</a> exposes the browser's own decoders and encoders — the same ones used for video playback and WebRTC — as <code>VideoDecoder</code> and <code>VideoEncoder</code>. On most machines that means hardware-accelerated encoding on the GPU or media engine.</p>
<p>Strengths:</p>
<ul>
<li>Fast. Hardware H.264 encoding runs at many times real-time on ordinary laptops.</li>
<li>Frames are <code>VideoFrame</code> objects that can stay in GPU memory, so you avoid copying every frame into the WASM heap.</li>
<li>Low CPU usage, so the tab doesn't melt the fan.</li>
</ul>
<p>Weaknesses:</p>
<ul>
<li>WebCodecs only does codecs, not containers. You still need a demuxer (MP4, WebM, MKV parsing) and a muxer to write the output.</li>
<li>Codec support depends on the browser and OS. <code>VideoEncoder.isConfigSupported()</code> is your friend, and you need a fallback.</li>
</ul>
<h3>ffmpeg.wasm: the compatibility path</h3>
<p><a href="https://github.com/ffmpegwasm/ffmpeg.wasm">ffmpeg.wasm</a> is FFmpeg compiled to WebAssembly. It understands practically every container and codec FFmpeg does, which makes it the safety net for odd inputs: old AVI files, exotic audio, formats the browser simply can't decode.</p>
<p>The trade-offs are the mirror image of WebCodecs:</p>
<ul>
<li>Software-only encoding, so it is much slower, especially for video.</li>
<li>It runs inside a WASM linear memory, which is where the size problems start.</li>
</ul>
<h3>Combining them</h3>
<p>The practical design is a router: try the hardware path first, fall back to WASM when the browser can't decode the input. Nibbo's compressor describes exactly this: "Your GPU encodes it through WebCodecs. Formats the browser cannot decode go through FFmpeg compiled to WebAssembly."</p>
<h2>Memory: why "3 GB in a browser" is not trivial</h2>
<p>The naive ffmpeg.wasm workflow is: write the input file into the in-memory virtual filesystem, run the command, read the output file back. For a 3 GB video that means 3 GB of input plus the output, all inside a WASM heap — and 32-bit WebAssembly memory tops out at 4 GB, with real browsers often refusing large allocations well before that. Mobile Safari will kill the tab much earlier.</p>
<p>So a converter that handles multi-gigabyte inputs has to avoid holding the file in memory at all. The general techniques:</p>
<ol>
<li><strong>Stream from the <code>File</code> handle.</strong> A <code>File</code> is a <code>Blob</code>; <code>file.slice(start, end)</code> lets you read chunks on demand without loading the whole thing. Demuxers can pull just the byte ranges they need.</li>
<li><strong>Keep frames out of the heap.</strong> With WebCodecs, decoded <code>VideoFrame</code>s can be fed straight into the encoder. Call <code>frame.close()</code> promptly — leaked frames are the most common way to exhaust GPU memory.</li>
<li><strong>Apply backpressure.</strong> Watch <code>encoder.encodeQueueSize</code> and <code>decoder.decodeQueueSize</code>, and stop feeding when they grow. Otherwise a fast decoder will happily queue thousands of frames.</li>
<li><strong>Write output incrementally.</strong> Encoded chunks go to a muxer that writes to disk-backed storage (OPFS, or a streaming download) instead of an ever-growing <code>ArrayBuffer</code>.</li>
<li><strong>Run in a Worker.</strong> Keeps the UI responsive, and isolates the heavy allocations from the main thread.</li>
</ol>
<p>Done this way, peak memory is governed by the number of frames in flight, not by the file size. That is what makes a 3 GB ceiling realistic on a normal laptop.</p>
<h2>Hitting an exact target size</h2>
<p>A detail I liked in Nibbo's video compressor: you pick a target in MB (say, under Discord's 20 MB free limit or Gmail's 25 MB), and the tool works backwards. Per its own description, it measures how detailed (spatial information, SI) and how much in motion (temporal information, TI) the clip is, computes a bitrate budget from the target size and duration, then picks resolution and frame rate to fit that budget — on the principle that a lower resolution with enough bits beats a blocky full-resolution picture. If the output still comes out slightly over the limit, it re-encodes with a corrected bitrate.</p>
<p>The math itself is simple (<code>bitrate ≈ target_bits / duration</code>, minus audio and container overhead); the hard part is that hardware encoders don't hit bitrates precisely, which is why the correction pass matters.</p>
<h2>Not just video</h2>
<p>The same approach extends to everything that is CPU-bound and doesn't actually need a server. Nibbo's sitemap lists well over a hundred tool pages, including:</p>
<ul>
<li><strong>Images:</strong> HEIC/WebP/AVIF/JPEG XL/PNG/JPG conversion, compress-to-exact-KB for exam and government portals, crop, resize, EXIF removal without re-encoding.</li>
<li><strong>PDF:</strong> merge, split, compress, OCR in 25 languages, PDF to Word/Excel, AES-256 protection, signing.</li>
<li><strong>Audio:</strong> cutters, normalization, LUFS meter, format conversions.</li>
<li><strong>Developer utilities:</strong> JSON formatter with schema validation, a JWT decoder that verifies signatures locally, hash generator.</li>
<li><strong>On-device AI:</strong> face blurring, auto subtitles, a redaction tool that strips emails, phone numbers and card numbers before you paste content into an AI chat.</li>
</ul>
<p>Several of these (the JWT decoder, the redaction tool, the e-invoice viewer) are tools where uploading the input would defeat the point entirely.</p>
<h2>Where client-side still loses</h2>
<p>Honest limitations of the model:</p>
<ul>
<li><strong>Hardware variance.</strong> A five-year-old phone will be slow, and some browsers lack certain encoders. You need graceful fallbacks and clear messaging.</li>
<li><strong>Software fallback is slow.</strong> When ffmpeg.wasm does the encoding, expect it to be far slower than native FFmpeg.</li>
<li><strong>Size ceilings exist.</strong> Even with streaming, there are practical limits; 3 GB is Nibbo's stated cap for free use.</li>
<li><strong>Cold start.</strong> WASM modules have to be downloaded once. Caching helps, but the first run costs bandwidth.</li>
</ul>
<h2>Takeaways</h2>
<p>If you are building anything that touches user files — media tools, document utilities, dev tools — it's worth asking whether the server is actually necessary. WebCodecs gives you fast hardware encoding, ffmpeg.wasm covers the long tail of formats, and careful streaming keeps memory bounded. The result is cheaper to run and privacy you can verify with DevTools instead of a promise in a policy.</p>
<p>To see the approach in practice, open <a href="https://nibbo.org">nibbo.org</a> with the Network tab open and run a conversion. Watching nothing upload is the clearest demo.</p>
]]></content:encoded></item></channel></rss>