Optimising JPG and PNG images for a Jekyll website
This website has been text-oriented for a long time now. The decision to add article banners and thumbnails is a recent one. I stayed away from images for a long time since handling and optimising images can be a PITA. I was glad to find that optipng
, jpegoptim
, and imagemagick
already exist to solve this problem for me.
My plan is as follows.
I already have all article images in my static/images
folder. From there, I want to generate two copies of all PNG and JPG images. The first would be a cropped thumbnail version measuring 422-by-316. The second would be a larger banner version measuring 1024x768.
Both copies, the thumbnail and the banner, will be in folders of their own. I’ll then leverage Jekyll’s custom variables for the folder paths.
Installing the binaries
On my Mac OS X, installing the binaries required a single brew
command.
brew install optipng jpegoptim imagemagick
Creating folders for thumbnails and banners
Next, I’ll create new folders under static/images
. Thumbnails will go in img-thumbs
and banners will go in img-normal
.
cd static/images
mkdir -p img-thumbs img-normal
With the folders created, I’ll first copy all GIF
, SVG
, JPG
, and PNG
files to both folders. I’ll use the GIFs
and SVGs
as-is for thumbnails and banner images.
cp content/*.gif img-thumbs/; cp content/*.gif img-normal/
cp content/*.svg img-thumbs/; cp content/*.svg img-normal/
cp content/*.jpg img-thumbs/; cp content/*.jpg img-normal/
cp content/*.png img-thumbs/; cp content/*.png img-normal/
Processing thumbnails
First let’s resize and optimise the thumbnails. As mentioned earlier, I want the thumbnails to be 422-by-316. I’ll use the mogrify
command from ImageMagick
to resize the JPGs
and PNGs
.
cd img-thumbs
mogrify -resize 422x316 *.png
mogrify -format jpg -resize 422x316 *.jpg
Now let’s optimise the PNGs
using optipng
and the JPGs
using jpegoptim
.
for i in *.png; do optipng -o5 -quiet "$i"; done
jpegoptim -sq *.jpg
In the above command:
- For
optipng
,-o5
swtich sets the level of optimisation, with zero being the lowest. - For
jpegoptim
,-s
strips all image metadata, and-q
sets quiet mode.
Processing banners
I’ll process the banner images like I processed the thumbnails above. Other than the file dimensions everything else will stay the same.
cd ..
cd img-normal
mogrify -resize 1024x768 *.png
mogrify -format jpg -resize 1024x768 *.jpg
for i in *.png; do optipng -o5 -quiet "$i"; done
jpegoptim -sq *.jpg
Configuring the paths in Jekyll
img-thumbs
now contains my thumbnails and img-normal
contains the banners. To make my life easier, I’ll set both of them to Jekyll variables in the _config.yml
.
content-images-path: /static/images/img-normal/
content-thumbs-images-path: /static/images/img-thumbs/
Using these is simple. When I want to display the thumbnail I’ll prepend content-thumbs-images-path
to the image. When I want to display the full banner I’ll prepend content-images-path
.
For example, articles on this site now have a banner_img
property in the front matter. The value of this property is the name of the image file. To display the thumbnail for the article, I use the following code:
{% if page.banner_img %}
<img src="{{ page.banner_img | prepend: site.content-images-path | prepend: site.baseurl | prepend: site.url }}" alt="Banner image for {{ page.title }}" />
{% endif %}
Conclusion
There are several improvements we can make to the commands above. The most obvious might be to use rsync
to only copy changed files to img-thumbs
and img-normal
. That way we’re not re-processing files over and over again. Another improvement might be to add those commands to Git pre-commit hooks or a CI pipeline.
Resizing and optimising images to reduce their size is a win for the user and the web as a whole. Fewer bytes transmitted over the wire means a lower carbon footprint, but that’s another article. The UX victory is good enough for now :)
Happy coding :)