Two product photos go through the same export settings: WebP, the same dimensions, quality 80.
One fits comfortably inside an upload limit. The other does not.
That result is not necessarily a broken encoder. A quality setting is an input to compression, while file size is an output you measure afterward. Image content matters, too.
This becomes a product problem when an interface turns “quality 80” into a promise that every file will be under 200 KB. The user does not care which parameter the encoder received. They care whether the exported file works in the receiving system and still looks acceptable.
Here is a reference workflow for treating those as two separate requirements. The examples are proposed engineering patterns, not a description of BatchSet's internal compression algorithm.
1. Give every number a specific meaning
A compression interface can expose several numbers that look related but measure different things:
| Number | What it describes | What it does not establish |
|---|---|---|
| Quality setting | A control passed to a particular encoder | A universal visual quality percentage |
| Output bytes | The encoded file's measured size | Whether fine text remains readable |
| Pixel dimensions | The output raster's width and height | A guaranteed byte count |
| Percentage saved | Change relative to one input file | Page-load time or upload acceptance |
“80% quality” does not mean that 80% of the original information is preserved. Nor should you expect quality 0.8 in two formats or two different encoders to produce equivalent images.
MDN's canvas export reference describes a quality argument for formats that support lossy encoding. It is an encoder option, not a target-size argument.
For a byte-limited upload, the final decision must use the encoded output. A preview or an estimate cannot replace that measurement.
2. Write down the acceptance criteria first
Suppose a team agrees on this illustrative requirement:
- The receiving system accepts WebP.
- Every delivered file must be at most 200,000 bytes.
- Product labels must remain readable at the intended display size.
- The first pass keeps approved dimensions unchanged.
- If quality adjustments cannot satisfy both conditions, request review.
These are project choices, not universal web standards.
Also settle whether “200 KB” means 200,000 bytes or 200 × 1024 bytes. The difference can determine whether a file just over the boundary is accepted. Internally, keep the limit as an integer byte count and make the displayed unit clear.
An uploader might have other requirements: allowed MIME types, minimum dimensions, or a server that recompresses files after upload. A local byte check addresses only the local file-size condition.
3. Measure a bounded set of candidates
I would begin with a small, explicit set of quality candidates rather than an unbounded loop that keeps reducing quality until something fits.
The following function accepts an injected encoder. It tries higher settings first and returns the first candidate inside the budget. If none fits, it returns the smallest tested candidate with an explicit failure status.
async function chooseWithinBudget({
encode,
expectedType,
maxBytes,
qualities = [0.9, 0.8, 0.7, 0.6, 0.5],
}) {
if (typeof encode !== "function" || !expectedType) {
throw new TypeError("An encoder and expected MIME type are required");
}
if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0) {
throw new RangeError("maxBytes must be a positive safe integer");
}
if (!qualities.length || qualities.some(q =>
!Number.isFinite(q) || q < 0 || q > 1
)) {
throw new RangeError("Invalid quality candidates");
}
const candidates = [...new Set(qualities)].sort((a, b) => b - a);
let smallest;
for (const quality of candidates) {
const blob = await encode(quality);
if (!(blob instanceof Blob) || blob.size === 0) {
throw new Error("Encoder returned no usable output");
}
if (blob.type !== expectedType) {
throw new Error(`Unexpected export type: ${blob.type}`);
}
const result = { blob, quality, bytes: blob.size };
if (!smallest || result.bytes < smallest.bytes) smallest = result;
if (result.bytes <= maxBytes) {
return { ...result, metTarget: true };
}
}
return { ...smallest, metTarget: false };
}
This is a bounded search over selected settings. It does not find the best possible image among all settings, and a higher encoder setting does not prove better perceptual quality for every use case.
It also does not assume that every successive output must be smaller. An unexpected increase is still a real measurement, so the smallest tested candidate is tracked separately.
The application must supply an encoder, handle its errors, and perform visual review. Cancellation, decoding, resizing, and memory management are outside this small function.
4. Verify the output format before assigning a filename
An application might request WebP and then name the result product.webp. That filename is only correct if the export actually produced WebP.
Canvas can fall back to PNG when a requested encoding is unsupported. Handle that before putting the result into an archive or sending it to a destination that only accepts certain formats.
For a canvas integration, wrap its callback-based export and reject a null result. Then verify the returned Blob's type against the requested format, as the reference function does.
Checking the type reported by your own browser encoder is a useful integration check. It is not a general-purpose file validator for arbitrary untrusted uploads. Those are different boundaries.
Similarly, treat an encoding error as an error. Replacing it with an empty Blob can make the byte budget look satisfied while delivering an unusable image.
5. Use a quality floor that matches the image's purpose
A file can meet its byte budget and still fail its job.
For a photograph, I might inspect fabric texture, hair, gradients, and edges. For a screenshot, I would inspect small text and thin interface lines. For a product label, the identifying text may be the most valuable part of the image.
That suggests a review sequence:
- Compare the original and candidate at the intended display size.
- Inspect important details at a larger zoom.
- Check the output on representative destination backgrounds.
- Reject candidates that lose information the recipient needs.
The “quality floor” is a decision about acceptable output, not merely a minimum slider value. A numerical floor can limit the search, but it still needs validation against representative images.
Automated similarity metrics can help flag changes in a controlled pipeline. They do not know that a tiny serial number matters more than a large blank background unless your evaluation makes that distinction.
6. Change dimensions deliberately when quality alone is insufficient
If no acceptable candidate fits, a second pass can consider smaller dimensions. Do not reduce dimensions silently and then show only a successful byte count.
For example, a proposed workflow might offer:
No tested quality setting met the limit at the current dimensions. Try a smaller export, choose another accepted format, or keep the original for manual review.
Reducing both width and height to 75% produces 56.25% as many pixels. That arithmetic does not predict the encoded file size: compression still depends on image content and representation.
Resize from the original working source for each candidate. Repeatedly decoding and recompressing an already lossy candidate makes it harder to compare alternatives fairly.
Keep any minimum dimensions required by the recipient. A 40 KB thumbnail is not a successful replacement for a full-size image someone needs to zoom into.
7. Make the report explain the result
A useful export report would show the selected format, dimensions, measured bytes, tested setting, and whether the target was met.
Use literal statuses such as:
- Within byte limit — visual review required.
- Above byte limit — smallest tested candidate retained.
- Encoding failed — no output produced.
These distinguish a measurement from a judgment and a failed operation from an oversized but valid file.
For batch statistics, add input bytes and output bytes first, then compute the total change. Averaging each file's percentage saved gives every tiny icon the same weight as a large photograph and can tell a different story.
Also show increases honestly. If a particular file grew, do not clamp its “saved” percentage to zero or quietly drop it from the report.
Start with measured compression
BatchSet's Bulk Image Compressor offers browser-based batch compression, quality controls, before-and-after sizes, and ZIP export. The free workflow uses a quality slider; target file-size mode is a Pro option.
Use the exported bytes and a visual check to judge the result. A quality setting is a starting point. A usable file inside the receiving system's actual limit is the outcome to verify.
Top comments (0)