Network Aware Image Optimization

Inian Parameshwaran
Inian Parameshwaran Jun 11, 2019
4 minutes read

Users visit websites from very different network conditions. Some countries are already looking at rolling out 5G whereas parts of other countries are able to come online only because of giant companies beaming down internet from drones, satellites and even hot air balloons! Even users within a single country typically experience varying speeds depending on network congestion levels, the time of day, where they are, etc. I know the exact spot in my train ride where my mobile network goes completely dark. With latencies and bandwidth varying a lot between these different connection types, the experience of users visiting your website would vary a lot too. In this post, we will see how to deliver a good user experience for users on slower networks without compromising the experience of users who are on the fast networks.

Videos already do a good job of adapting to your network speeds if encoded properly. Let’s look at how to optimise images in a more intelligent way using Service workers, the NetInfo API and Dexecure’s optimisation modes.

The NetInfo API

The network information API provides information about the network the user is on like the bandwidth (downlink), round trip time (rtt), connection type (effectiveType) in JavaScript land. Unfortunately this API is only supported in Chrome-like browsers. For other browsers, we will gracefully degrade to sending the original assets.

Dexecure’s optimisation modes

One of Dexecure’s features is to optimise the different assets on your website automatically. We tend to do things which are right in terms of performance and user experience out of the box but we also provide different optimisation levels to nudge our algorithms in the right direction. The four optimisation levels Dexecure supports are none, mild, default and aggressive. The cool thing is that these optimisation modes work across all your different types of assets like images, SVGs, GIFs, CSS, JS, etc. These optimisation modes can be controlled through our page rules or just by adding the right query parameters to the URL. For example a banner image on a page can be losslessly optimised by changing the URL to something like https://xxx.dexecure.net/banner-image.jpg?opt=mild

I am not going much into service workers since they have been talked about a lot already.

Putting it all together

In this service worker script, I am varying different optimisation modes depending on the type of network the user is on. On the fetch handler, we intercept image requests by checking the request.destination. We then modify these URLs to request the right asset from Dexecure. In this way, users on fast internet connections are delivered images which are only optimised by lossless algorithms and users on slower networks are more aggressively optimised. In this gist, I am just using the connection type to decide which variation of the asset is to be delivered to the user. But you can also use the other (combinations of) attributes in the NetInfo API to better segment your users.

This script can further be enhanced by accounting for changes in network conditions. Right now, when the user goes from a 4G environment to a 2G environment, the script downloads a new asset even if the higher quality image is already in the browser cache. We can store the downloaded images in the Service Worker Cache instead and check the cache if a version of the asset corresponding to a faster network is already there. If not, we can make the request to Dexecure and then store the result in the Service worker cache.

What else can you do?

The Save data header (which is also unfortunately Chrome-only) is a way for the user to indicate that he is on a metered connection and wants to download lesser bytes if possible. This is exposed as a header, which means that you can detect and serve different experiences to these users from your server (no service workers required!). This is also exposed in JavaScript via navigator.connection.saveData. Since this is just a Boolean value, it is not as fine-grained as the NetInfo API but you can use both of them together in the previous script to a better understanding of the user’s network conditions.

You can also consider sending Low Quality Image Placeholders (LQIP) for users on very slow connections or for users with the save-data setting enabled.

Connection aware components are another interesting way to go further in this direction.

What are the other ways you can think of to be kind to users with slower connections? Let us know in the comments below.

Stay updated, because #perfmatters!