Picture-Perfect Pictures

Ian Downie

Category: Development

08.29.2018

The following is intended for use by web developers to improve their ability to optimize and manage images as much as possible while minimizing the increased workload involved in doing so. This article assumes the use of the WordPress content management system (CMS) but similar practices can be applied to other systems.

A large portion of the web consists of images. Images are often the greediest bandwidth hogs on a site and they are some of the most frequently mismanaged resources. This leads to broken images, stretched/contorted images, and almost never fully optimized images. This is unfortunate because, while images are often the most visually appealing aspects of the web, they also present the greatest burdens on page loading times and bandwidth costs.

Fortunately, the modern web offers us an array of tools that the enterprising developer can utilize in pursuit of improving image management on their sites. Unfortunately, each of these tools comes with increased complexity and can greatly burden the task of adding images to a site if not handled efficiently. However, with proper forethought, we can utilize all of the latest tools with little to no additional work after setting up a proper foundation from which to work.

Image Optimization Techniques

The following are the set of tools and techniques I utilize to improve optimization, user experience, and the general management of images on the sites I work on. After setting them up properly, these techniques can make development work burden-free.

Source Set

One way to optimize images is to ensure that users are not loading images that are of unnecessarily large dimensions. If a user is on a mobile device with a viewport that is less than 1000 pixels wide, there is no reason why they should be downloading a 1500 pixel wide image that’s just going to be scrunched down to their viewport width. Source set allows developers to deal with this problem, albeit at a significant complexity cost. Here is an excellent explanation of what Source Set is and what it can do. I’ll assume knowledge of what this technology is and how it is used going forward.

Source set introduces the following complexities:

    • There must be several versions of each image, of varying dimensions.
    • Two additional, and complex, HTML attributes, “srcset” and “sizes”, must be constructed and applied to image elements.
    • Image elements must be used to display images rather than having images display as background images on HTML elements, which means that developers can no longer utilize the immensely helpful and highly supported background-size and background-position CSS attributes.

Web Optimized Image Formats

All of the major browsers have developed new image file formats intended to improve image optimization. Unfortunately, no browser supports the new formats that have been created by their competitors. This situation isn’t likely to change anytime soon as, if any of the browsers were to adopt their competitor’s file formats no one would bother adopting its specific format, rather than using the single format that works with it, and its competitor. This means that for every new browser’s format that your site supports, your site needs to be generating new images for that browser. So the complexity involved in making use of these new file formats becomes quite substantial if you attempt to support all major browsers. I’ll be demonstrating how to make use of only one of these; Chrome’s Webp image format. Chrome is far and away the world’s most popular browser, so using this format gives you the most bang for your development buck

Image Previewing

This technique is not aimed at improving a page’s final finished load time, but to improve the user’s experience while the page is loading. It does so by hiding images as they load and instead displaying a super-tiny version of the image, which will have a similarly super-tiny file size, and will thus load nearly instantaneously. Once the full-sized image has finished loading it fades in, replacing the preview. This technique isn’t a true optimization technique, but it does help the page feel faster and prevents pages from displaying embarrassing and potentially confusing large empty spaces on a page before large images have time to load. It gives your site a smoother, more professional, ambiance.

The increased complexities of implementing this technique involve having your site generate yet another version of each image (alternatively, you can just have a particular color display where images have not yet loaded, which isn’t quite as nice, but is admirably simple to implement), as well as having JavaScript track when images finish loading and then replacing the preview with the full version of each image after it’s been loaded.

Default Replacements for Missing Images

If a content administrator on your site neglects to supply an image where your site expects to find one, it is often preferable to have default images to fallback to rather than displaying no image. This can actually be a lot easier to accomplish rather than figuring out how to handle every individual situation where an image can potentially be not supplied.

I’ll be doing this by adding a default image field(s) to a WP custom options page in the admin panel. You might want multiple fields of this kind so that you can have one default fallback image for banner images, another for headshots, and others for teaser images for various post types, etc. Setting these fields up with ACF is quite easy, and adding fallbacks to our images will be even easier.

Lazy Loading

Lazy Loading is a technique where a page is built in such a way that prevents images from being loaded unless they are actually needed. For example, if an image is placed at the bottom of a page, or the not at top of a long feed of images; it won’t actually load until the user scrolls down to the part of the page where the image displays. This improves page loading times by reducing the amount of data the page is attempting to load when a user first opens it, and it’s quite a polite thing to do for mobile users who, if they are have limited data usage, have a strong reason to not want to load a bunch of images that they may never view.

The increase in complexity involved in implementing this technique is relatively modest, it just requires a bit of JavaScript to tell your images to load at the appropriate time and some changes in your HTML to prevent images from loading at first, and to provide a fallback method to load your images if JavaScript is disabled or broken on a page. The latter is to ensure that users never arrive at a page where lazy-load images never load. For more information, here is an excellent article covering lazy loading techniques in greater depth.

Automatic Image Optimization

This technique involves utilizing sophisticated algorithms to remove unnecessary data, and compress image files as much as possible, without reducing visible image quality. This technique alone can often reduce a large, high definition, that could takes minutes to finish loading for users with low bandwidth, to a small fraction of their original file size, without any noticeable reduction in quality. And, lucky for us, it can be done by simply adding a WP plugin!

Optimization Techniques for Your Image Optimization

Each one of the above techniques is fairly complicated to get working throughout an entire site; trying to utilize them all at once becomes quite a daunting task. Ensuring that developers consistently utilize all of them for every image on a site requires that we somehow make all of this nearly as easy to use as a simple image element with none of this complexity, or else other developers simply won’t use it. Fortunately, all of the tools we need to accomplish this task do exist, and we just have be smart in how we use them such that our code becomes so “DRY” that, once set up, adding a new image to the site isn’t burdened by all of this new complexity we’re adding.

I’m going to walk through my personal recipe for how to cook this up. We’ll start with the ingredients:

  • WordPress
    • All of these techniques can be used within the context of other Content Management Systems, but I have to use one as an example and WP is by far for most popular, so it will serve as my example.
  • imagesLoaded
  • Modernizr, detecting Object Fit support
    • Modernizr detects whether a user’s browser supports various capabilities so that you can activate fallback functionality for browsers that don’t play nice with your coolest techniques. I’ll be using it to detect at least Object Fit and possibly other features.
  • Advanced Custom Fields
    • I use this incredibly functional plugin for every WP project I work with, and it’s my primary way of allowing admins to manage content such as images on WP sites.
  • ShortPixel Image Optimizer
    • The Shortpixel Image optimizer plugin does most of the legwork in getting our alternate versions of our images in a web-optimized file format, in this case WebP, which is Google Chrome’s version.
  • Knowledge of PHP, JavaScript, SCSS, and jQuery.

Cooking Instructions

We need to combine all of our individual ingredients in a fashion that will be easy enough to use such that neither we nor other developers will become frustrated and just add images the normal way lest all of our prep-work go unused. We need to put in additional work up-front, to make sure that the process is as easy as possible down the road, whenever a developer wishes to add an image to the site. Our goal should be to end up with a process for adding an image in the site’s code that is just about as easy as adding a simple image tag with the image source and perhaps a CSS class.

The first step is to setup a WP site to play around with, and adding the Advanced Custom Fields and ShortPixel Image Optimizer plugins to our WordPress setup and configuring them. They’re installed the normal way any WP plugin is installed and they’re not difficult to configure as we’ll want them to be.

ShortPixel doesn’t need a lot of setup; you just need to tell it to make WebP versions of images (Chrome’s web-optimized image format) that are uploaded to the site. Here’s a post describing how to setup ShortPixel, as well as the benefits of using WebP images.

The ACF plugin isn’t strictly necessary as core WP featured images can work just fine, but ACF is just so convenient that it’d be rather silly not to use it. Plus, it’ll be good to show that all of this can be done with both core WP image uploads and ACF data. Once ACF is installed, you can simply add an image field to a post or page and then add an image to that field for testing purposes.

Once that’s complete we’ll be ready to jump into some PHP in order to build out a system to allow developers to generate the complex HTML that is necessary for us to take advantage of all the image optimizations we want to use.

The first thing we need to do is register our image sizes. Once registered, WordPress will create a copy of each uploaded to it for each size and, when the ShortPixel plugin is configured correctly, an additional WebP formatted image for each size will be generated. The sizes I’ve used below are commonly useful sizes for high definition mobile devices, tablets, laptop screens and large computer screens. I’m also having WordPress produce a “preview” size file for each image that is only a single pixel wide and tall. We’ll be using this to have a preview version of each image that is so small that it loads pretty much instantaneously and will display in place of the appropriate full sized image until the full size once finished loading.

Next, we need to write a couple of functions that will simplify the process of putting together all of different image handling ingredients. First we’ll build a function that you give image data, and potentially additional CSS classes you’d like to add and/or a default fallback to display if, for any reason, no image data is provided:

Then we also build a relatively simply function that will handle the logic for producing our background preview styles that each image will display until its full size image is finished loading:

And finally, this is the function that developers will actually call when they want to create and render a fully constructed image to a place on our site. All the developer will need to provide to it is image data, in the form of either an ACF array or WP media ID, optional classes for either the image elements themselves or their wrappers, as well as an optional default image field to fallback to should no image data be provided. It returns a picture element containing a image elements of different file formats (The browsers can handle the special web-optimized formats will automatically make use of those while others will use the standard format image elements), each with their own srcset and sizes attributes added to each image element:

It should be noted that we’re not taking full advantage of srcset here. Ideally we’d give each image srcset and sizes data that is particular to the exact widths that image will display at. But doing so would require placing a significant burden on a developer who would have to hand-craft that data any time they wanted to add an image to a new place on the site. But we can benefit from a large chunk of the value that srcset implementation offers, by simply having the srcset and sizes attributes limit the size of any image to the width of the current viewport, without placing any additional burden on developers looking to add a new image location to the site. And when a single poorly managed image can easily quadruple a page’s load time it’s better to get 90% of the value of the tools at your disposal 100% of the time, then to make the process so difficult that you end up using 10% of the tools at your disposal 90% of the time, even if that would allow you to make use of 100% of the value of your tools the remaining 10% of the time.

When a developer uses the above function they’ve be returned an HTML string that they can print that will look something like this:

With the appropriate styles this will display only the preview image at first, unless the user’s JS is disabled or encountering errors. In which case the HTML within the “noscript“ tag will display the non-lazy-loading version of the image. As you can see, there is an additional “source” element within each “picture” element to allow the loading of additional file formats, and each image element and source has srcset and sizes data. And the JS-enabled version of the picture sources have data-srcset and data-src attributes instead of srcset and src attributes to prevent them from loading until we want them to.

That lazy-loading functionality is the next thing we’ll be building, so it’s time to pull out our Javascript. First we need to load the JS libraries we’ll be using; imagesLoaded and Modernizr. ImagesLoaded will be able to notify our JS when the images that we’re lazy-loading have finished loading, at which time we can reveal them in all their full-loaded glory. (This avoids the standard ugly and frustrating image loading that users would otherwise experience). Our Modernizr build will need to either be custom built at their site to include handling for object-fit so that we’ll be able to provide fallback functionality to browsers that don’t support it (Damn you IE!). Or, alternatively, you could be using a node module that will automatically detect that you’re checking for browser support, using Modernizr, in either your CSS or JS and that will automatically build a Modernizr script that includes the necessary tools it detects. I know there’s a module for both Gulp and Webpack and there likely are for other build tools you might be using as well.

Next we’ll build a couple of helper functions:

This function will be able to tell our lazy-loader when any particular image element has entered the user’s viewport.

And this next function will simplify the process for registering event listeners:

Now we have the tools we need to build our lazy-loading and image-previewing functions:

The last bit of JavaScript we’ll want to add is fallback functionality for Object-fit, which is very often the best way to have images display, but which IE doesn’t support. We’ll be using Modernizr to detect whether the current browser supports it, and if not, we’ll add a CSS class to display the HTML a bit differently and we’ll be replacing the image-preview background with the full-sized image source rather than displaying the picture element. This means that we’ll lose out on srcset functionality for some browsers, but gaining access to the universally supported background-size CSS attribute is often necessary to have images look as intended on browsers that don’t support object fit. It’s preferable for old browsers to display a page correctly, and take a bit more time than a well-optimized page, then to either display optimized but misshapen images on older browsers (by using srcset and ignoring the absence of object-fit) or having the site load slowly on all browsers by using background-image for everything to begin with.

Here’s the JS necessary to provide an Object-fit fallback:

All that remains now is to add the styles that will properly display the image properly under all the different scenarios the image could conceivable be working within. There are different ways of handling these, but this is a good starting point that I’d recommend:

Once you have the SCSS adding its styles to your site, and have completed all the previous steps, then congratulations! You now have a site that takes advantage of all of the wide variety of image optimizations tools the modern internet makes available to us, and you and your fellow developers will actually use them as they’re super easy to make use of!

All it takes now to add an image to a template is something like the following for an ACF field’s image:

print EXAMPLE_image_render( get_field( 'your_image_field') );

Or this, if you want to render a core WP featured image for a post:

print EXAMPLE_image_render( get_post_thumbnail_id( $post->ID ) );

That’s at least as easy for a developer as having a normal image element display, so at this point it shouldn’t be too much of a burden for you and your fellow developers to consistently add all of this functionality to each image on the site as you build it out.

Now go forth and bask in the glory of your mighty works which fully harness the powers of the almighty interwebs!

fortune favors

the bold

We collaborate with tenacious organizations and ambitious people.