How UnboundPDF handles large PDFs locally
Updated 18 August 2026
Large documents are opened in stages, rendered as you reach them, batched during export and released as they go, with the plan chosen against your device before the run starts. Every figure below was measured on 2026-08-15 across Chromium, Firefox and WebKit, and every output was reopened in an independent PDF reader.
What runs where, why big documents are hard, and the 2026-08-15 benchmark tables across three browser engines — including the parts we could not measure.
UnboundPDF is a free suite of 43 PDF and image tools that run entirely in your browser — merge, split, compress, edit text, OCR in 126 languages, redact, sign, convert and archive to PDF/A. Your document is read and written by the page on your own device; there is no document-upload endpoint in the core tools, no account, no watermark and no daily cap. Every result can be checked — with the Network tab, or with the Document Passport the Workspace writes for a chain of steps.
- Open PDF Editor and load the largest PDF you have — a few hundred scanned pages is a good test.
- Watch the first page: it should paint before the rest of the document has been built.
- Scroll to the middle. Pages render as you reach them rather than all at once.
- Read the panel before you run anything — it names the mode it picked for your device, and says so when it has never measured a device like yours.
- Press Stop mid-run. The run ends, and the file you opened is untouched.
Last benchmark: 15 August 2026. 124 runs, three browser engines, twelve fixtures, one desktop machine. 79 of 79 byte-producing runs were reopened and checked in poppler — an independent PDF stack. The machine-readable per-tool record is capabilities.json; the dated write-up of what changed is the changelog.
What runs where
Every tool opens your document with pdf.js and writes it back with pdf-lib, inside the page. Two vendor libraries bring their own threads: pdf.js parses PDF structure in its own worker, and Tesseract recognises text in its own. Both are vendor code, and we record them as vendor code. Everything else — rasterising pages onto canvas, re-encoding JPEGs, assembling ZIP archives, and every pdf-lib call — runs on the page's own main thread.
We have no first-party worker. The capability registry records workerEnabled: false on all 33 PDF tools in the 15 August 2026 matrix, and the registry build refuses to run if any row claims otherwise. One first-party worker was written — a JPEG re-quantisation worker — and it ships switched off because its own benchmark failed: no improvement in the longest stall, and 818–1008 MB more peak process memory. We would rather publish that than describe a design we did not ship.
What makes main-thread execution liveable is measured rather than assumed. During the heaviest operations in the corpus, a self-rescheduling timer measured the longest stretch in which a queued task — your own click on Stop — could not run:
| Engine | Compress Balanced, 300-page scan | Page images, 1,000 pages | OCR, 100-page scan |
|---|---|---|---|
| Chromium | 35 ms | 39 ms | 34 ms |
| Firefox | 35 ms | 14 ms | 30 ms |
| WebKit (Safari's engine) | 57 ms | 30 ms | 35 ms |
The worst figure anywhere was 57 ms, and the run most likely to look frozen — an 18.5-minute OCR grind in Firefox, sampled 51,226 times (this is the responsiveness probe's own run of that cell; the throughput matrix's run of it took 19.7 minutes — the same work, measured twice, and both figures appear on this page) — was the one where a queued click would have run within 30 ms. That is the mechanism: these loops hand the thread back between batches and after each item, rather than holding it. Two bounds on that result: it is desktop only, on three operation classes only, and it measures queued-task lag, not frame timing — the paint meter that would have measured smoothness distorted the thing it measured by 26× and was discarded, so this page makes no claim about visual smoothness. On phones the opposite was observed by hand: scrolling a 1,000-page document was described as "like hanging" on an iPhone.
Why large PDFs are hard
The intuitive model — memory grows with file size — is wrong, and we published the measurement that falsified our own version of it. An early estimator encoded page-image export at roughly 55× the input size, derived from a single 39.76 MB measurement. Driven across a ten-fold input range in Chromium on 2026-08-13, that ratio collapses:
| Input | Peak process memory | Implied ratio |
|---|---|---|
| 39.76 MB / 100 pages | 2219 MB | 55.8× |
| 118.99 MB / 300 pages | 2400 MB | 20.2× |
| 237.98 MB / 600 pages | 2515 MB | 10.6× |
| 396.72 MB / 1,000 pages | 2362 MB | 6.0× |
Peak memory is nearly flat while the input grows ten-fold. The cost is a working set — how much of the document an operation insists on holding at once — not a multiple of the document. The "55×" was a fixed cost divided by the smallest input anyone had measured. Shipped as written, that estimator would have refused a document that finishes in under two minutes.
The same run gives the reason to distrust any single memory number: the identical 100-page fixture measured 1807, 1954, 2219 and 2721 MB across four runs in Chromium — a ±25% band on unchanged input. Every memory figure on this page is one sample of a quantity that does not reproduce, and none of them is a boundary.
Staged and lazy rendering
The single largest improvement in this programme was in the editor, and it was a structural bug rather than a memory problem. The editor built a canvas for every page before painting any of them, so on a 1,000-page document the first pixel arrived only after the last page was built:
| Document | First page painted, before | First page painted, after |
|---|---|---|
| 100-page scan | 9,036 ms | — |
| 300-page scan | 22,074 ms | — |
| 1,000 pages | 73,394 ms | ≤ 1,027 ms |
The "after" figure was sampled at one-second resolution, so it is an upper bound rather than a reading of 1,027 ms. The 2026-08-15 matrix re-measured the same behaviour at 500 ms polling across all three engines and found first paint no longer scales with page count at all: ≤ 517 ms in Chromium, ≤ 588 ms in Firefox, ≤ 516 ms in WebKit on a 1,000-page document — the same half-second as on a one-page document.
Page thumbnails are lazy and capped. Organize Pages, Split PDF and Rotate PDF render at most 200 thumbnails however long the document is, and render them in a band around the viewport: in standard mode a page starts rendering 300 px before it scrolls into view and is released 900 px after it leaves. Memory for those tools is bounded by the feature, not by the page count — 3,000 pages cost 721 MB where 1,000 pages cost 688 MB in the same Chromium run.
The editor keeps a small cache of rendered pages around the one you are looking at — three in standard mode, one in low-memory mode — and zeroes the backing store of the canvases it evicts.
Batch processing
Page-image export is the most memory-hungry operation we have, and it was rebuilt as a render → encode → release pipeline: a batch of pages is rasterised, encoded to JPEG bytes and released before the next batch starts, rather than every page canvas living until the end. Batch size is eight pages in standard mode and four in low-memory mode.
Measured against the same 100-page scan, PDF to JPG fell from 2219 MB before that change to a 534–582 MB band after it. On the 300-page scan, the same operation read 2400 MB in the 2026-08-13 matrix and 779 MB in Chromium on 2026-08-15.
One cliff is not fixed, and it is a page-count cliff rather than a size one: the exporter still holds every encoded JPEG while the ZIP is assembled as a single contiguous array. On a 1,000-page document that read 1500 MB in Chromium — for a 2.3 MB input — while producing a 320 MB archive that poppler confirmed holds all 1,000 images.
What gets released, and when
Every heavy resource a run allocates is registered with a cleanup ledger and disposed in reverse order when the run ends — whether it ended by finishing, by failing, or because you pressed Stop. Canvases have their backing store zeroed, ImageBitmaps are closed, object URLs are revoked, vendor workers are terminated, large arrays are dropped. Each disposal is independent, so a failure to clean one resource can never overwrite the run's real outcome.
What is verified: a repeated-save stress suite asserts that process memory stays flat across five consecutive saves, and the ledger's release-on-every-exit contract is unit-tested. What is not verified by the benchmark matrix: leak behaviour across a long session, because every matrix run started from a cold browser and each engine's process tree was drained before it.
Where intermediate bytes go
When you drop a file on the homepage and pick a tool, the document is carried across in IndexedDB rather than through a string copy — bytes, a name and a type, stored under one key. It is taken exactly once, deleted in the same transaction, and ignored entirely if it is more than five minutes old, so a document from an earlier session cannot resurface in a later tab. Those bytes never touch the network.
We did not ship OPFS. The Origin Private File System was in the design as an option for spilling intermediates to disk; it was not built, and nothing in the product uses it. Intermediates live in page memory for the life of the run.
The pre-flight check
Before a heavy run starts, the tool looks at the document — pages, bytes, the operation class — and at what the browser will tell it about the device, and picks one of five execution modes: standard, low-memory, selected pages (on the tools that accept a page range), desktop recommended, and one that stops before it starts — where the operation cannot produce a valid file at that size, the tool refuses rather than running, in its own words: “we are stopping before it starts. Your file has not been changed.” In every case it names the mode it picked and why, before anything begins rather than after.
The part worth reading is what it does when it does not know. Memory bands exist for exactly one device class — the desktop the benchmark ran on. A device outside that class gets the verdict unknown, in those words: "no memory band has ever been measured on this device class." A browser reporting a small memory budget gets low-memory mode and an unknown verdict, because a small budget is a signal we have no measurement for. No memory quantity is ever shown to you, on any device, because on every device except one we do not have one to show.
Low-memory mode — and the claim we will not make
Low-memory mode is wired on seven of the 33 tools in the 15 August 2026 matrix, and it does something different on each family. On PDF Editor, Fill & Sign and Sign PDF it shrinks the rendered-page cache from three pages to one and clamps the canvas area budget from 16 to 4 megapixels. On Split, Organize and Rotate it narrows the thumbnail band so pages render only once they are on screen and are released 300 px later. On PDF to JPG it narrows that same thumbnail band and halves the export batch as well — the page cache and the canvas clamp are read only by the editor, so PDF to JPG does not get those.
We cannot tell you it reduces the risk of failure. "Reduces risk" is a comparison against a failure rate, and the failure rate we have measured is zero — nought failures in 124 runs on 2026-08-15, nought in the 20 runs before that. There is no risk in our data for the mode to reduce. The mode is real, it is named, and what it changes is listed above; its effect on failure is unmeasured, and will stay unclaimed until something actually fails.
Stopping a run
Every long operation carries a Stop control, and the label tells you what stopping means for that operation rather than offering one vague promise: "it stops within the current item" (26 tools), "it stops after the current page finishes" (7 tools), "this cannot be stopped during the final rebuild" (4 tools, plus 1 that is item-cancellable up to its rebuild), or "stopping discards the work — there is no resume; you would start again" (4 tools, the image tools, whose single pass has nothing to abandon part-way). One tool is recorded as not wired, and says so in the registry rather than pretending.
Nothing resumes. All 33 tools in that matrix are recorded as restart-required, and the registry's resumable value is defined so that it can never be assigned — pdf-lib and pdf.js need a full restart, and a resume claim would be a lie the code could not honour. Cancellation is driven through four real tools in a real browser on every gate run, which asserts that the run ends, that the tool can be run again afterwards, and that your input file is untouched.
Verifying the output
Of the 124 runs on 2026-08-15, 79 produced an output document, and 79 of 79 were reopened and checked in poppler — a PDF stack entirely independent of the one this site ships. The other 45 produce no document: 36 are open-path runs and 9 are refusals. Each check is specific rather than a green tick: correct page count wherever the input's own count was readable (including 301-page and 501-page merges), a real page-1 raster in every PDF, ZIP entry counts equal to page counts, including a 1,000-entry archive, 91,127 searchable characters extracted from an OCR output, and 2,988 characters surviving a re-save of a Chinese document.
The product does not verify itself. Poppler checked those outputs externally, afterwards, in our test harness. The four guided outcome chains do verify on the output bytes before they show you a receipt — that is what makes them chains rather than articles — but the 33 individual tool pages do not. If you want the check on your own file, reopen the result in a different reader.
Working offline
A service worker caches the site shell and, on first use, the heavy engines. Only one tool has a verified offline run: on every gate run the HTTP server is killed outright, the browser context is put offline, Merge PDF is reloaded from cache, two real PDFs are merged and the output is reopened. The other 42 tools are recorded as shell-cached but unverified offline, and that is exactly how the registry words it.
The same suite enumerates every cache entry on the origin and byte-scans each one for a needle taken from the middle of each input document. No byte of either document appears in any cache. Caching the program is not caching your file, and that distinction is tested rather than asserted — the method for checking it yourself is in the PDF Privacy Lab.
The benchmark tables
All of the following was measured on 15 August 2026 on one machine — Apple M4, 16 GB unified memory, macOS 26.3.1 — driving the real interface through Playwright 1.61.1 on Chromium, Firefox Nightly and WebKit. Wall times come from a headless browser with unthrottled animation timing: they are the friendliest this product will produce, and they are not phone numbers.
One document, six operations, three engines
A 300-page scanned document, 118.99 MB. Every memory figure in this table and the next is a single sample of a quantity that does not reproduce — a lower bound on one run, never a boundary; the paragraph under the third table gives the repeat measurements that establish that.
| Operation | Chromium | Firefox | WebKit | Output, checked in poppler |
|---|---|---|---|---|
| Compress, Balanced | 15.5 s · 949 MB | 13.6 s · 1561 MB | 13.4 s · 1260 MB | valid 300-page PDF, page 1 rasters |
| Compress, Maximum | 22.6 s · 795 MB | 25.4 s · 1505 MB | 18.4 s · 962 MB | valid 300-page PDF |
| Merge (+1 page) | 2.4 s · 1076 MB | 2.7 s · 2082 MB | 2.5 s · 1288 MB | valid 301-page PDF |
| Page images → ZIP | 23.0 s · 779 MB | 23.8 s · 1793 MB | 19.8 s · 1517 MB | valid ZIP, 300 JPEG entries |
| Editor open (first page painted) | 528 ms | 592 ms | 523 ms | open path — no output document |
| Organize open (first thumbnail) | 515 ms | 597 ms | 516 ms | open path — no output document |
Five of the same six on a 1,000-page document (2.31 MB, born-digital text)
Compress Maximum is absent deliberately: on a text-only 1,000-page fixture it is the page-image path, so the matrix pruned it as a re-measurement of the row below it at roughly three times the cost. Sixty-eight cells were pruned that way, each against a written rule.
| Operation | Chromium | Firefox | WebKit |
|---|---|---|---|
| Compress, Balanced | 1.8 s · 567 MB | 1.8 s · 1258 MB | 1.8 s · 734 MB |
| Merge (→ 1,001 pages) | 1.4 s · 644 MB | 1.7 s · 1300 MB | 1.3 s · 770 MB |
| Page images → ZIP | 21.6 s · 1500 MB | 16.1 s · 2163 MB | 28.8 s · 1737 MB |
| Editor open (first page painted) | 517 ms | 588 ms | 516 ms |
| Organize open (first thumbnail) | 516 ms | 522 ms | 568 ms |
Compression depends on the browser, not only on the document
Identical input, identical Balanced setting, identical slider position, on 2026-08-15. Output-to-input byte ratio:
| Document | Chromium | Firefox | WebKit |
|---|---|---|---|
| 100-page scan, 39.76 MB | 0.342 | 0.387 | 0.565 |
| 300-page scan, 118.99 MB | 0.342 | 0.387 | 0.565 |
| 500-page mixed, 99.6 MB | 0.346 | 0.390 | 0.567 |
| 1,000-page text, 2.31 MB | 0.986 | 0.986 | 0.986 |
| 2-page government form, 0.21 MB | 1.011 | 1.011 | 1.011 |
Read as the numbers a person would quote: on that same 118.99 MB scan on 2026-08-15, Chromium delivered a 66% reduction, Firefox 61%, and Safari's engine 43%. The mechanism is in the code the measurement pointed at. Balanced re-quantises embedded JPEGs arithmetically — pure JavaScript, and correspondingly identical on all three engines, which is exactly what the text and form rows show. But a photograph above 150 DPI for its page box takes a different route: it is decoded, downsampled through a canvas and re-encoded by the browser's own JPEG encoder, and Skia, Gecko and ImageIO do not produce the same bytes at the same nominal quality. Every scan in this corpus is 200 DPI, so every scanned page lands on that branch. This is why Compress PDF carries no single "compress by X%" figure anywhere on this site.
The same split shows in Maximum (0.195 in Chromium, 0.214 in Firefox, 0.299 in WebKit on the 100-page scan) and in page-image output size. And Maximum has a failure mode in the other direction: on a document with nothing to rasterise it makes the file larger — 1.695 in Chromium, 1.775 in Firefox, 2.62 in WebKit on the 2-page form, and 25.1× on a 2.4 KB letter in Chromium. The tool detects that and tells you to keep your original rather than announcing a win.
OCR is roughly twelve times slower in Firefox
| Engine | OCR, 100-page scan | Per page | Output |
|---|---|---|---|
| Chromium | 94.2 s | 0.94 s | valid 100-page PDF, 91,127 characters |
| WebKit | 88.9 s | 0.89 s | valid 100-page PDF, 91,464 characters |
| Firefox | 1179.3 s (19.7 min) | 11.8 s | valid 100-page PDF, 91,127 characters |
We recorded that cell as a hang first, and were wrong. A three-page control settled it — 36.4 s in Firefox against 4.1 s in Chromium for the same file — and a progress sample through the run showed a per-page counter, a bar backed by that counter and a live Stop button at every sample. OCR in Firefox is slow, not stuck, and it produced byte-for-byte the same recognised text.
Observed completions per operation class
The largest document each operation class was measured finishing, with the peak process memory recorded on that run, and that same peak with the engine's own idle floor subtracted (Chromium 407 MB, Firefox 1075 MB, WebKit 444 MB — Firefox carries roughly 660 MB more baseline before this product has done anything):
| Operation class | Engine | Largest completed | Peak | Minus browser floor |
|---|---|---|---|---|
| Editor | Chromium | 100 pages / 39.76 MB | 1466 MB | 1059 MB |
| Editor | Firefox | 300 pages / 118.99 MB | 1905 MB | 830 MB |
| Editor | WebKit | 500 pages / 99.6 MB | 1090 MB | 646 MB |
| Merge | Chromium | 100 pages / 39.76 MB | 896 MB | 489 MB |
| Merge | Firefox | 300 pages / 118.99 MB | 1620 MB | 545 MB |
| Merge | WebKit | 500 pages / 99.6 MB | 1237 MB | 793 MB |
| OCR | Chromium | 100 pages / 39.76 MB | 849 MB | 442 MB |
| OCR | Firefox | 100 pages / 39.76 MB | 1401 MB | 326 MB |
| OCR | WebKit | 100 pages / 39.76 MB | 1176 MB | 732 MB |
| Page images | Chromium | 1,000 pages | 1500 MB | 1093 MB |
| Page images | Firefox | 500 pages / 99.6 MB | 1609 MB | 534 MB |
| Page images | WebKit | 1,000 pages | 1463 MB | 1019 MB |
| Compress | Chromium | 300 pages / 118.99 MB | 949 MB | 542 MB |
| Compress | Firefox | 500 pages / 99.6 MB | 1598 MB | 523 MB |
| Compress | WebKit | 500 pages / 99.6 MB | 1336 MB | 892 MB |
| Organize / split | Chromium | 300 pages / 118.99 MB | 811 MB | 404 MB |
| Organize / split | Firefox | 300 pages / 118.99 MB | 1510 MB | 435 MB |
| Organize / split | WebKit | 300 pages / 118.99 MB | 1034 MB | 590 MB |
Read every memory figure above as a single sample and a lower bound. Peak process memory does not reproduce. A pdf-to-jpg cell repeated five times under identical conditions in Chromium read 684, 689, 627, 694 and 709 MB in that order — a spread of 13.1% — while wall time held steady. An independent four-run repeat of a different cell, a compress run in WebKit, spread 924–1681 MB (+82%) with byte-identical output across all four. These are the largest documents each operation was seen to finish, not the point at which anything stops. Nothing stopped.
Difficult documents: the intake door
A clean, named refusal is a pass here. Across 27 runs on damaged, encrypted, rotated, Chinese-script and structurally deep documents: zero silent hangs, zero crashes, zero "something went wrong".
| Document | Behaviour, on every engine that ran it |
|---|---|
| Password-protected | Refused in ≤ 0.6 s, 9 runs of 9. Two different messages, not one: the editor's 3 runs name the tool that fixes it — Unlock PDF — while compress and organize (6 runs) name the reason and the action but not the tool. All 9 are actionable; only 3 of 9 point at the tool. |
| Truncated cross-reference table (poppler cannot open it at all) | Opened and processed on all three engines, 9 runs of 9. The repair route recovered 22 pages; compress produced a valid 22-page PDF, merge a valid 23-page one. You are never asked to know the file was damaged — Repair PDF exists for when you do. |
| Mixed page rotations | 3 thumbnails, 3 JPEG entries, valid 3-page re-save. |
| Chinese text, CID-keyed embedded fonts | Compressed output still yields 2,988 non-space characters through poppler — the text layer survives the re-save. |
| 756 pages, deep bookmark tree, owner-restricted encryption | Opens rather than refusing — an owner-password-only file with an empty user password is correctly treated as readable. First paint ≤ 516 ms, 200 thumbnails, all 756 pages intact after compression. |
Real devices — two phones, run by hand
These are the only non-desktop rows that exist. They were driven by hand by the site's author on 2026-08-13 against the live site, on bundles older than the one the desktop matrix measured, with a stopwatch and a human eye. The bounds are coarse because the method was coarse, and they are reproduced with those bounds intact.
| Device | Test | Result |
|---|---|---|
| iPhone 14, Safari | Editor open, 100-page 40 MB scan | Page 1 visible within 10–20 s; no crash, no reload |
| iPhone 14, Safari | Compress Balanced, 100-page scan | Completed fast, sizes shown; no memory-kill banner |
| iPhone 14, Safari | Compress Balanced, 300-page 119 MB scan | Completed in 30–40 s — no white flash, no reload |
| iPhone 14, Safari | Page images + ZIP, 100-page scan | Completed in 10–20 s, ZIP download landed |
| iPhone 14, Safari | Editor open, 1,000 pages | Page 1 visible, but scrolling "like hanging" — jank, not failure |
| Xiaomi (model not recorded), Chrome | Editor open, 100-page scan | Page 1 in 30–40 s; scrolling "okay, not slow or fast"; no crash |
| Xiaomi, Chrome | Compress Balanced, 100-page scan | Completed; 40–60 s |
| Xiaomi, Chrome | Compress Balanced, 300-page 119 MB scan | Completed, 60–100 s — no crash, no reload |
| Xiaomi, Chrome | Page images + ZIP, 100-page scan | Images under 60 s, conversion under 60 s, completed |
| Xiaomi, Chrome | Editor open, 1,000 pages | Loaded 10–20 s; scroll slow on that bundle |
What these rows license: both handsets completed a compression of a 119 MB, 300-page scan, and neither hit a memory wall at that size. What they do not license: any figure for a phone not in that table, any timing tighter than those bounds, or any memory number for any phone — no memory measurement exists for any phone at all. The dominant problems found on phones were interface and speed, not crashes. Phone-specific walkthroughs live in the iPhone guide and the Android guide.
What we still cannot tell you
The benchmark was written as a gate on this page, with a pass/fail verdict against fourteen requirements. Nine passed — one of those nine, prompt cancellation, is a pass cited from a separate suite rather than re-measured in this matrix. Here are all five that did not, in the benchmark's own words.
| Requirement | Verdict | Why |
|---|---|---|
| Heavy work moved to its own thread wherever possible | FAIL | No first-party worker is enabled on any of the 33 PDF tools in the 15 August 2026 matrix. The one that was built ships off, and its benchmark is why. |
| Unsafe tasks detected before a crash | UNPROVEN | Nothing has ever crashed — 124 runs here, 20 before. A pre-crash detector cannot be validated against a crash that has never been produced, and desktop browser flags cannot manufacture one. The logic exists and is unit-tested; its thresholds are uncalibrated. |
| Low-memory mode materially reduces failure risk | UNMEASURABLE | The mode exists and is described above. The claim about its effect cannot be tested against a measured failure rate of zero. |
| The interface stays usable during processing | PARTIAL | Measured on desktop only, on three operation classes only. No mobile measurement exists, and the instrument that would have measured visual smoothness had to be discarded. |
| Output is verified before success is shown | PARTIAL | The four guided chains verify on the output bytes. The 33 individual tool pages do not — poppler checked those externally, afterwards. |
And what was never measured at all, stated as an absence rather than left as an implication:
- Mid-range laptops — the class most likely to actually struggle. Not run. No such machine was available. Nothing on this page describes an 8 GB Intel laptop.
- Mid-range Android. Not run. The Xiaomi handset's model and Android version were never recorded, so it cannot even be classified.
- Instrumented phone runs. Not possible with this harness. The phone rows are human observation: no memory sampling, no per-phase timing, no output verification.
- iOS Safari's desktop-class mode. Never tested. WebKit on macOS is not a proxy for it.
- Edge, and any browser on Windows or Linux. Not run. Headless Chromium is not Chrome on Windows.
- Several tabs or several operations at once — which is how people actually run out of memory. Not run.
- A genuine allocation failure. Never observed, in 144 instrumented runs. The branch that handles one is proven only against injected errors.
- Peak memory as a distribution. Not characterised. No cell has enough samples to state one.
- The current bundle on any phone. Not run — the phone rows are older builds.
Twenty-seven of the 33 tools in that matrix have no large-document benchmark of their own and are recorded as unknown rather than inheriting a neighbour's numbers. Which six are measured, and what each one's record says, is in capabilities.json.
Method, and how to check it
Twelve fixtures across three classes — ordinary documents, large documents, and deliberately difficult ones — driven through the real interface exactly as a person drives it: file input, action button, then wait for the tool's own result panel or the tool's own error panel. An error panel is a result, not a harness failure, which is why a named refusal is recorded as a named refusal and only a genuinely silent wait becomes a timeout.
Memory is peak resident set size of the whole browser process tree, sampled every 500 ms, matched to that engine's own process tree — a run that samples zero is recorded as instrumentation failure, never as 0 MB. We learned that the hard way: the browser's own JavaScript-heap metric read 4.7 MB while a tab held a 119 MB PDF, because PDF bytes, decoded images and canvas backing stores live outside it. Output validity is checked with poppler, chosen precisely because it shares no code with what this site ships.
Every table above regenerates from a machine-readable record of all 124 runs, and the run plan can be printed without running anything. The full write-up, including all 68 deliberately pruned matrix cells with the rule that pruned each one, is the source this page is drawn from; the per-tool summary is published as capabilities.json and the dated account of what changed is in the changelog. If you would rather check the privacy claim than the performance claim, the Privacy Lab is a method you can run yourself in a browser you control, and About explains what we do and do not promise.
Related
PDF Privacy Lab · Why in-browser is faster · Compare with upload tools · Changelog · All 43 tools · All guides
Frequently asked questions
Does UnboundPDF use worker threads of its own?
No. Two vendor libraries bring their own — pdf.js parses in its worker, Tesseract recognises in its — but no first-party worker is enabled on any of the 33 PDF tools in the 15 August 2026 matrix, and the build refuses to emit the capability registry if a row claims one. The one we did build ships switched off, because its own benchmark showed no improvement and 818–1008 MB more peak memory.
How big a document have you actually seen finish?
On 2026-08-15, on one Apple M4 desktop: a 300-page 119 MB scan compressed, merged, exported to JPEGs and opened in the editor on all three engines; a 500-page 99.6 MB mixed document likewise; a 1,000-page document opened, merged and exported. Those are documents we watched operations finish, not a point where anything stopped — nothing stopped.
Why does compression give a different result in a different browser?
Photographs above 150 DPI for their page box are re-encoded with the browser's own JPEG encoder, and Skia, Gecko and ImageIO produce different bytes at the same nominal quality. On the same 119 MB scan on 2026-08-15, Balanced returned a ratio of 0.342 in Chromium, 0.387 in Firefox and 0.565 in WebKit. Documents with no oversized photographs come out identical on all three.
What happens if my device runs out of memory?
In 124 measured runs on three browser engines on 15 August 2026, plus 20 in an earlier matrix, no run produced an allocation failure; before a large job starts, the tool checks the document against your device and tells you what it found. The branch that handles one has so far been tested only against injected errors. What is measured is that no run modified its input file.
Can I stop a long run?
Yes, and the tool tells you what stopping means for that operation: within the current item, after the current page finishes, or not during the final rebuild. Nothing resumes — every tool is recorded as restart-required, and no tool anywhere claims resumability.