@squoosh-kit/mozjpeg 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +276 -0
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# @squoosh-kit/mozjpeg
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/js/%40squoosh-kit%2Fmozjpeg)
|
|
4
|
+
[](https://bun.sh/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT) [](https://www.apache.org/licenses/LICENSE-2.0)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
## Squoosh-Kit
|
|
11
|
+
|
|
12
|
+
Squoosh-Kit is built on a simple idea: provide a lightweight and modular bridge to the powerful, production-tested codecs from Google's Squoosh project. This package (`@squoosh-kit/mozjpeg`) is one of those modules.
|
|
13
|
+
|
|
14
|
+
**Directly from the Source**
|
|
15
|
+
We don't modify the core MozJPEG codec. The WebAssembly (`.wasm`) binary is taken directly from the official Squoosh repository builds. This means you get the exact same performance, quality, and reliability you'd expect from Squoosh.
|
|
16
|
+
|
|
17
|
+
**A Thin, Modern Wrapper**
|
|
18
|
+
Our goal is to provide a minimal, modern JavaScript wrapper around the codec. We handle the tricky parts—like loading WASM, managing web workers, and providing a clean, type-safe API—so you can focus on your application. The library is designed to be a thin bridge, not a heavy framework.
|
|
19
|
+
|
|
20
|
+
**Modular by Design**
|
|
21
|
+
We believe you should only install what you need. As a standalone package, `@squoosh-kit/mozjpeg` allows you to add high-quality JPEG encoding and decoding to your project without pulling in other unrelated image processing tools.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
bun add @squoosh-kit/mozjpeg
|
|
27
|
+
# or
|
|
28
|
+
npm install @squoosh-kit/mozjpeg
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { encode, decode, createMozjpegEncoder } from '@squoosh-kit/mozjpeg';
|
|
35
|
+
import type { ImageInput, MozjpegEncodeOptions } from '@squoosh-kit/mozjpeg';
|
|
36
|
+
|
|
37
|
+
const imageData: ImageInput = {
|
|
38
|
+
data: imageBuffer,
|
|
39
|
+
width: 1920,
|
|
40
|
+
height: 1080,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Encode with default settings (quality 75, progressive)
|
|
44
|
+
const jpegBuffer = await encode(imageData);
|
|
45
|
+
|
|
46
|
+
// With custom quality
|
|
47
|
+
const highQuality = await encode(imageData, { quality: 90 });
|
|
48
|
+
|
|
49
|
+
// Decode a JPEG back to raw pixel data
|
|
50
|
+
const rawImage = await decode(jpegBuffer);
|
|
51
|
+
|
|
52
|
+
// For multiple images, create a persistent encoder
|
|
53
|
+
const encoder = createMozjpegEncoder('worker');
|
|
54
|
+
const result = await encoder(imageData, { quality: 85, progressive: true });
|
|
55
|
+
await encoder.terminate();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## What is MozJPEG?
|
|
59
|
+
|
|
60
|
+
MozJPEG is Mozilla's improved JPEG encoder. It produces smaller JPEG files than standard libjpeg at the same visual quality, typically 10–20% smaller with no perceptible difference. It's widely used in image optimization pipelines and is a drop-in replacement for standard JPEG encoding.
|
|
61
|
+
|
|
62
|
+
## Public API
|
|
63
|
+
|
|
64
|
+
Only the following exports are part of the public API and guaranteed to be stable across versions:
|
|
65
|
+
|
|
66
|
+
- `encode(imageData, options?, signal?)` - Encode an image to JPEG format using MozJPEG
|
|
67
|
+
- `decode(data, signal?)` - Decode a JPEG file to raw pixel data
|
|
68
|
+
- `createMozjpegEncoder(mode?)` - Create a reusable encoder function
|
|
69
|
+
- `createMozjpegDecoder(mode?)` - Create a reusable decoder function
|
|
70
|
+
- `ImageInput` type - Input image data structure
|
|
71
|
+
- `MozjpegEncodeOptions` type - MozJPEG encoding configuration
|
|
72
|
+
- `MozjpegEncoderFactory` type - Type for reusable encoder functions
|
|
73
|
+
- `MozjpegDecoderFactory` type - Type for reusable decoder functions
|
|
74
|
+
|
|
75
|
+
## Real-World Examples
|
|
76
|
+
|
|
77
|
+
**Upload handler with timeout**
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
const controller = new AbortController();
|
|
81
|
+
const timeout = setTimeout(() => controller.abort(), 30000);
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const jpeg = await encode(
|
|
85
|
+
uploadedImage,
|
|
86
|
+
{
|
|
87
|
+
quality: 85,
|
|
88
|
+
progressive: true, // Loads progressively in browsers
|
|
89
|
+
},
|
|
90
|
+
controller.signal
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
await saveToStorage('photo.jpg', jpeg);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if (error.name === 'AbortError') {
|
|
96
|
+
console.log('Encoding timed out');
|
|
97
|
+
}
|
|
98
|
+
} finally {
|
|
99
|
+
clearTimeout(timeout);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Batch server-side JPEG optimization**
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
const encoder = createMozjpegEncoder('client'); // Direct encoding, no worker
|
|
107
|
+
|
|
108
|
+
for (const imagePath of imageFiles) {
|
|
109
|
+
const imageData = await loadImage(imagePath);
|
|
110
|
+
const jpegData = await encoder(imageData, {
|
|
111
|
+
quality: 80,
|
|
112
|
+
progressive: true,
|
|
113
|
+
optimize_coding: true,
|
|
114
|
+
});
|
|
115
|
+
await writeFile(`${imagePath}.jpg`, jpegData);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
await encoder.terminate();
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## API Reference
|
|
122
|
+
|
|
123
|
+
### `encode(imageData, options?, signal?)`
|
|
124
|
+
|
|
125
|
+
Encodes raw RGBA pixel data to JPEG format using MozJPEG.
|
|
126
|
+
|
|
127
|
+
**Note**: `encode()` uses a global singleton worker. For long-running applications where worker cleanup is important, use `createMozjpegEncoder()` instead.
|
|
128
|
+
|
|
129
|
+
- `imageData` - `ImageInput` object with your pixel data
|
|
130
|
+
- `options` - (optional) `MozjpegEncodeOptions` for quality and compression settings
|
|
131
|
+
- `signal` - (optional) `AbortSignal` to cancel the operation
|
|
132
|
+
- **Returns** - `Promise<Uint8Array>` with the encoded JPEG data
|
|
133
|
+
|
|
134
|
+
### `decode(data, signal?)`
|
|
135
|
+
|
|
136
|
+
Decodes a JPEG file back to raw RGBA pixel data.
|
|
137
|
+
|
|
138
|
+
- `data` - `BufferSource` containing the JPEG file bytes
|
|
139
|
+
- `signal` - (optional) `AbortSignal` to cancel the operation
|
|
140
|
+
- **Returns** - `Promise<ImageData>` with decoded pixel data, width, and height
|
|
141
|
+
|
|
142
|
+
### `createMozjpegEncoder(mode?)`
|
|
143
|
+
|
|
144
|
+
Creates a reusable encoder. More efficient for processing multiple images.
|
|
145
|
+
|
|
146
|
+
- `mode` - (optional) `'worker'` or `'client'`, defaults to `'worker'`
|
|
147
|
+
- **Returns** - A function with the same signature as `encode()`
|
|
148
|
+
|
|
149
|
+
### `createMozjpegDecoder(mode?)`
|
|
150
|
+
|
|
151
|
+
Creates a reusable decoder.
|
|
152
|
+
|
|
153
|
+
- `mode` - (optional) `'worker'` or `'client'`, defaults to `'worker'`
|
|
154
|
+
- **Returns** - A function with the same signature as `decode()`
|
|
155
|
+
|
|
156
|
+
## Cancellation Support
|
|
157
|
+
|
|
158
|
+
To cancel an encoding operation in progress, pass an `AbortSignal`:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
const controller = new AbortController();
|
|
162
|
+
|
|
163
|
+
const encodePromise = encode(imageData, { quality: 85 }, controller.signal);
|
|
164
|
+
setTimeout(() => controller.abort(), 5000);
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const result = await encodePromise;
|
|
168
|
+
} catch (error) {
|
|
169
|
+
if (error.name === 'AbortError') {
|
|
170
|
+
console.log('Encoding was cancelled');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Important**: If no signal is provided, the encoding operation cannot be cancelled.
|
|
176
|
+
|
|
177
|
+
## Input Validation
|
|
178
|
+
|
|
179
|
+
All inputs are automatically validated before processing:
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
// Will throw TypeError: image must be an object
|
|
183
|
+
await encode(null, { quality: 85 });
|
|
184
|
+
|
|
185
|
+
// Will throw TypeError: image.data must be Uint8Array or Uint8ClampedArray
|
|
186
|
+
await encode({ data: [0, 0, 0, 255], width: 32, height: 32 }, { quality: 85 });
|
|
187
|
+
|
|
188
|
+
// Will throw RangeError: image.data too small
|
|
189
|
+
await encode(
|
|
190
|
+
{ data: new Uint8Array(100), width: 800, height: 600 },
|
|
191
|
+
{ quality: 85 }
|
|
192
|
+
);
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Package Size
|
|
196
|
+
|
|
197
|
+
**Size breakdown:**
|
|
198
|
+
|
|
199
|
+
- JavaScript code: ~5-8KB gzipped
|
|
200
|
+
- TypeScript definitions: ~3KB
|
|
201
|
+
- WASM binaries: ~40-50KB gzipped
|
|
202
|
+
|
|
203
|
+
### Worker Cleanup
|
|
204
|
+
|
|
205
|
+
When using worker mode, always clean up the worker when done:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
const encoder = createMozjpegEncoder('worker');
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
const jpegData = await encoder(imageData, { quality: 85 });
|
|
212
|
+
} finally {
|
|
213
|
+
await encoder.terminate();
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**Note**: In client mode, `terminate()` is a no-op. It's always safe to call for consistency.
|
|
218
|
+
|
|
219
|
+
### `MozjpegEncodeOptions`
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
type MozjpegEncodeOptions = {
|
|
223
|
+
quality?: number; // 0–100, visual quality (default: 75)
|
|
224
|
+
baseline?: boolean; // Use baseline instead of progressive JPEG (default: false)
|
|
225
|
+
arithmetic?: boolean; // Use arithmetic coding (default: false)
|
|
226
|
+
progressive?: boolean; // Enable progressive encoding (default: true)
|
|
227
|
+
optimize_coding?: boolean; // Optimize Huffman coding tables (default: true)
|
|
228
|
+
smoothing?: number; // 0–100, pre-encode smoothing (default: 0)
|
|
229
|
+
color_space?: MozJpegColorSpace; // Color space: GRAYSCALE, RGB, YCbCr (default: YCbCr)
|
|
230
|
+
quant_table?: number; // Quantization table preset, 0–8 (default: 3)
|
|
231
|
+
trellis_multipass?: boolean; // Multi-pass trellis quantization (default: false)
|
|
232
|
+
trellis_opt_zero?: boolean; // Optimize trellis zero coefficients (default: false)
|
|
233
|
+
trellis_opt_table?: boolean; // Optimize trellis quantization table (default: false)
|
|
234
|
+
trellis_loops?: number; // Trellis quantization passes, 1–50 (default: 1)
|
|
235
|
+
auto_subsample?: boolean; // Auto chroma subsampling (default: true)
|
|
236
|
+
chroma_subsample?: number; // Chroma subsampling level (default: 2)
|
|
237
|
+
separate_chroma_quality?: boolean; // Enable separate chroma quality (default: false)
|
|
238
|
+
chroma_quality?: number; // 0–100, chroma quality when separate (default: 75)
|
|
239
|
+
};
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**Key options:**
|
|
243
|
+
|
|
244
|
+
- `quality` — Primary quality control. `75–85` is a good range for most photos.
|
|
245
|
+
- `progressive` — Progressive JPEGs load gradually in browsers (low-res → high-res). Usually a good default.
|
|
246
|
+
- `optimize_coding` — Slightly improves compression with no quality tradeoff. Leave enabled.
|
|
247
|
+
- `trellis_multipass` — More aggressive optimization pass; produces smaller files at the cost of encoding time.
|
|
248
|
+
|
|
249
|
+
## Performance Tips
|
|
250
|
+
|
|
251
|
+
- **Quality 75–85 is the sweet spot** — Near-indistinguishable from higher quality at significantly smaller file size
|
|
252
|
+
- **Use progressive for web** — Better user experience; browsers can show a rough preview immediately
|
|
253
|
+
- **Use client mode for batch jobs** — Avoids worker overhead in Node/Bun scripts
|
|
254
|
+
- **MozJPEG vs WebP vs AVIF** — MozJPEG is best for maximum JPEG compatibility; for modern browsers, WebP or AVIF will be smaller
|
|
255
|
+
|
|
256
|
+
## Encoding Quality & File Size
|
|
257
|
+
|
|
258
|
+
MozJPEG produces consistently smaller files than standard JPEG encoders at the same quality setting:
|
|
259
|
+
|
|
260
|
+
- **Quality 80–100** — High fidelity, minimal artifacts
|
|
261
|
+
- **Quality 60–80** — Good quality for most photos, significant size savings
|
|
262
|
+
- **Quality 40–60** — Visible compression artifacts; useful only for thumbnails or previews
|
|
263
|
+
- **Quality 0–40** — Heavy compression; noticeable degradation
|
|
264
|
+
|
|
265
|
+
At quality 80, MozJPEG files are typically **10–20% smaller** than libjpeg output at the same setting.
|
|
266
|
+
|
|
267
|
+
## Works With
|
|
268
|
+
|
|
269
|
+
- **Bun** - First-class support, fastest performance
|
|
270
|
+
- **Node.js** - Works great in server environments
|
|
271
|
+
- **Browsers** - Full Web Worker support for responsive UIs
|
|
272
|
+
- **TypeScript** - Complete type definitions included
|
|
273
|
+
|
|
274
|
+
## License
|
|
275
|
+
|
|
276
|
+
MIT - use it freely in your projects
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@squoosh-kit/mozjpeg",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MozJPEG codec for squoosh-kit.",
|
|
6
6
|
"author": "Bartosz Nowak <bnowak008@gmail.com>",
|
|
@@ -47,6 +47,6 @@
|
|
|
47
47
|
"test": "bun test"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@squoosh-kit/runtime": "
|
|
50
|
+
"@squoosh-kit/runtime": "0.2.4"
|
|
51
51
|
}
|
|
52
52
|
}
|