Image Processing in wanderer

Update on January 18: I ended up discarding sharp and all the other tooling to use GraphicsMagick and PNGQuant, as that gave me much faster image processing than the sharp stack.

It doesn’t look like there are many (any?) node.JS modules that do image processing that also don’t have any dependencies.

Things I’ve looked at so far:

Unfortunately, they appear to have a lot of dependencies:

sharp has 71 depencencies

What would I need in an image processing package?

There are several concerns with web images:

  • image size - I frequently draw on an iPad, where all images are around 3000 pixels in dimensions. I’ll want to reduce down to around 1200
  • compression - large filesizes in images destroy load times
  • removing exif data - I’m always troubled by how much metadata ends up in each image nowadays (including things like camera model and location).

Non-goals

  • progressively loading images
  • non-image media (for now)

No Progressive Images?

As fancy as they are, I’m tabling them for now as they require Javascript to work (as far as I know). They also require generating various previews, which I’d want to stylize – and thus drastically increase build times.

What should we do?

In wanderer, when an image file is added to content, the result that gets put in build should go through the processor. This lets us keep the full-res image in content, without it showing up online.

Left unsolved is whether we’d want to change image file extension (like, say, for favicon), or what happens if we want to add a thumbnail or the fullsize version.

Maybe have a special meta file that can be associated with images that has that information?

Implementation Details

There’s no two ways about it - unless I’m willing to spin up a web browser and have canvas handle image manipulation, I’ll want to use a dependency. I’m not yet at the point where I’m willing to write my own image manipulation library (and probably not in JS).

I’ll use sharp, for speed purposes. It doesn’t support exif stripping, so I’ll also use exiftool for stripping out exif metadata.

In wanderer, the image processing is implemented in tools/image-processing, and should be accessible in the github.

pngquant

PNGs take two steps to be processed: first resized and compressed in sharp, then converted to lossy version via pngquant. Using a node module for this can work, but takes up another 200 or so dependencies, so for now I assume that the computer has it installed.

I spent entirely too much time working on this, mostly because pngquant was failing to write the output images.

It turns out that the problem was creating a writeStream for the target file path, which prevented any other piece of the program from writing to that target file path:

const outputStream = fs.createWriteStream(targetFilePath)
fs.writeFileSync(targetFilePath) // this will fail!

POSSIBLE EXPLORATION:

There does exist https://www.npmjs.com/package/pngquant-bin which can be used to run pngquant without having it installed globally - that may be something I look into in the future if it turns out that enforcing externally installed pngquant is too much a hassle.