DOCUMENT COMPRESSION · FOR EVERY TEAM

Shrink documents before they ever upload.

The Abscode Browser JS SDK compresses JPEGs, PNGs, single- or multi-page TIFFs, and PDFs entirely on the user's device, WebAssembly in the browser, no server round-trip. DPI, page layout, and filters under your control. Because the work runs on-device, you move and store far less data, which cuts time and cost first and the money follows. Production-grade, priced for any team.

Runs on-device No uploads Less data moved & stored Affordable to all
TRY IT LIVE

Compress an image right here

Drop an image (or a PDF / TIFF) and compress it entirely in this browser tab, nothing is uploaded. It outputs a PDF by default, open Advanced options to change the layout, DPI or output format. This is the SDK itself running on your device.

Advanced options
Drop an image here, or click to choose
JPEG · PNG · TIFF · PDF, it stays on your device

    Unlicensed demo, outputs carry a watermark band. Ask for an evaluation blob to remove it.

    WHAT IT DOES

    Client-side PDF compression in JavaScript

    Hand the SDK an array of file buffers. It compresses every page against your settings and hands back the bytes, all without a network call.

    • ✓ JPEG, PNG, TIFF (single or multi-page) and PDF accepted as input, plus other common image formats
    • ✓ Multi-page inputs fan out and compress per page
    • ✓ Output a JPEG per page, a single PDF, or a multi-page TIFF
    • ✓ DPI, page layout (A0–A6), and colour filter per job
    • ✓ Every method resolves {STATUS, DESCRIPTION, DATA}, never throws
    • ✓ Runs offline once the page has loaded
    compress.js
    const helper = new ImageWizHelper();
    await helper.ready;
    await helper.initialize(LICENSE_BLOB);
    
    // tune the job
    await helper.SetDPI(200);
    await helper.SetPageLayout(4);  // 0..6 → A0..A6
    await helper.SetFilter(0);          // 0 Normal · 200 B&W · 201 Grayscale
    
    // read the picked files straight into memory
    const buffers = await Promise.all(
      [...input.files].map(async f =>
        new Uint8Array(await f.arrayBuffer()))
    );
    
    const res = await helper.compressToPdf(buffers);
    
    if (res.STATUS) {
      // res.DATA is a Uint8Array, one PDF of every page
      save('out.pdf', res.DATA, 'application/pdf');
    } else {
      console.error(res.DESCRIPTION);
    }
    WHY IN THE BROWSER

    The upload shrinks before it starts

    A phone camera photographs a form at several megabytes a page. Compressing server-side means you pay to receive all of it first. Compressing on the device means you never do.

    Less upload traffic

    Only the compressed bytes cross the network. The raw original never leaves the device, so you are not billed for ingress on data you were going to discard anyway.

    No compression tier to run

    There is no queue, no worker fleet, and no autoscaling group doing image work. The compute runs on hardware your user already owns and you do not pay for.

    Documents stay put

    Nothing is transmitted to be processed, so there is no third-party processing hop to describe in a DPA and no retention question to answer.

    No round-trip latency

    The result is in memory the moment the call resolves. You can render a preview before the user has decided whether to submit at all.

    Works offline

    Once the page has loaded there is no network dependency. Field staff can capture and compress with no connectivity and sync the finished file later.

    Nothing to install

    It is a script on your page. No native runtime, no browser extension, no per-desktop rollout, which is usually the reason a scanning step never shipped.

    Efficient by design, priced for everyone

    Compressing on the device moves and stores less data and runs on hardware your user already owns, so there are fewer server cycles to pay for and to power, the efficiency is what makes it greener, not a slogan. That same efficiency is why the SDK is priced for any team, from an individual or a startup to a system integrator or an enterprise. Absolute technologies for all: affordable to all, with lower cost and no drop in output quality.

    HOW IT WORKS

    Four calls, start to finish

    1
    Load
    Construct the helper and await ready, that resolves once the WebAssembly module is live.
    2
    Initialize
    Pass your license blob to initialize(). It is non-fatal: an empty or rejected blob still runs, watermarked.
    3
    Configure
    Set DPI, page layout, and filter. Settings stick to the handle until you change them.
    4
    Compress
    Call compressToPdf, compressToTiff, or compressToJPG with your buffers and read DATA.

    Defaults, if you set nothing

    The SDK starts at 200 DPI, A4 page layout, aspect-fit scaling, and EXIF rotation on, and outputs a PDF. ResetToDefault() returns to exactly that state, it and the EXIF rotation toggles are exposed only by the main-thread build.

    CONTROLS

    Every knob is explicit

    No heuristics deciding your output for you. Three setters cover the common cases; a fourth handles anything they don't.

    Resolution, SetDPI

    Set any DPI you like, 100, 150, 200, 300, 500, 600 are the usual stops. Dropping a 600 DPI phone capture to 200 discards resolution a printed form was never going to use, which is often the single biggest size win.

    Page layout, SetPageLayout

    Pass 0 to 6 for A0 through A6, 4 is A4. Anything outside that range falls back to A4 rather than failing, so a bad value degrades quietly instead of breaking the job.

    Filters, SetFilter

    Applied per page during the compress call, so the filter and the compression are one pass:

    • 0, Normal. Leave the colour as captured.
    • 200, Black & White. SetFilter(200) then compressToTiff() gives a B&W TIFF.
    • 201, Grayscale. Drops colour, keeps tone.

    Custom page layout, SetCustomPageLayout(layout, width, height, colorInt)

    When A0–A6 isn't the shape you need, a fixed-pixel thumbnail, a bespoke form size, a letter-size target. Pass 11 to stretch the page to your dimensions or 12 to fit it inside them preserving aspect ratio, then width and height in pixels, then a background colour as 0xRRGGBB00 for the letterboxing that aspect-fit leaves behind.

    OUTPUT

    One input array, three shapes out

    Each method takes the same array of buffers. What differs is what comes back in DATA.

    compressToPdf

    Returns one Uint8Array, a single PDF containing every page of every input, in order. The usual choice when the file is going to a human or an archive.

    compressToTiff

    Returns one Uint8Array, a multi-page TIFF of every input. Pair it with SetFilter(200) when a downstream system expects bitonal scans.

    compressToJPG

    Returns an array of Uint8Array, one JPEG per page, not one file. This is the method that behaves differently from the other two; index it, don't blob it.

    Per-page progress with compressToJPGStream

    Same JPEG-per-page output, delivered as each page finishes rather than all at the end, you get index, total, name, fileData, and progress per page. Useful for rendering thumbnails into a grid while a fifty-page batch is still running. The main-thread build exposes it as an event; the worker build takes a callback.

    RUN MODES

    Main thread or worker pool

    Two builds, the same method names and the same response contract. Pick one per page.

    Instant, main thread

    Synchronous WebAssembly on the main thread. The simplest thing that works, and the right default for a one-document-at-a-time tool.

    • ✓ Fewest moving parts
    • ✓ Exclusive extras: EXIF rotation toggles, ResetToDefault(), page count straight from a File
    • ✓ Input buffers stay usable after the call
    • , Blocks the UI on a large job

    Background, worker pool

    A dispatcher spreads work across a pool of workers. Config setters broadcast to every worker handle, so the pool stays consistent.

    • ✓ UI stays responsive under load
    • ✓ Fan a batch out in parallel with Promise.all
    • ✓ Same methods, same {STATUS, DESCRIPTION, DATA}
    • , Input ArrayBuffers are transferred, and detach on your side

    The transfer gotcha, stated plainly

    The worker build transfers your input buffers into the worker for speed. A transferred buffer is detached on the caller's side afterwards, if you need those bytes again (to show the original next to the result, say), re-read the file rather than reusing the array.

    CHOOSING

    In the browser, or on your server?

    These are different tools for different moments. The honest test is simple: where is the document right now, and who owns the CPU?

    Use the Browser JS SDK when…

    • ✓ The document is being captured or picked by a user, right now, in a browser.
    • ✓ You want the upload to be small, the point is to not send the original.
    • ✓ The documents should not leave the device at all.
    • ✓ The workflow has to survive patchy or absent connectivity.
    • ✓ You'd rather not operate an image-processing tier for a step this small.

    Use the Compression SDK when…

    • ✓ The files are already on your servers. Sending them to a browser to compress would be absurd.
    • ✓ You're compressing a back-file, millions of documents, unattended, overnight.
    • ✓ Documents arrive by email, scanner, or SFTP, with no browser in the path.
    • ✓ You need throughput on hardware you control and can predict.
    • ✓ It's a desktop or backend application, not a web page.
    See the Compression SDK →

    They're frequently both

    Compressing at capture keeps the upload small and the user waiting less. Compressing on the server catches everything that arrived through some other door, and re-compresses archives on a schedule. Running both is a normal architecture, not a redundancy.

    FAQ

    Questions worth answering first

    Does any document get uploaded to compress it?
    No. The SDK is WebAssembly running in the browser tab, compression happens on the user's device and there is no server round-trip in the operation. Once the page has loaded, it works offline. The only bytes that reach your backend are the ones you choose to send afterwards.
    Can it compress a multi-page PDF or TIFF, or only single images?
    Both. Single images, JPEG, PNG, TIFF, and multi-page PDF or TIFF inputs are all accepted, and multi-page files fan out to compress per page. compressToPdf and compressToTiff take an array of buffers and return one file containing every page of every input; compressToJPG takes the same array and returns one JPEG per page.
    Will compressing a big batch freeze the page?
    It depends which build you load. The main-thread build runs synchronous WebAssembly and can block the UI on a large job. The worker-pool build runs the same methods off the main thread across a pool, so the UI stays responsive and a batch can fan out in parallel. Same method names, same response shape, the choice is a script tag.
    What happens if I test without a license key?
    Everything works. initialize() is non-fatal: with a missing, invalid, or demo license the SDK still initializes and every operation still succeeds, the output carries a watermark band. That means you can wire up and verify the whole flow before licensing anything. A false from initialize() means the process handle could not be created, not merely that the license was rejected.
    How do I handle errors, try/catch?
    Not needed. Every method is async and resolves to {STATUS, DESCRIPTION, DATA}, and failures never throw across the API. Check STATUS; on false, read DESCRIPTION for the message. DATA is present only on success.
    Can I force scans to black & white or grayscale as I compress?
    Yes, SetFilter(200) for black & white, SetFilter(201) for grayscale, SetFilter(0) to leave colour alone. The filter is applied per page during the compress call, so SetFilter(200) followed by compressToTiff() yields a black-and-white TIFF in one pass.
    How do I know what I started with, before compressing?
    readImageInformation(buffer) returns file size in KB, total page count, and per page the number, width, height, colour type (BW, GREYSCALE, or COLOR) and DPI. It's the natural way to decide settings per document, or to show a before-and-after in your UI.
    Do I still need the server-side Compression SDK?
    Only for documents that were never in a browser. If files land on your servers by email, scanner, or SFTP, or you're re-compressing a back-file archive in bulk, that's the Compression SDK's job. If a user is picking or capturing the document in your web app, compressing it there is strictly less work for everyone. Plenty of teams run both.

    Licensing questions, volume terms, or an evaluation blob, talk to us.

    THE FULL TOOLKIT

    Explore the rest of the Browser JS Library

    One license, every capability — take this and you also get all of these.

    Compress it before it leaves the device

    Wire up the whole flow unlicensed, every operation runs, watermarked, until you're ready.