To bring you a fast-loading and photo-based home page, I’ve custom-made a responsive image system I call “ideal images.”

Whereas most sites serve one image to fit all screens, PhotoSecrets serves the optimum image for each screen. This minimizes bandwidth and load time, and gets full-resolution images to high-resolution devices.

On the PhotoSecrets home page, each image is drawn first as an empty box. The actual pixel dimensions (including pixel density) are calculated in JavaScript, then the nearest-sized image is requested from the server.

For each image and aspect ratio, up to 50 file sizes are available, ranging from 60px to 2560px wide. Now, each device gets fast-loading AND full resolution images.


HTML

The HTML code for each ideal image looks like this:

<div
  class="img w50vw a1x1"
  data-img="ideal shutterstock-0127554743-a1x1-max2560">

This decodes as:

source:shutterstock
image:0127554743
aspect:1x1 (square)
max:2560 pixels wide

As you can see, this is not an <img> element, so no image file is loaded initially. The CSS draws a box according to the class — in this case at 50% of the viewport width, and as a square (aspect 1x1).

The CSS would be:

.img{
  height:0;
  vertical-align:top;
  font-size:0;
  line-height:0;
  background-color:gray
  background-size:cover;
  background-position:center center;
  background-repeat:no-repeat;
  }
.img.w50vw.a1x1{
  width:50vw;
  padding:0 0 50vw 0;
  }

JavaScript measures the rendered size, and picks the optimum width from an array, up to the maximum available which is 2560 pixels wide.

For example, if the rendered size is 720 pixels wide, the image file will be:

/images/responsive/shutterstock/0127554743/a1x1/shutterstock-0127554743-a1x1-max2560-w720.jpg

JavaScript adds this as a CSS background-image. Voilà, the image pops up in the browser.


The conundrum of responsive images

Serving appropriately sized images requires a technique known as “responsive images.” Although simple in theory, it is a conundrum in practice.

Over half the web is images, yet there is no direct way for a device to request a specific image size.

For each image on a HTML page, we know the relative size but not the rendered size. For example, a full-width image will be rendered at 320 pixels wide on an iPhone 3, and 1125 pixels wide on an iPhone X. In my tests with optimized and compressed images, a 320-pixel-wide square image is 21 KB in size, and a 1125-pixel-wide image is 205 KB. That’s ten times as large.

iPhone widths

DeviceWidthRetinaPixels
iPhone 1–33201320
iPhone 4–53202640
iPhone 6–83752750
iPhone 6+–8+4142.61080
iPhone X37531125
Notes: Width=CSS width (or points)

Serving the large file to the small screen creates many problems:

  • 90% of the data is wasted
  • the transfer time is ten times longer than it should be
  • the user pays ten times more for bandwidth
  • the page render time is ten times longer
  • the display memory is unnecessarily bloated and slower

Conversely, serving the small file to the large screen gives the user an image that is 1/10 the resolution it should be.

To serve fast websites with high quality images, some intelligence is required. There are several approaches available, but they all have drawbacks. Let’s take a look.

IMG srcset

In HTML5, an <img> (image) tag can include the attribute srcset. This is a list of one or more strings separated by commas indicating a set of possible image sources for the user agent to use. The accompanying <sizes> tag can use media queries.

Example:

<img src="clock-img-200.png"
alt="Clock"
srcset="clock-img-200.png 200w, clock-img-400.png 400w"
sizes="(min-width: 600px) 200px, 50vw">

Although this is generally what I wanted, I calculated that around 50 images sizes are required to cater for most devices. With one <srcset> and one <sizes> attribute per image, that is 100 lines of code — per image. That would result in bloated and ugly HTML code.

PICTURE srcset

A similar approach uses the HTML <picture> tag, which is a container used to specify multiple <source> elements for a specific <img> contained in it. The <source> element can use media queries.

Example:

<picture>
<source srcset="clock-img-600.png" media="(min-width: 600px)">
<img src="clock-img-200.png" alt="Clock">
</picture>

However, this still still has the bloat problem above, and the <picture> tag is experimental technology.

CSS background

The above approach can be hidden inside the CSS by using the background property and media queries. However, the bloat problem still exists, and if this is a separate CSS file, the image file info is removed from the HTML, which impairs the code readability.

image-set

An extension of the CSS background property is the image-set() function. However, this only addresses pixel density and is an experimental technology.

HTACCESS sniffing

The server could detect the device type based on the HTTP_USER_AGENT string in the Apache .htaccess file, then serve the appropriate file size. However, this is straying too far from the HTML code for my liking.

Compressive images

A highly compressed high resolution image (HiDPI) can be sent, but this still provides high file size and imperfect images.

Progressive images

JPEG 2000 can store multiple sizes. In theory, the HTTP connection could be terminated when the appropriate size is received. However, site loading is faster when the connection is kept alive, so the browser ends up downloading a large file when only a small one may be needed. And JPEG 2000 is an experimental technology with little support.

Progressive JPEGs use interlaced display, like GIFs, to paint higher resolutions, but doesn’t address the rendered size problem.

JavaScript image replacement

This is the approach I took, where code analyzes the rendered size and downloads the optimum image.

“One big drawback to this approach is that using JavaScript means that you will delay image loading until at least the look-ahead parser has finished. This means that images won’t even start downloading until after the pageload event fires.”
Pete LePage, Google Developer Advocate

Fortunately, for me, this is a benefit. My “Lightning Load” approach displays the layout quickly, using gray boxes for the images. Photos can be backfilled after page load using JavaScript. So I’m already doing JS image replacement, and decoding a responsive image is little additional work.

As HTML5Rocks notes, “there are approximately one million JavaScript libraries that do something like the above, and unfortunately none of them are particularly outstanding.”

Adaptive Images

The best off-the-shelf approach I found is Adaptive Images by Matt Wilcox. This uses cookies and htaccess to deliver optimum image file, automatically resized on the server with PHP.

Adaptive Images detects your visitor’s screen size and automatically creates, caches, and delivers device appropriate re-scaled versions of your web page’s embeded HTML images. No mark-up changes needed.”
Matt Wilcox, Adaptive Images

My approach

I opted to write my own code as I wanted particular control. A PHP function finds images tagged as “ideal” and, if files are not already made, gets the original full-resolution image and downsamples it to an array of sizes. A matching JavaScript function measures the rendered size of the image box, chooses the optimum size from the same array, and adds the link as a CSS background-image.

Links

Here are interesting links if you’re looking into this:


You might also like

Next page: PhotoSecrets redesign — Tiles

Add Your Comment

Comment:

Name:

Email (optional):

Submit your comment: