The unofficial way to embed Squoosh into your web app.

Earlier this week I wrote about a tool that I created to create an optimal HTML <picture> element with an associated set of optimized images. The app is pretty cool (even if I do say so my self).

I think Squoosh is an amazingly useful tool in our efforts to improve the optimization of images on the web and make it easier for developers to do the right thing without having to think too much. The Squoosh team have done amazing work recently to create a CLI that enables you to easily compress and resize images straight from the command line, this opens up the ability to integrate image optimzations in to your build process on any platform that has the ability to run Web Assembly.

This is all great, but the CLI is not my web app. I really needed an API version of Squoosh.

In the past, I've worked on getting FFMPEG CLI running inside web apps, so that gave me the idea that it might also be possible to get the Squoosh CLI running in a web page too. Squoosh's CLI had all the options I needed, so if I could get it work as an API I would be very happy.

Squoosh's code is well structured, so it wasn't too hard to find the code that dealt with the CLI and a quick scan through it showeded that there were only a couple of places where it relied on Node and so I would have the choice of stripping it out, or polyfilling Node API's in the browser.

Thankfully, after a couple of hours hacking (and a sleep), I managed to get it working. I won't go into it too much detail of all the changes, but the summary is:

  • The Web Worker code in Node had to be extracted to it's own file.
  • WASM Bindings for Web had to be generated because they were for node.
  • Replaced all of Node's File handling with Web API's.
  • Expose the run method as the entry point to the API.

Once it was all built, all I had to do was copy the WASM and JS bindings into my repository and call it as follows:

import { run } from './lib/squoosh';

// Resize the image to 800x600 and convert to avif
const result = wait run(
    files: [this._file], 
      "resize": { "width": 800, "height": 600 }, 
      "avif": "auto" 

This solution worked well for my demo app, but it's not official and is already lagging behind Squoosh's main-line branch. I think that a dedicated, browser-friendly API (rather than a run method) will open up a lot of opportunities, for example it would allow: any CMS to integrate image compression as soon as the client tries to upload an image; Tools such as Lighthouse and Page Speed insights could integrate it so the instant they say you are doing your images wrong, they also give you the optimal solution; and an ecosystem of image optimizers could be built directly into web apps.

If you want to integrate Squoosh in to your web app, then go and ping the team and let them know they need a Browser-side API. Tell them I sent you!

About Me: Paul Kinlan

I lead the Chrome Developer Relations team at Google.

We want people to have the best experience possible on the web without having to install a native app or produce content in a walled garden.

Our team tries to make it easier for developers to build on the web by supporting every Chrome release, creating great content to support developers on, contributing to MDN, helping to improve browser compatibility, and some of the best developer tools like Lighthouse, Workbox, Squoosh to name just a few.