This folder contains an example how to decode JPEG XL files on a web page using WASM engine.
The simplest way to get support of JXL images on the client side is simply to
link one extra script (<script src="service_worker.js">
) to the page.
This script installs a ServiceWorker
that:
- checks if the browser supports the JXL image format already
- if it is not, then advertise
image/jxl
as media format in image requests - then, if the server responds with
image/jxl
content it gets decoded and re-encoded to PNG on the fly
Generally the message / data flow looks the following way:
Fetch API
receives a resource request from client page (e.g. when the HTML engine discovers animg
tag) and asks theServiceWorker
how to proceed- the
ServiceWorker
alters the request and uses theFetch API
to obtain data - when data arrives, the
ServiceWorker
forwards it to the "client" (the page) that initiated the resource request - the client forwards the data to a worker (see
client_worker.js
) to avoid processing in the "main loop" thread - a worker does the actual decoding; to make it faster several additional workers are spawned (to enable multi-threading in WASM module); the decoded image is wrapped in non-compressed PNG format and sent back to client
- the client relays image data to
ServiceWorker
- the
ServiceWorker
passes data toFetch API
as a response to initial resource request
Despite the additional "hop" (client) in the flow, data is not copied every time but rather "transferred" between the participants.
Demo page: one_line_demo.html
. Extended demo, that also shows how long it
took do decode images: one_line_demo_with_console.html
.
Page that shows "manual" decoding (and has benchmarking capabilities):
manual_decode_demo.html
.
To enable multi-threading some files should be served in a secure context (i.e. transferred over HTTPS) and executed in a "site-isolation" mode (controlled by COOP and COEP response headers).
Unfortunately GitHub Pages does not allow setting response headers.
Netlify provides free, easy to setup and deploy platform for serving such demonstration sites. However, any other service provider / software that allows changing response headers could be employed as well.
netlify.toml
and netlify/precompressed.ts
specify the serving rules.
Namely, some requests get "upgraded" responses:
- if a request specifies that
brotli
compression is supported, then precompressed entries are sent - if a request specifies that
image/jxl
format is allowed, then entries transcoded to JXL format are sent
build_site.py
script takes care of JavaScript minification, template
substitution and resource compression. Its arguments are:
- source path: site template directory (that contains this README file)
- binary path: build directory, that contains compiled WASM module
- output path
To complete the site few more files are to be added to output directory:
image00.jpg
,image01.png
demo images; will be shown ifServiceWorker
is not yet operable (fallback); to see those one could initiate "hard page reload" (press Shift-(Ctrl|Cmd)-R)image00.jpg.jxl
,image01.png.jxl
demo images in JXL formatimageNN.jxl
images for "manual" decoding demo; NN is a number starting form00
favicon.ico
is an optional site iconindex.html
is an optional site "home" page
In the source code (service_worker.js
) there are two compile-time constants
that modify the behaviour of Service Worker:
FORCE_COP
flag allows rewriting responses to add COOP / COEP headers; this is useful when it is difficult / impossible to setup response headers otherwise (e.g. GitHub Pages)FORCE_DECODING
flag activate JXL decoding when image response type hasContent-Encoding
header set toapplication/octet-stream
; this happens when server does not know the JXL MIME-type
One dependency that build_site.py
requires is uglifyjs, which can be installed with
npm install uglify-js -g
If you followed the wasm build instructions, assuming you are in the root level of the cloned libjxl repo a typical call to build the site would be
python3 ./tools/wasm_demo/build_site.py ./tools/wasm_demo/ ./build-wasm32/tools/wasm_demo/ /path/to/demo-site
Then you need to put your image files in the correct same place and are should be good to go.
To summarize, using the wasm decoder together with a service workder amounts to adding
<script src="service_worker.js"></script>
to your html and then putting the service_worker.js
and jxl_decoder.wasm
binary in directory where they can be read.
It is not guaranteed, but somewhat fresh demo is hosted on
https://jxl-demo.netlify.app/
, e.g.:
- one line demo
- one line demo with console
- manual decode demo;
URL contains query parameters that control rendering and benchmarking options;
please note, that HDR canvas is often not enabled by default, it could be
enabled in some browsers via
about://flags/#enable-experimental-web-platform-features
service_worker.js
jxl_decoder.wasm