tl;dr: Progressive images render faster on HTTP2, thus increasing perceived performance. Take control of progressive JPEG’s scan layers to show meaningful image content with only 25% of image data sent. Use HTTP2 Server Push for progressive JPEG scan layers to maximize rendering performance for key images.

We Have An Image Problem

Engaging, enraging and encouraging images Images make the world go round: they engage, enrage & encourage us. The web as we know it depends on images. This comes at a price: images make up ~65% of average total bytes per page and have a high correlation to page load time as well as the Speed Index. They also grow by ~200kb year after year. In short, images are heavy and make things slow.

Get Compressin’!

HTTP Archive Statistics The best way to counter negative effects of loading image assets is image compression: using tools such as Kornel Lesiński‘s ImageOptim, which utilizes great libraries like mozjpeg and pngquant, we can reduce image byte size without sacrificing visual quality. And thanks to libraries such as DSSIM we can ensure good visual quality testing different compression levels.

The bad news is that even after reducing image byte size by an average ~29% per image using above tools and even making use of other image formats such as WebP, images are still likely to be the single largest asset type on any given website – closely followed by JavaScript. We need a way to deliver these crucial components for emotional engagement faster.

Enter Multiplexing

HTTP2 Multiplexing Diagram Part of the solution comes from a popular area of the performance conundrum: HTTP2. One of its main benefits is “Multiplexing”: the ability to handle multiple requests and responses at the same time, all using the same TCP connection.

With Multiplexing, website assets load faster. Depending on site architecture, you can also prioritize resources inside a multiplexed connection: flagging assets such as critical CSS with high priority in HTTP2 will make them load sooner. On top of this, pushing not yet requested but crucial assets via HTTP2 Server Push can create a super fast perceived performance, when applied correctly. More on this later.

HTTP2 Waterfall demo on WebPagetest Multiplexing has a curious side-effect when it comes to image loading: certain kinds of images load significantly faster in terms of perceived performance because initial image information can be downloaded in parallel via HTTP2 Multiplexing. Progressive JPEGs and interlaced PNGs benefit from this.

Progressive All The Things

Baseline sequential image loading example

Progressive JPEG image loading example

Progressive and interlaced images are like layer cake: they contain information not in a single stream that renders images from top left to bottom right, but as a stack of layers, each improving on information already shipped in earlier layers. Each individual layer is more lightweight in terms of byte size than the final image.

Since browsers loading website assets via HTTP2 Multiplexing will initiate almost all image downloads simultaneously, the initial, lightweight layers of progressive and interlaced images start rendering much more quickly than sequentially encoded images. Sequentially encoded images render in a windowblind manner: line by line until all image information has been shipped.

John Mellor's SPDY test demo That perceived performance and the Speed Index benefit from delivering progressive JPEG scan layers via HTTP2 Multiplexing was already observed by Google’s John Mellor in 2012. He was experimenting with the SPDY protocol, a precursor to HTTP2. Today, we can improve on this discovery to make progressive images appear even faster:

Take The Power Back! Or: The Scans File

Demo of 10 scan layers Progressively encoded JPEGs contain ten scan layers by default. That means ten iterative layers of image information build on each other to deliver the final visual quality of the image. The first visible scan layer of a progressive JPEG is always highly pixelated and often black & white because it saves on color channel information. If you want to check out how each scan layer looks, use Frédéric Kayser’s “jsk” tool to split a progressive JPEG into its individual scan layers.

Why ten layers? That’s the default setting inside all common JPEG encoders. It’s a compromise between byte size per scan layer, visual quality and helping the JPEG encoder achieve smaller total image file sizes during Huffman table optimizations.

Default scan layers script Unlike PNGs, which use a fixed method called Adam7 encoding to create interlaced layers, we can supplement JPEG encoders with custom directives for scan layer creation: the “-scans” flag for JPEG encoders. You can use it like this with mozjpeg: “cjpeg -quality 75 -scans customscans.txt -outfile output.jpg input.jpg”. Now the JPEG encoder accepts a plaintext file containing your custom commands for scan layer creation.

Each line in the scans file defines a new scan layer. They contain multiple parts of information on color channel, matrix index and lossiness.

The three channels are brightness (‘Y’), blue (‘Cb’) and red (‘Cr’), which respectively have the numbers 0,1 & 2 in the scans file. The matrix index in the scan file goes from 0 to 63, covering a 64-pixel block. (JPEG encoding has a native 8×8 block setting.)

Getting Creative

Custom scan layers script Our goal is to show meaningful image contents sooner while enabling browsers to lay out the site speedily. Our initial scan layer should therefore be lean but meaningful, followed by an as steep increase in perceived visual quality as possible.

The custom scans file displayed here ensures a first scan layer with appropriate colors. At the second scan layer, we already have a highly acceptable preview. Scan layers three and four deliver the necessary color information: the red channel before blue channel since it is likely that red color information is more important to improve visuals, e.g. when showing faces. After the fourth scan layer, the image looks complete and the final fifth scan layer improves only fine high frequency details. These improvements are reflected in an ~6% better Speed Index, reflecting the perceived performance.

WebPagetest results

Progressive JPEG image loading example

The above scans script is only one example of what is possible when customizing progressive JPEG encoding: using the same approach, you could recreate Guy Podjarny’s LQIP technique within progressive JPEGs as shown by Jon Sneyers.

Push! Push! Push!

HTTP2 offers another tool we may use for even faster delivery of image contents: Server Push. In supporting HTTP2-enabled web servers, it is possible to flag individual scan layers of progressive JPEGs with high priority and making the server push those scan layers into the client browsers’ Push cache even before the request for the respective image is initiated. Browsers then can lay out the page and render initial scan layers with the performance of a warmed cache, making users perceive the site’s images as rendering exceptionally fast.

Check out this brilliant article in this year’s Performance Advent Calendar to find out more about HTTP2 Server Push.

WebPagetest results

Progressive JPEG image loading example

This technique should only be used strategically: find out which images are crucial for creating user engagement on your page, e.g. an emotion-evoking hero image or a product overview image, and only apply Server Push to the initial scan layers of those JPEG images. This will enable you to increase user engagement and thus successful conversions without harming overall site asset downloads.


  • Multiple progressive or interlaced images render faster on HTTP2 thanks to Multiplexing
  • Taking control of progressive JPEG creation may give users a better visual experience
  • HTTP2 Server Push can increase perceived performance for important images

Credits: Thanks to Corinna Baldauf for proof & editing. Thanks to Colin Bendell and Yoav Weiss for inspiration & support.


Tobias Baldauf (@tbaldauf) is a web performance evangelist and consultant at Akamai. He creates DevOps tools, image optimization algorithms & speaks at conferences. He's a proud dad, mindful veggy & music lover.

10 Responses to “Even Faster Images using HTTP2 and Progressive JPEGs”

  1. CHUKS

    Love this

    So enlightening and explanative.

    Would really love to know more

  2. Laurent

    Great details. Thanks Tobias.
    What about the added bandwidth due to delivering 10 jpeg instead of 1.
    I tried jsk and the combine size of the 10 jpeg images was about 5 times larger than the initial jpeg.

    Is there a risk of negatively impacting performance due to this large bandwidth increase?

  3. Tobias

    Hey Laurent!

    Is it possible that you are talking about spriting, which means glueing several images together into one sprite to save HTTP requests?

    The technique I’m proposing manipulates individual scan layers within a single image to make its progressive rendering more user friendly.

    However, changing progressive scan layer contents & amount does indeed influence a JPEG’s final file size due to Huffman table optimizations being less efficient when working on fewer, more heavily loaded scan leaders. This small growth in file size is however very much cancelled out by the positive user experience / perceived performance that these JPEGs have.

  4. Not Laurent

    Hey Laurent,

    each of the ten layers should have much less information than the final image. The bandwith difference should be negligible.

    Maybe the output of the jsk tool has cumulated information? (so Image 10 is the full image while layer ten only contains the final details).

  5. fimdomeio

    If indeed Laurent is talking about spriting, is also important to know that with http-2 it becames mostly obsolete since all images are requested in parallel

  6. Bookmarks for December 28th | Chris's Digital Detritus

    […] Performance Calendar » Even Faster Images using HTTP2 and Progressive JPEGs – […]

  7. David

    Utterly marvelous article. Helping clients move to Shopify and their Fastly based CDN, where often enormous images are used, has made this top of mind. I figured it may be to use progressive images again. Thanks for your contributions!

    What’s a good workflow when using OmageOptim?

    I love that tool, but I don’t remember seeing a way to enable progressive jpegs within it. If I feed it a progressive jpeg, though, will it keep it progressive?

    If so, would you know the very highest quality tool for creating the progressive jpeg in the first place? Unfortunately, many of the finer pieces of image editing software I use do not have progressive jpeg as an output default. A shame, really.

    Progressive jpeg seems marvelous, especially in these days of giant hero images, but I really need to keep iterative loss at a minimum. I always like to put quality first, so the prospect of too much generational loss really hurts my mind. 🙂

  8. Rasmus Fløe

    Great insights! 🙂

    One question: how will Server Push ever be practical when using srcSet/picture?

  9. Akshay Ranganath


    With jsk, each image is actually a cumulative scan. So, scan 10 will be the same as the original image.

Leave a Reply

You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
And here's a tool to convert HTML entities