Framework-agnostic image optimization library. Compress and convert images in the browser — no server, no dependencies.
ngx-media-optimizer is a TypeScript library that converts and compresses images entirely on the client side, using the browser's native OffscreenCanvas and createImageBitmap APIs. No server round-trips, no C++ WASM blobs, no third-party image libraries.
Formats supported: WebP · AVIF · JPEG · PNG
Works with Angular, React, Vue, and plain JavaScript. Zero runtime dependencies.
npm install ngx-media-optimizerFor the full API reference and framework-specific quick starts see the package README.
- Zero extra dependencies — native
OffscreenCanvasreplacesbrowser-image-compression - Parallel batch processing — auto-detected concurrency, configurable via
concurrencyoption - Binary-search quality — finds the highest quality that fits a
maxSizeMBbudget (5 iterations, ±2%) - Stepwise halving resize — avoids quality loss from a single large downscale
- Discriminated union state —
compressedUrl/compressedSizeonly accessible afterstatus === 'completed' - Reactive callbacks — framework-agnostic
onImagesChange/onUploadingChangepattern - ImageUtilsService — standalone utility for validation, analysis, thumbnails, and format probing
- 228 tests
import { ImageConverterService } from 'ngx-media-optimizer';
const svc = new ImageConverterService();
svc.onImagesChange(images => console.log(images));
await svc.convertFormat(fileList, {
outputFormat: 'webp',
quality: 80,
maxSizeMB: 1,
});| v1 | v2 | |
|---|---|---|
| Encoding engine | browser-image-compression (libvips) |
Native OffscreenCanvas |
| Dependencies | 1 | 0 |
getSupportedFormats() |
— | Probes actual browser codec support |
estimateCompressedSize() |
— | Fast synchronous heuristic |
getBestQuality() |
— | Binary-search quality for a target size |
hasTransparency() |
— | Useful before converting PNG → JPEG |
isAnimated() |
— | Detects animated GIF / WebP |
sortOrder option |
— | Sort batch by file size before processing |
useWebWorker |
Feature | Removed in v2.0.1 |
Breaking change: Encoding results will differ slightly from v1 because native codec quality curves differ from libvips. The API is otherwise unchanged, apart from method renames:
| v1 | v2 |
|---|---|
validateImage(file) |
isValidImage(file) |
shouldCompress(file, n) |
needsCompression(file, n) |
media-optimizer-workspace/
├── projects/
│ └── media-optimizer/ # The npm library
│ ├── src/lib/
│ │ ├── media-optimizer.service.ts # ImageConverterService
│ │ ├── image-utils.service.ts # ImageUtilsService
│ │ └── shared/
│ │ ├── image-codec.ts # NativeImageCodec (OffscreenCanvas pipeline)
│ │ ├── image-helpers.ts
│ │ ├── lru-cache.ts
│ │ ├── subject.ts # Zero-dep reactive primitive
│ │ └── types.ts
│ └── README.md # API docs published to npm
├── angular.json
├── rollup.config.mjs
└── CHANGELOG.md
git clone https://github.com/barbozaa/media-optimizer-workspace.git
cd media-optimizer-workspace
npm install| Command | What it does |
|---|---|
npm run build:lib |
Build + rollup the library into dist/media-optimizer/ |
npm run pack:lib |
Build + pack a local .tgz for testing |
npx vitest run |
Run the 228 tests |
npx vitest --watch |
Run tests in watch mode |
npx vitest run228 tests across image-utils.service.spec.ts and media-optimizer.service.spec.ts.
npm run build:lib
cd dist/media-optimizer
npm publishIssues and pull requests are welcome. Please open an issue first if you are planning a large change.
See CHANGELOG.md.
MIT © Barboza