SPIF - Streaming Progressive Image Format

SPIF is a proof-of-concept image format that makes images look good on all screen resolutions.

SPIF is easy to use, it behaves like a normal image.

The difference between SPIF and normal images is that SPIF looks good on Retina displays and loads fast on mobile browsers.

How SPIF works

A SPIF file contains several versions of the same image at different resolutions.

The SPIF viewer loads the version of the image that's closest to the displayed size.

The SPIF viewer saves bandwidth by not loading the larger versions of the image.

Tech details

The SPIF viewer tries to fill each screen pixel with at least one image pixel. To do that, the SPIF viewer loads the version of the image with dimensions larger or equal to the image's screen dimensions.

The SPIF format starts with a header that tells the offsets and sizes of the images in the SPIF. The images are stored smallest first, but there are no image size restrictions apart from that.

The current viewer prototype loads the image until it finds a version that's large enough to cover the screen area used by the image. For less bandwidth use, the viewer can load just the header and first thumbnail, and get the wanted size image with a Range request.

Example

The current uni-resolution way

  1. Take a 7360 x 4912 photo with your fancy new camera.
  2. Use Photoshop to create 2048 x 1366, 1024 x 683 and 128 x 86 pixel versions of the image.
  3. Upload all the different versions to the server.
  4. Write HTML with img-tags that load the version you want.
  5. Images display at too low resolution on Retina MBP or at a too high, bandwidth wasting resolution on mobiles.

The current multi-resolution way

  1. Take a 7360 x 4912 photo with your fancy new camera.
  2. Use Photoshop to create multiple smaller versions of the image.
  3. Upload all the different versions to the server.
  4. Write HTML with img-tags that load the thumbnail version. Set image display size with CSS / dimension attributes.
  5. Write a bit of JavaScript on your page that figures out the screen dimensions of your image and loads the smallest image that's larger than the screen dimensions.
  6. Images display at optimal resolution and use the minimum amount of bandwidth.

The SPIF way

  1. Take a 7360 x 4912 photo with your fancy new camera.
  2. Convert the photo to a SPIF.
  3. Upload the SPIF to the server.
  4. Write HTML with img-tags that load the SPIF image. Set image display size with CSS / dimension attributes.
  5. Done. The browser does the rest.
  6. Images display at optimal resolution and use the minimum amount of bandwidth as long as the server does Range requests.

Problems to solve

Browsers don't load SPIF images natively. This implementation is using JS to load and parse the SPIF files.

Browsers don't cache partial downloads. This can be worked around in JS implementations by using client-side storage.

This viewer loads all the smaller versions of the image on its way to the display resolution. This allows the viewer to get away with only one HTTP request per image. A bandwidth-optimal solution would load the SPIF header, then issue a HTTP Range request to load the wanted image version. A <img srcset> or <picture> -style solution lets you skip the header load (as it basically inlines the header in the HTML), but it's more trouble for web developers.

Downloading a higher resolution version of an image when you zoom into it is possible by hanging onto the header and doing a HTTP Range request for the high-res version. Not implemented yet.

For fast initial display, SPIFs should start with a small 32x32 thumbnail that fits in the same TCP packet with the header. That way you get an image on the first packet. After getting the initial image in, you can skip to realistic-size images.

Maybe SPIF should be a directory of files instead of a single file, that way you could avoid all the browser-server-HTTP problems. Save the images into foo.spif/$POWER_OF_2, browser tries to fetch smallest power of two that covers the image screen area. On failure, request foo.spif/original.

JS implementation issues

Safari doesn't support Blob URLs as image sources, so on Safari the images are loaded using a data URL. On Chrome, the images are loaded using Blob URLs.

XMLHttpRequest with ArrayBuffer response type doesn't give you access to partially loaded data, so you can't stop loading early. As a workaround, this implementation uses responseText which is slower but gives you partial results.

Parsing the images in JavaScript is kinda slow, exacerbated by having to deal with responseText and data URLs. Not a problem on the desktop, but uses more power on mobiles.

Fork me on GitHub