Slow-loading pages are frustrating for visitors, with research by Google suggesting it directly affects whether they continue to visit your site:
“The probability of bounce increases 32% as page load time goes from 1 second to 3 seconds.”
With tools like Lighthouse we can test the performance and view recommendations. Images are a critical part of a page’s weight and so image optimisation is vital. Luckily there are a few ways we can do this:
The simplest way to improve performance is to serve an appropriately-sized image depending on the viewport. For example, on a large screen desktop you might want a lovely hi-res image, but on a mobile where the screen is smaller, you want to serve a much smaller image to fit the screen.
Drupal comes with the Responsive Image module that will allow us to create image styles for each viewport, and it will bundle them together.
When you visit a URL, e.g. www.zoocha.com, the default behaviour for the browser looks like this:
- Download the HTML
- Parse the HTML
- Load CSS
- Load JS
- Load Images
- Trigger ‘DOMContentLoaded’ event
- Trigger Window ‘load’ event
The order of the assets is dependent on the order in the HTML, but the principle is the same. If a page has lots of images these will all have to load before the JS can execute.
Lazy loading is a technique to defer the loading of non-critical elements (such as images) until they reach the viewport of the browser window.
The ‘loading’ attribute is a relatively recent addition which allows the browser to handle when to load images. This is now well supported; by default in Drupal 9 (and above) the `loading` attribute is added to images when there are width and height attributes.
The dimensions are important to establish how much space the image takes up, thus dictating when the browser should trigger the load.
Traditionally, web developers had the choice of 3 file formats:
- GIF: used for animations, typically low quality and large filesize
- JPEG: efficient compression and good quality, but images cannot have transparency
- PNG: Allows for alpha transparency, and high quality, at the expense of a larger file size
There are ‘next-generation’ image formats that make images much more efficient, lossless compression which results in significantly smaller files. The two main ones are:
- WebP: Webp uses ‘predictive compression’ to reduce file sizes by up to 26% compared with PNG images
- AVIF: AVIF is an open-source format, based on the video AV1 codec. It offers considerably smaller (up to 50% in some cases) images than JPEG with similar visual quality. The browser support is not as comprehensive (https://caniuse.com/?search=avif) with only Firefox and Chrome fully supporting this, though support for Edge and Safari is likely to come soon
Webp, AVIF and Drupal
WebP support is relatively straightforward with Drupal: simply install the WebP module! To offer backwards compatibility, it’s recommended to use responsive images with the <picture> element.
In theory, AVIF is the same: there’s an AVIF module too. However, it’s a bit more complicated as AVIF processing is quite new, requiring the latest version of ImageMagick, or other image processing tool that will convert to AVIF.
To be polite, results have been mixed. Being an alpha module there are a few bumps, and it also depends on having the avifenc libraries installed on your webserver alongside an up-to-date version of ImageMagick (7 at the time of writing). Of course, being a Drupal module it’s open source so we are planning to contribute back our fixes as we find them!
By adding a
<source> element for each format and specifying the type attribute you pass the choice of image over to browser:
<source src=”my-image.avif” type=”image/webp” alt=”This is a WebP image” />
<source src=”my-image.webp” type=”image/avif” alt=”This is an AVIF image” />
<source src=”my-image.jpg” type=”image/webp” alt=”This is a JPEG image” />
<img src=”fallback.jpg” alt=”this is a fallback image” />
In the example above, if the browser supports the AVIF image it will load that. If not, it will check the other
<source>s to find one that matches.
The screenshot below shows a comparison of the three image formats:
- Original PNG image at the top : 611kb
- Webp version: 25kb
- AVIF version 18kb
Now this a bit of a contrived version as the image is just an illustration, meaning it’s likely to be compressed more efficiently than average. Also, these were converted with local tools and might not be representative of the real world, using Cloudflare’s Polish to convert, the WebP image was ~320kb. Still a significant saving though, so will be a generous performance boost. The results are almost identical visually too: