Almost any modern web application requires some image manipulation for some reason. It might be an image resizing strategy to reduce site load speed or protect pictures with watermarks or anything you can imagine. All the heavy lifting does image processing libraries. Let’s look at one of them for Node.js.

When I start to look for an image processing library for one of my latest projects, I put some requirements:

  • the library should support streams

  • it should have the support of modern formats

  • the library should be swift

Long story short, I spotted the “Sharp” library https://sharp.pixelplumbing.com/ which complies with all my requirements.

In contrast to many libraries, Sharp uses under-the-hood libvips https://www.libvips.org/ instead of ImageMagic or GrpahicMagic. And it makes it faster up to 5 times compared with other “classic” libraries. As well as the support of many modern image formats, such as “WebP”, “HDR” and even “HEIC” (with some tricks).

And what about Stream support? It supports Streams, Buffer objects, and file system objects as a source, and output may be split into several processing pipelines and output streams. Sounds wonderful, isn’t it? Let’s take c closer look at how we can use this library in our projects.

I prepared a project with several examples of data processing. https://github.com/a13xg0/sharp-ts-demo

To add the library to your project, you might just type

yarn add sharp

It requires some time because the library downloads a binary file or builds libvips from the source, depending on your target OS and CPU architecture.

Let’s try to make a simple conversion:

sharp('assets/jeremy-bezanger-unsplash.jpeg')
    .png()
    .toFile('output/jeremy-bezanger-unsplash.png')
    .then(console.log)

As a result of executing this code, we will get a converted file in the output folder. And the object with file information in the console output.

Let’s use the more complex example with transformation

sharp('assets/jeremy-bezanger-unsplash.jpeg')
    .rotate(45)
    .resize(null, 200)
    .png()
    .toFile('output/jeremy-bezanger-unsplash-transformed.png')
    .then(console.log)

Oh wait! Where is the complexity? With this amazing library we have no one!

All right, since I have complexity debt let’s try to embrace the power of Node.js - pipelines of streams. We will read image form stream, process it and write to stream back. I’ll use file streams but you can imagine we are working over network, in the example.

const readableStream = fs.createReadStream('assets/jeremy-bezanger-unsplash.jpeg')
const writeableStream = fs.createWriteStream('output/jeremy-bezanger-unsplash-pipeline.png');

const transformationPipeline = sharp()
    .rotate(45)
    .resize(null, 200)

const imageConvertPipeline = sharp()
    .png()

readableStream
    .pipe(transformationPipeline)
    .pipe(imageConvertPipeline)
    .pipe(writeableStream)
    .on('finish', () => {
        console.log('done')
    })

In this way we can combine different effects depending on conditions in our code.

Definetly, Sharp library is my image transformation library of a choice at this moment. It provides convenient interface and all necessary tools for image processing.

Source codes of examples and demo project can be found on GitHub https://github.com/a13xg0/sharp-ts-demo