Progressive image rendering

 

 

PROGRESSIVE IMAGE RENDERING

@jmperezperez

About me

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Page Load Time

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Version A

JSDayES

Madrid

2017

Speakers & Schedule

0.0s

0.3s

0.6s

0.9s

1.2s

Version B

JSDayES

Madrid

2017

Speakers & Schedule

0.0s

0.3s

0.6s

0.9s

1.2s

JSDayES

Madrid

2017

Speakers & Schedule

JSDayES

Madrid

2017

JSDayES

Madrid

2017

PROGRESSIVE IMAGE RENDERING

@jmperezperez

User Perceived Performance

PROGRESSIVE IMAGE RENDERING

@jmperezperez

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Speed Index = \int_0^{end} 1 - \frac{VC}{100}
SpeedIndex=0end1VC100Speed Index = \int_0^{end} 1 - \frac{VC}{100}

end = end time in milliseconds

VC = % visually complete

PROGRESSIVE IMAGE RENDERING

@jmperezperez

WebPageTest

PROGRESSIVE IMAGE RENDERING

@jmperezperez

More kilobytes

 

improved perceived performance

can sometimes lead to

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Avoiding this:

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Techniques to improve perceived performance

  • Server-side rendering
  • Critical CSS
  • Async JS
  • Async fonts

PROGRESSIVE IMAGE RENDERING

@jmperezperez

What about images?

PROGRESSIVE IMAGE RENDERING

@jmperezperez

PROGRESSIVE IMAGE RENDERING

@jmperezperez

images

1712kB (66%)

scripts

426kB (16%)

other

464kB (18%)

source: http archive 15 apr 2017

To use or not to use images

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Minimalist | Flat Design

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Not that long ago

Optimise your images

PROGRESSIVE IMAGE RENDERING

@jmperezperez

TIP 1

PROGRESSIVE IMAGE RENDERING

@jmperezperez

right format

gif | png | jpg | webp | <the_next_thing>

PROGRESSIVE IMAGE RENDERING

@jmperezperez

<picture>
    <source type="image/webp" srcset="2700x1209/my-image.webp 2700w, 
                    1024x1024/my-image.webp 1024w, 
                    600x600/my-image.webp 600w" 
            sizes="100vw" />
    <source srcset="2700x1209/my-image.jpg 2700w, 
                    1024x1024/my-image.jpg 1024w, 
                    600x600/my-image.jpg 600w" 
            sizes="100vw" />
    <img class="rsImg" src="600x600/my-image.jpg" alt="My beautiful image" />
</picture>

compression

PROGRESSIVE IMAGE RENDERING

@jmperezperez

· lossless

· perceptual

PROGRESSIVE IMAGE RENDERING

@jmperezperez

1 column

2 columns

3 columns

Responsive

TIP 2

<img sizes="(max-width: 30em) 100vw,
            (max-width: 50em) 50vw,
            calc(33vw - 100px)"
     srcset="profile-200.jpg 200w,
             profile-400.jpg 400w,
             profile-800.jpg 800w,
             profile-1600.jpg 1600w"
     src="profile-400.jpg" alt="Pinchito">

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Challenge: Keeping in sync markup and CSS

Example

RESPONSIVE IMAGES

Lazy-load images

PROGRESSIVE IMAGE RENDERING

@jmperezperez

TIP 3

<img> above the fold

&

Lazy below the fold

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Intersection

Observer

PROGRESSIVE IMAGE RENDERING

@jmperezperez

// load image when it's within 100px of the viewport

const options = {
  rootMargin: '100px'
}

const callback = entries => { 
  entries.forEach(entry => {
    if (entry.intersectionRatio > 0) {
        // load image
    }
  });
};

const observer = new IntersectionObserver(callback, options);
observer.observe(document.querySelector('.lazy-img'));

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Example

INTERSECTION OBSERVER

class LazyImage extends React.Component {

    constructor() {
        this.observer = new IntersectionObserver(entries => {
            if (entries[0].intersectionRatio > 0) {
                // load!
            }
        });
        this.element = null;  /* render() will set it through a ref */
    }

    componentDidMount() {
        this.observer.observe(this.element);
    }

    componentWillUnmount() {
        this.observer.unobserve(this.element);
    }
    ...
}

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Encapsulating in React

INTERSECTION OBSERVER

What to show while the image is loading

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Options

PROGRESSIVE IMAGE RENDERING

PLACEHOLDERS

Nothing

Placeholder

Solid colour

Progressive image loading or "Blur-up"

@jmperezperez

Examples of solid color

PROGRESSIVE IMAGE RENDERING

@jmperezperez

PROGRESSIVE IMAGE RENDERING

@jmperezperez

PROGRESSIVE IMAGE RENDERING

@jmperezperez

PROGRESSIVE IMAGE RENDERING

@jmperezperez

PROGRESSIVE IMAGE RENDERING

@jmperezperez

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Examples of

Progressive Image Loading

PROGRESSIVE IMAGE RENDERING

@jmperezperez

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Medium

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Medium

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Quartz (qz.com)

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Quartz (qz.com)

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Quora

PROGRESSIVE IMAGE RENDERING

@jmperezperez

How it is done

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Markup - Eg Medium

<figure>
  <div>

    <div/> <!-- this div keeps the aspect ratio so
                the placeholder doesn't collapse -->

    <img/> <!-- this is a tiny image with a small
                resolution (e.g. ~27x17) and low quality -->

    <canvas/> <!-- takes the above image and applies a blur filter -->

    <img/> <!-- the large image to be displayed -->

    <noscript/> <!-- fallback for no JS -->

  </div>
</figure>

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Progressive JPEGs

Baseline

Progressive

PROGRESSIVE IMAGE RENDERING

@jmperezperez

With the Progressive JPEG method [...] cognitive fluency is inhibited and the brain has to work slightly harder to make sense of what is being displayed.

— From Progressive image rendering: Good or evil?

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Facebook

Inlining thumbnail image in payload

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Facebook

 

Unfortunately, the standard JPEG header is hundreds of bytes in size. In fact, the JPEG header alone is several times bigger than our entire 200-byte budget. However, excluding the JPEG header, the encoded data payload itself was approaching our 200 bytes.

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Facebook

Header (Quantization Table and Huffman Table) Compressed
Data

Client (mobile app)                                                GraphQL

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Thumbnails

Getting creative with SVGs

PROGRESSIVE IMAGE RENDERING

@jmperezperez

PROGRESSIVE IMAGE RENDERING

@jmperezperez

SVG

PROGRESSIVE IMAGE RENDERING

@jmperezperez

SVG

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Drawing with SVG

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Drawing bitmap images

Canny Edge Detector

PROGRESSIVE IMAGE RENDERING

@jmperezperez

PROGRESSIVE IMAGE RENDERING

@jmperezperez

<svg>
  <polyline points="51,1 61,1 61,2 56,4 56,3"/>
  <polyline points="52,1 50,2 51,3 50,4 50,9 46,10 46,8 48,8 48,9"/>
  <polyline points="61,4 61,5 58,6"/>
  ...
  <polyline points="62,58 61,59 61,60 50,62 50,61 51,61"/>
</svg>
  1. Find edges with canny edge detector
  2. Create lines
  3. Use JS and SVG to animate

How to draw bitmaps

PROGRESSIVE IMAGE RENDERING

@jmperezperez

Should we do this?

Just because you can it doesn't mean you should

PROGRESSIVE IMAGE RENDERING

@jmperezperez

  • Reduce requests
  • Choose the right format and optimise
  • Embrace responsive images
  • Try to lazy load
  • Innovate!

Summary

The Web is fun.

PROGRESSIVE IMAGE RENDERING

@jmperezperez

talk with me for more info

Thanks!

@jmperezperez