libjpeg-turbo for WebAssembly
v3.2.0WebAssemblylibjpeg-turbo 3.2.0 for browsers, Node.js and edge runtimes, precompiled for wasm32, single-threaded and multi-threaded as @crossbind/port-jpegturbo-wasm.
Install
crossbind itself arrives with your bundler plugin, or with a new project from npm create crossbind@beta; Bundlers has Vite, Webpack, Rspack and Rollup.
Usage
Each example runs here, in this tab, and prints what the site build checked.
Each example also has a JavaScript only tab: the same task with no C++ file, calling libjpeg-turbo's own headers from @crossbind/port-jpegturbo directly. None of them work that way; each tab says what stops it.
Encode pixels to a JPEG
jpeg_mem_dest writes the file into memory; jpeg_set_quality and the luma sampling factors decide how much detail it keeps.
@crossbind/port-jpegturbo/jpeglib.h fails the build today: crossbind binds struct fields that jpeglib.h declares only under #if JPEG_LIB_VERSION >= 70 (the port has 62), and compiling the bindings stops at "no member named 'min_DCT_h_scaled_size' in 'jpeg_compress_struct'". With that bug patched out, the encoder stays out of reach: of the jpeg_compress_struct fields it sets, only input_components is bound (image_width, image_height, in_color_space, next_scanline and comp_info are typedef, enum or pointer fields, which are skipped, and image_width reads undefined), and nothing binds the err pointer, so jpeg_start_compress ends in RuntimeError: null function when libjpeg calls its error manager. TurboJPEG, the API without structs (tj3Compress8), is not in the port.Decode a JPEG and read its header
jpeg_mem_src reads the file from memory: jpeg_read_header answers what the image is before a pixel is decoded, and jpeg_read_scanlines decodes it to RGBA. A file libjpeg-turbo cannot read throws its message.
@crossbind/port-jpegturbo/jpeglib.h fails the build today: crossbind binds struct fields that jpeglib.h declares only under #if JPEG_LIB_VERSION >= 70 (the port has 62), and compiling the bindings stops at "no member named 'min_DCT_h_scaled_size' in 'jpeg_compress_struct'". With that bug patched out, reading the header fails: jpeg_read_header on a valid 640x480 JPEG ends in RuntimeError: null function, because libjpeg reports through cinfo->err and nothing binds that pointer. Of the header fields only num_components is bound (image_width reads undefined; image_height, jpeg_color_space, out_color_space, output_width, output_height and output_scanline are typedef or enum fields, which are skipped). The error line could not come back either: even with an error manager in place, libjpeg's default error_exit prints the message to stderr and calls exit(1), which reaches JavaScript only as "Program terminated with exit(1)".Decode straight to a thumbnail
Set scale_num and scale_denom before decoding and libjpeg-turbo runs a smaller inverse DCT on every block: a 1/8 decode never builds the full-size image.
@crossbind/port-jpegturbo/jpeglib.h fails the build today: crossbind binds struct fields that jpeglib.h declares only under #if JPEG_LIB_VERSION >= 70 (the port has 62), and compiling the bindings stops at "no member named 'min_DCT_h_scaled_size' in 'jpeg_compress_struct'". With that bug patched out, jpeg_read_header ends in RuntimeError: null function (libjpeg reports through cinfo->err, and nothing binds that pointer), and the scaling itself is out of reach: scale_num and scale_denom share one declaration, which crossbind skips, and output_width and output_height are JDIMENSION fields, which it skips too.Make a JPEG smaller without re-encoding it
jpeg_read_coefficients and jpeg_write_coefficients copy the quantised DCT coefficients from one file to another, as jpegtran -copy all does: the Huffman coding is redone, optimised or progressive, and every pixel stays the same.
@crossbind/port-jpegturbo/jpeglib.h fails the build today: crossbind binds struct fields that jpeglib.h declares only under #if JPEG_LIB_VERSION >= 70 (the port has 62), and compiling the bindings stops at "no member named 'min_DCT_h_scaled_size' in 'jpeg_compress_struct'". With that bug patched out, jpeg_read_coefficients and jpeg_write_coefficients need no fields of their own, but they come after jpeg_read_header, which ends in RuntimeError: null function because libjpeg reports through cinfo->err and nothing binds that pointer. Optimized coding needs optimize_coding, a boolean field that is skipped, and copying the markers walks marker_list, which is not bound.Read and write EXIF and comments
jpeg_save_markers keeps the APPn and COM segments libjpeg-turbo would otherwise skip, and jpeg_write_marker adds new ones. Here an EXIF orientation and a comment go in without re-encoding the image.
@crossbind/port-jpegturbo/jpeglib.h fails the build today: crossbind binds struct fields that jpeglib.h declares only under #if JPEG_LIB_VERSION >= 70 (the port has 62), and compiling the bindings stops at "no member named 'min_DCT_h_scaled_size' in 'jpeg_compress_struct'". With that bug patched out, jpeg_read_header ends in RuntimeError: null function (libjpeg reports through cinfo->err, and nothing binds that pointer), and the saved markers stay out of reach: marker_list is not bound, and of jpeg_marker_struct only original_length and data_length are (next, marker and data are pointer or UINT8 fields, which are skipped).What is different on WebAssembly
- In a browser the module runs in a Worker by default (
useWorker), so every call returns a promise:awaitcalls and constructors alike. - The module has its own filesystem:
m.FSwrites files,m.getFileBytesreads them back andm.autoMountFilesmountsFileobjects from an<input type=file>./memfslives in memory;/opfspersists across reloads and needs the Worker. See Filesystem. - In Node.js,
m.FSis the real disk, so use real paths there. - Multi-threaded builds (
runtime: 'mt') need COOP and COEP headers in production. See Threading.
Other platforms
- libjpeg-turbo overview: the apps, every platform's setup and the packages.
- libjpeg-turbo for Android: React Native apps on Android.
- libjpeg-turbo for iOS: React Native apps on iOS.
- libjpeg-turbo for WASI: command-line programs under wasmtime.
Facts on this page come from the port manifests in the repository and from what npm served on beta when the site was built. See the Libraries guide for the full consumer flow.