Update: prImages has been superseded by Prime
In my quest to optimize websites for loading performance, I was intrigued by loading images progressively. Progressive image loading is a technique that basically loads a low quality version of an image first and loads a better version over time. (This differs from formats that are progressive on their own). This is a good optimizing technique when your images always have specific dimensions, but is a bit too limited if the images respond to the end-user’s viewport size. When you also need to take the viewport into account, you need another technique: responsive images.
Combining these two techniques was my ultimate goal and because I could not find any scripts that did this, I decided to build my own: prImages.
prImages – progressive responsive images
How it works
prImages watches for page load, browser resizing and scrolling to do its work. Only visible elements are processed (this can be disabled entirely, or per-image). These elements initially load a low-quality version of the original image (this is not handled by the script, but should already be in the HTML).
Then, for all images, processing is done as follows:
- The script checks if the image is currently in view. If it isn’t, it is not considered for (re)processing.
- The script then measures the width of each image container – either a div (as background attribute) or an img – then matches that to common responsive width breakpoints (160, 320, 480, 768, 1024, 1224, 1824 pixels), keeping aspect-ratio in consideration if specified. Matching is done upwards; say an image’s container is 300px wide, an image with 320px width is selected. If the user’s device has a high dpi screen (such as retina screens for Apple devices), this value is doubled.
- On basis of the calculated width, a new url is composed.
- The new image is pre-loaded
- The new image is injected into the html as a replacement of either source or background attribute.
On each event (resize or scroll), the images are reprocessed and adjusted if necessary. As it is a prototype script, each image has its prImage-variables stored, so there is not much overhead on reprocessing. Also, when the container requires a width larger than the image’s original dimensions, no processing is done and the original image is loaded instead.
Width-determination is quite rigorous: if the script cannot determine a width, it will look for all the image’s direct siblings (with the proper classname) if a similar node is found *with* width and use that. If even that fails, it will continue to traverse the DOM upwards (max 3 times, but this can be changed) looking into parent elements until it finds a width. This covers 99% of situations where no width can be determined for the image’s container.
I use prImages on this website (hnldesign.nl) for all images.
You can also check out this demo gallery.
Especially when set-up using an image processor such as phpThumb, you require zero effort for your (responsive) images: everything is generated (and cached) on the fly. Only the first time someone (you or a visitor of your site) views an image, there will be a small delay while the images are resized/cropped.
View/fork the code on Github: https://gist.github.com/c-kick/7cd27db9c9350d186a4b
As I have not intended prImages to be a universal, easily implementable script, but more as a tool for my own work, setting it up can be a bit difficult perhaps. But I’ll try to explain the basics as best I can here:
Specify your responsive images as follows (just the bare essential tags) :
<img decoding="async" src="<full-URL-to-image>" width="<width>" data-alwaysprocess="<true or false>" data-imgprocessor="<URL>" data-path="<PATH>" data-ratio="<ratio>" data-srcwidth="<width>" data-crop="<true or false>" class="primage">
- The class (primage) is essential, as the script will select elements using that class.
data-alwaysprocesswill determine if the image is processed even if it is not in view (yet). Useful for carousels.
data-imgprocessorfull URL to the root of your image processor, such as phpThumb. Note that this is only necessary when you use query-based urls for your images. (See below).
data-paththe path of the image RELATIVE to the image processor, eg the path that will be sent to your img processor url (resulting in something like
data-ratiothe ratio of the image (height/width)
data-srcwidthoriginal width of the source image (thus maximum width)
data-cropwether or not to crop the image (setting a height in accordance with the aspect ratio, and setting the cropping flag for the processor)
I highly recommend automating this using a server-side script (in PHP, for example)
Further required configuration
Specify if you are using rewritten urls or query urls by setting the
UrlType in prImages to either ‘uri’ or ‘query’.
Specify the way you want prImages to construct these url’s inside the
buildUrl function. Per default, it will construct the image url as:
For query-based url’s; configure the options inside the
You can specify a few other variables inside prImages:
HighQualityQuality setting of the replacement images to load (0-100), defaults to 90.
WatchResizeWhether to execute prImages on browser resize (this is debounced). Defaults to true.
WatchScrollWhether to execute prImages when the user scrolls the page (this is debounced). Defaults to true.
WatchLoadWhether to execute prImages when the browser fires the ‘DOMContentLoaded’ event (when all html and CSS content is loaded). Defaults to true.
OnlyVisibleWhether to process only images visible in the viewport. Defaults to true.
SearchSiblingsWhether to search siblings (see above) if a width cannot be determined
ResBrPtsThe responsive breakpoints to match to. Defaults to [160, 320, 480, 768, 1024, 1224, 1824],
DpiMultiplierThe multiplier used for high dpi device-screens. Defaults to
(window.devicePixelRatio >= 1.5) ? 2 : 1.
As said, I never intended this script as a publicly available script, but purely as a personal tool. This implies that the script may contain errors with unforeseen situations, so please excuse me if that happens 😉