Processing stages explained
From the moment you drop a file in, your upload moves through five stages. Here is what each one does, what you see while it runs, and how the pipeline works under the hood.
The five stages
Every file you upload moves through the same processing pipeline, shown as the segmented progress bar under the file row. Each segment is one stage, timed separately, and the bar only reports Complete once the server has confirmed the file is ready.
Processing completes on every plan — including Free. The only exception is the last stage, Pixels, which is a paid feature.
Upload
Your browser uploads the file directly to cloud storage using a short-lived presigned URL, so the bytes never queue up on our servers.
This is the only segment driven by your real transfer speed — the fill level is your browser’s actual byte progress, so larger files visibly take longer here.
Range
StreamPDF verifies that the stored file supports HTTP byte-range requests — the ability to fetch just the bytes a page needs instead of the whole file.
This is the mechanism behind instant previews: the reader opens your PDF in about a second and streams the rest as you scroll.
Metadata
The server reads the object’s metadata — exact size and MIME type — and runs the plan checks for your account: the per-file size limit and your total storage quota.
A file that exceeds either limit fails at this stage with a clear reason on the progress bar, and the uploaded object is cleaned up automatically.
Pages
A cloud analysis pass inspects the PDF’s structure and per-page data: it extracts the total page count, checks it against your plan’s monthly page quota, and prepares the data the reader needs to render pages.
This is typically the longest stage. When it finishes, your file is already openable in the reader.
Pixels
PAIDThe final stage is the pixel-level fidelity pass — a precision-rendering step that makes the streamed preview match the original document pixel for pixel.
On paid plans it runs automatically for every upload. On the Free plan the segment is visible but locked: it never runs, and your standard preview is unaffected.
Learn more about pixel-level fidelity →Reading the progress bar
While processing runs, the bar expands under the file row: the header line shows a live description of the current stage and the total elapsed time, and each segment fills in its own real duration.
Stage widths reflect how long each stage actually took — a fast Range stage appears narrow next to a long Pages stage. Each stage is displayed for at least about two seconds so quick files stay readable instead of flashing by.
The final segment fills to 95% and holds there until the server confirms completion, so the bar never says done before your file actually is. On success the bar collapses to a single line: “Completed in X.Xs”.
When a stage fails
If a check fails — the file is too large for your plan, your storage or monthly page quota is exhausted, or the file can’t be parsed — the bar turns red at the failing stage and shows a human-readable reason.
Depending on the reason, the source file is either removed automatically or kept so you can delete it yourself and free the storage.
For developers
Request flow
POST /api/upload-url- Creates the task row (status uploading) and returns a short-lived presigned PUT URL for the R2 object key.
PUT (presigned)- The browser uploads bytes straight to R2 via XHR; the real upload percent drives stage 0 of the progress bar.
POST /api/upload-complete- HeadObject for size/MIME, then plan enforcement (maxFileSize, storageBytes) and the downstream pre-check call that extracts the page count and enforces the monthly page quota.
Pre-check artifacts- The pre-check result is stored on the task as pre_check_file; its presence is what marks the task ready for reading.
Task lifecycle
Status transitions live on the tasks table and mirror the stages above:
uploading- Task row created, bytes still in flight from the browser.
uploaded- R2 object confirmed (size/MIME persisted); plan size and storage checks passed.
ready- Pre-check completed, page count persisted, artifacts stored — the file is openable.
failed- failure_reason is set (e.g. file_too_large, storage_exceeded, page-quota codes); dependent R2 objects are cleaned up where applicable.
Progress model
The progress bar is a pure data model, decoupled from React, so it can be unit-tested and reused by any view:
STAGE_MIN_MS = 2000- Minimum wall-clock time per stage, so fast files keep a readable bar; elapsed time below it is padded for display only.
STAGE_HOLD_PERCENT = 95- The last stage fills to 95% and holds until the server confirms the task is ready, preventing a false complete state.
stageWeights()- Segment widths are proportional to real per-stage durations (with a floor so short stages don’t collapse visually).
createTerminalProgress()- Rebuilds canonical final states for rows restored from history: each executed stage occupies one minimum duration, so a free-plan file totals 8.0s and a paid one 10.0s.
pixelDone / startPixelRun()- Files processed on Free keep pixelDone = false; after upgrading, a manual run rebuilds the terminal state into “first four stages done, last stage running” and reuses the existing stage renderer.
FAQ
- Why does every stage take at least two seconds?
- A minimum display duration keeps the bar readable on small files — you can see each stage light up instead of the whole pipeline flashing past in a blur. It never delays your file: processing itself finishes as fast as the server allows.
- Why does the bar stop at 95%?
- The final segment holds at 95% until the server confirms the file is ready. This way the bar never claims completion before the processing pipeline actually does.
- Why is the Pixels segment locked for me?
- Pixel-level fidelity is a paid stage included with every paid plan. On Free it is shown but not executed — your preview works normally without it. See the pixel-level fidelity page for details.
- Can I close the tab while processing runs?
- Yes. Processing continues on the server. When you come back, the bar is rebuilt from the task’s server-side state: finished files show a canonical completed timeline, and in-flight tasks resume their stage progression.
Curious about the last stage?
Pixel-level fidelity is the only paid stage — everything above runs on every plan.