@poly67/core
v0.1.0
Published
Zero-dependency, browser-only engine that resizes, converts, and compresses images to meet strict upload requirements (e.g. "under 20KB, 200x200 JPEG") using Canvas/OffscreenCanvas.
Maintainers
Readme
@poly67/core
Framework-agnostic, zero-dependency engine that resizes, converts, and compresses images entirely in the browser so they satisfy strict upload requirements — the kind government portals and job application forms love to enforce ("JPEG, under 20KB, exactly 200x200px").
It is not a general-purpose compressor. Given a target size and/or dimensions, it runs a full pipeline — EXIF correction, crop, resize, format conversion, and a binary search over encode quality — until the output fits.
See the root README
for the full guide. For React bindings, use
@poly67/react.
Install
npm install @poly67/coreUsage
import { optimizeImage } from "@poly67/core";
const input = document.querySelector<HTMLInputElement>("#file")!;
const file = input.files![0];
const result = await optimizeImage(file, {
maxSizeKB: 20,
maxWidth: 200,
maxHeight: 200,
format: "jpeg",
});
console.log(result.file); // File — append to FormData and upload
console.log(result.sizeKB, result.width, result.height, result.quality);API
optimizeImage(file, options?)
function optimizeImage(file: File, options?: OptimizeOptions): Promise<OptimizeResult>;OptimizeOptions:
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| maxSizeKB | number | — | Target maximum output size in KB. |
| maxWidth | number | — | Maximum output width. Never upscales. |
| maxHeight | number | — | Maximum output height. Never upscales. |
| format | "jpeg" \| "png" \| "webp" | "jpeg" | Output format. |
| quality | number | 0.9 | Starting quality for lossy formats. |
| minQuality | number | 0.1 | Lower bound for the quality search. |
| background | string | "#ffffff" | Fill color used to flatten transparency. |
| preserveAspectRatio | boolean | true | Set false to stretch to the exact box. |
| fit | "contain" \| "cover" | "contain" | How the image fits maxWidth/maxHeight. |
| crop | {x,y,width,height} | — | Optional pixel rect to crop before resizing. |
| correctOrientation | boolean | true | Read and apply EXIF orientation. |
| fileName | string | — | Override the output File name. |
| signal | AbortSignal | — | Cancel an in-flight optimization. |
| onProgress | (p: OptimizeProgress) => void | — | Stage/attempt progress callback. |
| maxQualitySearchIterations | number | 8 | Cap on binary-search steps. |
OptimizeResult:
interface OptimizeResult {
file: File;
blob: Blob;
width: number;
height: number;
sizeKB: number;
originalSizeKB: number;
format: ImageFormat;
quality: number;
iterations: number;
}Errors
All failures throw/reject with ImageOptimizerError, which carries a
code: "ABORTED" | "INVALID_FILE" | "DECODE_FAILED" | "ENCODE_FAILED" | "UNSUPPORTED_FORMAT" | "UNKNOWN"
so you can branch without parsing messages:
import { ImageOptimizerError } from "@poly67/core";
try {
await optimizeImage(file, options);
} catch (error) {
if (error instanceof ImageOptimizerError && error.code === "ABORTED") {
return;
}
throw error;
}How target sizes are hit
- Decode via
createImageBitmap(falls back toHTMLImageElement). - Correct EXIF orientation.
- Apply an optional crop.
- Resize into
maxWidth/maxHeight(containorcover). - Encode; if
maxSizeKBis set and the format is lossy, binary-search quality betweenminQualityandqualityuntil the byte budget is met. - If quality alone can't reach the target (common for lossless
png, or very aggressive targets), progressively downscale and re-encode.
Browser support
Requires Blob, Canvas (or OffscreenCanvas), and ideally
createImageBitmap. All are broadly supported in evergreen browsers; on
older engines the library falls back to an HTMLImageElement + <canvas>
path automatically.
License
MIT
