Sharp Introduction

The javascript sharp library is used for image manipulation. https://sharp.pixelplumbing.com/

These are various experiments and exercises to better understand the possibilities of this library and how it can be used to automate various image manipulation tasks such as:

  • resizing
  • cropping
  • extracting a section
  • compositing (overlaying an image with another)

Most of these are pretty straightforward but there are some more advanced scenarios that seem possible but don't have many samples such as:

  • compositing with various blend modes
  • convolve to warp an image
  • affine, clahe, etc that are not clear what they actually do

The goals of these experiments are:

  • increase my own knowledge as to the possibilities and see if certain photoshop actions can be done via javascript
  • document some of these behaviors with examples so I can refer to later

Typical jpg metadata

const sharp = require("sharp")

const loadJpg = async () => {
    const image = await sharp('./cat1.jpg')
    const metadata = await image.metadata()    
    console.log(metadata)
}
loadJpg()
{
  format: 'jpeg',
  width: 1680,
  height: 1050,
  space: 'srgb',
  channels: 3,
  depth: 'uchar',
  density: 72,
  chromaSubsampling: '4:2:0',
  isProgressive: false,
  hasProfile: false,
  hasAlpha: false
}

Load animated gif and save the default frame

const loadAnimatedGif = async () => {
    // loads default frame (page 1) and save to jpg
    const image = await sharp('./cat-animated.gif')
    const metadata = await image.metadata() 
    image.toFile('./cat-animated-frame1.jpg')   
    console.log(metadata)
}
loadAnimatedGif()

Note that it contains 102 frames (pages)

{
  format: 'gif',
  width: 548,
  height: 610,
  space: 'srgb',
  channels: 4,
  depth: 'uchar',
  isProgressive: true,
  paletteBitDepth: 5,
  pages: 102,
  loop: 0,
  delay: [
    30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
    30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
    30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
    30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
    30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
    30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
    30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
    30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
    30, 30, 30, 30,
    ... 2 more items
  ],
  background: { r: 255, g: 255, b: 255 },
  hasProfile: false,
  hasAlpha: true
}

Load a specific frame (page) from an animated gif and save it

const loadAnimatedGifFrame42 = async () => {
    // load only frame 42 and save to a jpg
    const image = await sharp('./cat-animated.gif', {pages: 1, page: 42})
    image.toFile('./cat-animated-frame42.jpg')
}
loadAnimatedGifFrame42()

Load 10 frames and save to an animated webp file

const loadAnimatedGif10Frames = async () => {
    // load only 10 frames and save to animated webp
    const image = await sharp('./cat-animated.gif', {pages: 10, page: 42})
    .toFile('./cat-animated.webp')
}
loadAnimatedGif10Frames()

Reload the animated webp file

Confirmed that it only has 10 frames (pages)

const loadAnimatedWebp = async () => {
    // open up the webp file and confirm we only have 10 frames
    const image = await sharp('./cat-animated.webp')
    const metadata = await image.metadata() 
    console.log(metadata)
}
loadAnimatedWebp()
{
  format: 'webp',
  width: 548,
  height: 610,
  space: 'srgb',
  channels: 4,
  depth: 'uchar',
  isProgressive: false,
  pages: 10,
  loop: 0,
  delay: [
    30, 30, 30, 30, 30,
    30, 30, 30, 30, 30
  ],
  hasProfile: false,
  hasAlpha: true
}

Change the delay speed of the animated gif

This adjust the delay value between each frame thus slowing it down

const loadAnimatedGif10FramesSlowDown = async () => {
    // load only 10 frames and save to animated webp
    const image = await sharp("./cat-animated.gif", {
      pages: 10,
      page: 42,
    })
    .webp({delay: 90}).toFile("./cat-animated-slow.webp");
}
loadAnimatedGif10FramesSlowDown()

Make Noise sample

const makeNoise = async () => {
  // Generate RGB Gaussian noise
  await sharp({
    create: {
      width: 300,
      height: 200,
      channels: 3,
      noise: {
        type: "gaussian",
        mean: 128,
        sigma: 30,
      },
    },
  }).toFile("noise.png");
}
makeNoise()

Make Text Image sample

const makeImageFromText = async () => {
// Generate an image from text
await sharp({
    text: {
      text: 'Hello, world!',
      width: 400, // max width
      height: 300 // max height
    }
  }).toFile('text_bw.png');
}
makeImageFromText()

Make Text Image sample from Pango Markup

const makeImageFromTextHtml = async () => {
  // Generate an rgba image from text using pango markup and font
  await sharp({
    text: {
      text: '<span foreground="red">Red!</span><span background="cyan">blue</span>',
      font: "sans",
      rgba: true,
      dpi: 300,
    },
  }).toFile("text_rgba.png");
}
makeImageFromTextHtml()

Pango is a library for laying out and rendering of text, with an emphasis on internationalization Pango Documentation

Go to resize examples