Optimizing Images at Scale — Our Sharp Pipeline
How our queue, processors, and presets work together to deliver fast, predictable bulk optimization.
This post explains how the app achieves fast, predictable results when optimizing many images at once. We’ll look at the queue, client processors, server routes, and the decisions behind quality presets.
High-level flow
- Files are validated client-side and enqueued with a concurrency limit.
- A processor submits a
FormDatarequest to a server route (Sharp). - The route returns a binary response (with headers for size/quality), and the UI updates progress.
- Users can download single results or ZIP all.
The queue
We use a small concurrency queue that supports abort. This prevents the browser from flooding the server while keeping the UI responsive. Each task exposes run(signal) and can be cancelled at any time.
Processors
Each tool (optimize, compress, resize, convert, crop) has a matching processor function. Processors are typed, registered, and consumed via a central registry, which keeps the architecture modular and testable.
A minimal example call (client-side concept):
async function optimize(file: File, options: { quality?: number; format?: string }) {
const form = new FormData();
form.set("file", file);
if (options.quality != null) form.set("quality", String(options.quality));
if (options.format) form.set("format", options.format);
const res = await fetch("/api/image/optimize", { method: "POST", body: form });
if (!res.ok) throw new Error("Optimize failed");
const blob = await res.blob();
return blob;
}
Sharp at the edge of UX
On the server, Sharp performs the heavy lifting:
- Tune encoders by format (e.g., WebP/AVIF for best web savings).
- Strip metadata when requested.
- Return consistent headers so the UI can display before/after sizes and quality.
Quality & presets
We expose a quality control with sensible defaults/presets. The exact ranges are governed centrally (via constants and env), so documentation and UI always reflect reality without hardcoding limits inside content.
Guidelines:
- Web content: start between 60–80%
- Social: 70–85%
- Print: 90%+
Adjust per format as needed. The presets ensure teams can pick from a short list of good options and move on.
Why it feels fast
- Concurrency cap avoids network thrash and improves perceived speed.
- Binary responses + streaming reduce overhead.
- Modular processors keep code paths short and focused.
What’s next
- Per-post OG images for blog content.
- Live before/after previews where relevant.
- Optional per-format advanced controls for power-users.
Thanks for reading! If you have ideas or requests, head to our Contact page or open a feedback form from the UI.