@squoosh-kit/jxl 0.2.1 → 0.2.3

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.
Files changed (2) hide show
  1. package/README.md +276 -0
  2. package/package.json +2 -1
package/README.md ADDED
@@ -0,0 +1,276 @@
1
+ # @squoosh-kit/jxl
2
+
3
+ [![npm version](https://badge.fury.io/js/%40squoosh-kit%2Fjxl.svg)](https://badge.fury.io/js/%40squoosh-kit%2Fjxl)
4
+ [![Bun](https://img.shields.io/badge/Bun-000000?logo=bun&logoColor=white)](https://bun.sh/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
7
+
8
+ ![Squoosh-Kit](https://github.com/bnowak008/squoosh-kit/blob/main/squoosh-kit-banner.webp)
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/jxl`) is one of those modules.
13
+
14
+ **Directly from the Source**
15
+ We don't modify the core JPEG XL 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/jxl` allows you to add JPEG XL 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/jxl
27
+ # or
28
+ npm install @squoosh-kit/jxl
29
+ ```
30
+
31
+ ## Quick Start
32
+
33
+ ```typescript
34
+ import { encode, decode, createJxlEncoder } from '@squoosh-kit/jxl';
35
+ import type { ImageInput, JxlEncodeOptions } from '@squoosh-kit/jxl';
36
+
37
+ const imageData: ImageInput = {
38
+ data: imageBuffer,
39
+ width: 1920,
40
+ height: 1080,
41
+ };
42
+
43
+ // Encode with default settings
44
+ const jxlBuffer = await encode(imageData, { quality: 75 });
45
+
46
+ // With cancellation support
47
+ const controller = new AbortController();
48
+ const jxl = await encode(
49
+ imageData,
50
+ { quality: 75, effort: 7 },
51
+ controller.signal
52
+ );
53
+
54
+ // Decode JXL back to raw pixel data
55
+ const rawImage = await decode(jxlBuffer);
56
+
57
+ // For multiple images, create a persistent encoder
58
+ const encoder = createJxlEncoder('worker');
59
+ const result = await encoder(imageData, { quality: 80, progressive: true });
60
+ await encoder.terminate();
61
+ ```
62
+
63
+ ## What is JPEG XL?
64
+
65
+ JPEG XL (JXL) is a next-generation image format designed to replace JPEG. It offers:
66
+
67
+ - **Better compression than AVIF and WebP** at equivalent visual quality
68
+ - **Lossless mode** with better compression than PNG
69
+ - **Progressive decoding** for better web loading experience
70
+ - **JPEG recompression** — losslessly pack existing JPEGs into JXL with ~20% smaller files
71
+
72
+ Browser support is growing. JXL is currently supported in Safari 17+ and Chrome with a flag. For maximum compatibility, consider providing a JPEG/WebP fallback.
73
+
74
+ ## Public API
75
+
76
+ Only the following exports are part of the public API and guaranteed to be stable across versions:
77
+
78
+ - `encode(imageData, options?, signal?)` - Encode an image to JPEG XL format
79
+ - `decode(data, signal?)` - Decode a JXL file to raw pixel data
80
+ - `createJxlEncoder(mode?)` - Create a reusable encoder function
81
+ - `createJxlDecoder(mode?)` - Create a reusable decoder function
82
+ - `ImageInput` type - Input image data structure
83
+ - `JxlEncodeOptions` type - JPEG XL encoding configuration
84
+ - `JxlEncoderFactory` type - Type for reusable encoder functions
85
+ - `JxlDecoderFactory` type - Type for reusable decoder functions
86
+
87
+ ## Real-World Examples
88
+
89
+ **High-quality archival encoding**
90
+
91
+ ```typescript
92
+ const controller = new AbortController();
93
+ setTimeout(() => controller.abort(), 60000);
94
+
95
+ try {
96
+ const jxl = await encode(
97
+ photoData,
98
+ {
99
+ quality: 90, // High quality for archival
100
+ effort: 9, // Maximum compression effort
101
+ progressive: true,
102
+ },
103
+ controller.signal
104
+ );
105
+
106
+ await saveToStorage('archive.jxl', jxl);
107
+ } catch (error) {
108
+ if (error.name === 'AbortError') {
109
+ console.log('Encoding timed out — try a lower effort setting');
110
+ }
111
+ }
112
+ ```
113
+
114
+ **Fast batch conversion for web delivery**
115
+
116
+ ```typescript
117
+ const encoder = createJxlEncoder('client');
118
+
119
+ for (const imagePath of imageFiles) {
120
+ const imageData = await loadImage(imagePath);
121
+ const jxlData = await encoder(imageData, {
122
+ quality: 75,
123
+ effort: 4, // Faster encoding for bulk conversion
124
+ });
125
+ await writeFile(`${imagePath}.jxl`, jxlData);
126
+ }
127
+
128
+ await encoder.terminate();
129
+ ```
130
+
131
+ ## API Reference
132
+
133
+ ### `encode(imageData, options?, signal?)`
134
+
135
+ Encodes raw RGBA pixel data to JPEG XL format.
136
+
137
+ **Note**: `encode()` uses a global singleton worker. For long-running applications where worker cleanup is important, use `createJxlEncoder()` instead.
138
+
139
+ - `imageData` - `ImageInput` object with your pixel data
140
+ - `options` - (optional) `JxlEncodeOptions` for quality and compression settings
141
+ - `signal` - (optional) `AbortSignal` to cancel the operation
142
+ - **Returns** - `Promise<Uint8Array>` with the encoded JXL data
143
+
144
+ ### `decode(data, signal?)`
145
+
146
+ Decodes a JXL file back to raw RGBA pixel data.
147
+
148
+ - `data` - `BufferSource` containing the JXL file bytes
149
+ - `signal` - (optional) `AbortSignal` to cancel the operation
150
+ - **Returns** - `Promise<ImageData>` with decoded pixel data, width, and height
151
+
152
+ ### `createJxlEncoder(mode?)`
153
+
154
+ Creates a reusable encoder. More efficient for processing multiple images.
155
+
156
+ - `mode` - (optional) `'worker'` or `'client'`, defaults to `'worker'`
157
+ - **Returns** - A function with the same signature as `encode()`
158
+
159
+ ### `createJxlDecoder(mode?)`
160
+
161
+ Creates a reusable decoder.
162
+
163
+ - `mode` - (optional) `'worker'` or `'client'`, defaults to `'worker'`
164
+ - **Returns** - A function with the same signature as `decode()`
165
+
166
+ ## Cancellation Support
167
+
168
+ To cancel an encoding operation in progress, pass an `AbortSignal`:
169
+
170
+ ```typescript
171
+ const controller = new AbortController();
172
+
173
+ const encodePromise = encode(imageData, { quality: 75 }, controller.signal);
174
+ setTimeout(() => controller.abort(), 10000);
175
+
176
+ try {
177
+ const result = await encodePromise;
178
+ } catch (error) {
179
+ if (error.name === 'AbortError') {
180
+ console.log('Encoding was cancelled');
181
+ }
182
+ }
183
+ ```
184
+
185
+ **Important**: If no signal is provided, the encoding operation cannot be cancelled.
186
+
187
+ ## Input Validation
188
+
189
+ All inputs are automatically validated before processing:
190
+
191
+ ```typescript
192
+ // Will throw TypeError: image must be an object
193
+ await encode(null, { quality: 75 });
194
+
195
+ // Will throw TypeError: image.data must be Uint8Array or Uint8ClampedArray
196
+ await encode({ data: [0, 0, 0, 255], width: 32, height: 32 }, { quality: 75 });
197
+
198
+ // Will throw RangeError: image.data too small
199
+ await encode(
200
+ { data: new Uint8Array(100), width: 800, height: 600 },
201
+ { quality: 75 }
202
+ );
203
+ ```
204
+
205
+ ### Package Size
206
+
207
+ **Size breakdown:**
208
+
209
+ - JavaScript code: ~5-8KB gzipped
210
+ - TypeScript definitions: ~3KB
211
+ - WASM binaries: ~50-70KB gzipped (multi-threaded and SIMD variants included)
212
+
213
+ ### Worker Cleanup
214
+
215
+ When using worker mode, always clean up the worker when done:
216
+
217
+ ```typescript
218
+ const encoder = createJxlEncoder('worker');
219
+
220
+ try {
221
+ const jxlData = await encoder(imageData, { quality: 75 });
222
+ } finally {
223
+ await encoder.terminate();
224
+ }
225
+ ```
226
+
227
+ **Note**: In client mode, `terminate()` is a no-op. It's always safe to call for consistency.
228
+
229
+ ### `JxlEncodeOptions`
230
+
231
+ ```typescript
232
+ type JxlEncodeOptions = {
233
+ effort?: number; // 1–10, encoding effort (default: 7, lower = faster)
234
+ quality?: number; // 0–100, visual quality (default: 75)
235
+ progressive?: boolean; // Enable progressive decoding (default: false)
236
+ epf?: number; // -1–3, edge-preserving filter (-1 = auto, default: -1)
237
+ lossyPalette?: boolean; // Lossy palette optimization (default: false)
238
+ decodingSpeedTier?: number; // 0–4, optimize for faster decoding (default: 0)
239
+ photonNoiseIso?: number; // 0–50000, add film grain noise (default: 0)
240
+ lossyModular?: boolean; // Use lossy modular mode (default: false)
241
+ };
242
+ ```
243
+
244
+ **Key options:**
245
+
246
+ - `quality` — Primary quality control. `75` is a good default. JXL typically achieves better visual quality than AVIF at the same setting.
247
+ - `effort` — Encoding effort. `1` = fastest (larger files); `10` = maximum compression (very slow). `7` is a practical default.
248
+ - `progressive` — Enables progressive decoding, allowing browsers to show a rough preview before the full image loads.
249
+ - `decodingSpeedTier` — Sacrifice some file size for faster client-side decoding. Useful when targeting lower-end devices.
250
+
251
+ ## Performance Tips
252
+
253
+ - **JXL encodes slower than WebP** — Use effort 4–7 for interactive pipelines; effort 9–10 only for archival
254
+ - **Use workers for UI apps** — Encoding at high effort can take 10+ seconds for large images
255
+ - **JXL at quality 75 ≈ WebP at quality 85** — JXL achieves better compression at the same perceptual quality
256
+ - **Progressive mode costs little** — Enable it for web delivery with minimal size penalty
257
+
258
+ ## Encoding Quality & File Size
259
+
260
+ - **Quality 85–100** — Near-lossless quality
261
+ - **Quality 65–85** — Excellent for general photography
262
+ - **Quality 50–65** — Good for thumbnails and previews
263
+ - **Quality 0–50** — Heavy compression; visible artifacts
264
+
265
+ At quality 75 with effort 7, JXL files are typically **20–35% smaller** than equivalent-quality WebP.
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 (JXL decode support varies by browser)
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/jxl",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "type": "module",
5
5
  "description": "JPEG XL codec for squoosh-kit.",
6
6
  "author": "Bartosz Nowak <bnowak008@gmail.com>",
@@ -10,6 +10,7 @@
10
10
  "url": "https://github.com/bnowak008/squoosh-kit.git",
11
11
  "directory": "packages/jxl"
12
12
  },
13
+ "homepage": "http://squoosh-kit.dev",
13
14
  "publishConfig": {
14
15
  "access": "public",
15
16
  "registry": "https://registry.npmjs.org/"