high performance images: beautiful shouldn't mean slow
TRANSCRIPT
Average Web Page Weight(June ’15)
Not 37%
Images 63%
Source: HTTP Archive
Images On Average Page
Sep '12 Jun '13 Jun '14 Jun '15
696 KB891 KB
1,128 KB1,312 KB
50 Reqs 56 Reqs 55 Reqs 55 Reqs
Requests KB
+189%
Source: HTTP Archive
What Can you do?Image
Compression
Choose The Right Format
Control Quality
Image Loading
Use Responsive Images
Prioritize Critical Content
Image Operations
Encode Well
Transcode in Proxy
What Can you do?Image
Compression
Choose The Right Format
Control Quality
Image Loading
Use Responsive Images
Prioritize Critical Content
Image Operations
Encode Well
Transcode in Proxy
PNG
19 Years Old (1996)
8-32 bit color palettes
Alpha Transparency
Non-patented
Credit: Wikimedia
GIF -> PNG = 21% Savings
PNG File Size GIF File Size
Source: Styoan Stefanov, “Give PNG A Chance” (2009)
WebP
0
3.75
7.5
11.25
15
File Size (KB)
JPEG (q75) WebP
9.9 KB
14.4 KB
0
0.825
1.65
2.475
3.3
Bytes Per Pixel
PNG WebP
2.42 bpp
3.27 bpp -26% -31%
Source: Google Studies
New Image Formats
WebP JPEG XR JPEG 2000
SupportChrome, Opera,
Android 4.xIE 10+ Safari on
iOS, OS XSavings
(over JPEG)40-50% ~25% 15-20%
Mime Type image/webp image/vnd.ms-photo Soon: image/jxr image/jp2
Identification Accept: image/webp
Detect IE 10+
DetectSafari 5+
Using Custom Formats Client Side
<script src="picturefill.js"></script> <picture> <source type="image/webp" srcset="book.webp"> <source type="image/vnd.msphoto" srcset="book.jxr"> <img src="book.jpg" alt="a book"> </picture>
Using Custom Formats Server Side, Single URL
GET /book.jpg GET /book.jpg
GET /book.jpg Accept: image/webp
GET /book.webp
OriginCDN/Cache
Using Custom Formats Server Side, Single URL
GET /book.jxr
GET /book.jpg GET /book.jpg
GET /book.jpg Accept: image/webp
GET /book.webp
GET /book.jpg User-Agent: MSIE 10 Accept: image/jxr*
OriginCDN/Cache* Spartan
Quality Scale Is Per Format
Source: Nick Doyle Performance Calendar
Sim
ilarit
y
0.00
0.06
0.13
0.19
0.25
Quality
0 25 50 75 100
JPEGJPEG XRWebP
Download & Shrink
866px
240px
1876px
520px
35,345 Bytes
110,744 Bytes
79% wasted pixels 68% wasted bytes
(~75KB)
Download & Shrink
866px
240px
1876px
520px
79% wasted pixels 68% wasted bytes
79% wasted memory (3MB)
831K Mem Bytes
975,520 * 4(RGBA) = 3.9M Memory Bytes
“...25% of new Android phones have only 512MB of RAM.”
–Jen Fitzpatrick VP of product management for Google Maps
Download & Shrink Processing Times
Source: Tim Kadlec, “Mobile Image Processing”
Implementing Responsive Images
<img src="small.jpg" srcset="large.jpg 1024w, medium.jpg 640w, small.jpg 320w" sizes="(min-width: 36em) 33.3vw, 100vw" alt="A rad wolf">
Implementing Responsive Images
<img src="small.jpg" srcset="large.jpg 1024w, medium.jpg 640w, small.jpg 320w" sizes="(min-width: 36em) 33.3vw, 100vw" alt="A rad wolf"> Hint, Hint…
Implementing Responsive Images
<img src="small.jpg" srcset="large.jpg 1024w, medium.jpg 640w, small.jpg 320w" sizes="(min-width: 36em) 33.3vw, 100vw" alt="A rad wolf"> Hint, Hint…
Implementing Responsive Images
<picture> <source media="(min-width: 40em)"
srcset="big.jpg 1x, big-hd.jpg 2x"> <source srcset="small.jpg 1x, small-hd.jpg 2x"> <img src="fallback.jpg" alt=""> </picture>
Implementing Responsive Images
<picture> <source media="(min-width: 40em)"
srcset="big.jpg 1x, big-hd.jpg 2x"> <source srcset="small.jpg 1x, small-hd.jpg 2x"> <img src="fallback.jpg" alt=""> </picture> Use Picturefill
Which Breakpoints To Use? How Big & Complex Are Your Images?
Source: Jason Grigsby, “Sensible Jumps In Responsive Image File Sizes”
Width Height File Size
1 320 213 25K2 453 302 44K3 579 386 65K4 687 458 85K5 786 524 104K6 885 590 124K7 975 650 142K8 990 660 151K
Which Breakpoints To Use? How Big & Complex Are Your Images?
Source: Jason Grigsby, “Sensible Jumps In Responsive Image File Sizes”
point # Width Height File Size
1 320 213 9.0K
2 731 487 29K
3 990 660 40K
Page Content
Not Visible 62%
Visible 38%
On A Typical Page & Desktop Screen…
Image Requests
Not Visible 80%
Visible 20%
Lazy Load Images
<img src="book.jpg" alt="A Book">
<img src="1px.gif" data-src="book.jpg" alt="A Book" onload="loadImage(this)">
Lazy Load Images<script> function loadImage(img) { var dataSrc = imgs[i].getAttribute("data-src"); if (dataSrc && isAboveTheFold(img)) { img.onload = null; img.src = dataSrc; } } </script> <img src="1px.gif" data-src="book.jpg" alt="A Book" onload="loadImage(this)">
Lazy Load Images<script> function loadImage(img) { var dataSrc = imgs[i].getAttribute("data-src"); if (dataSrc && isAboveTheFold(img)) { img.onload = null; img.src = dataSrc; } } // Repeat check on viewport changes (scroll, resize...) </script> <img src="1px.gif" data-src="book.jpg" alt="A Book" onload="loadImage(this)">
Defer Load Images<script> function loadImage(img, force) { var dataSrc = imgs[i].getAttribute("data-src"); if (dataSrc && (force || isAboveTheFold(img)) ) { img.onload = null; img.src = dataSrc; } else if (deferOthers) { window.addEventListener("load",
function() { loadImage(img,true)}); }} </script>
HTML Parser<html> <head> <script src="main.js"></script> <link src="styles.css" type="text/css"> </head> <body> <img src="book.jpg"/> <img src="bag.jpg"/> </body> </html>
HTML Parser
main.js
styles.css
book.jpg
bag.jpg
7
<html> <head> <script src="main.js"></script> <link src="styles.css" type="text/css"> </head> <body> <img src="book.jpg"/> <img src="bag.jpg"/> </body> </html>
HTML Parser & Pre-parser
main.js
styles.css
book.jpg
bag.jpg
<html> <head> <script src="main.js"></script> <link src="styles.css" type="text/css"> </head> <body> <img src="book.jpg"/> <img src="bag.jpg"/> </body> </html>
HTML Parser & Pre-parser
main.js
styles.css
book.jpg
bag.jpg
<html> <head> <script src="main.js"></script> <link src="styles.css" type="text/css"> </head> <body> <img src="book.jpg"/> <img src="bag.jpg"/> </body> </html>
HTML Parser & Pre-parser
main.js
styles.css
book.jpg
bag.jpg
11
<html> <head> <script src="main.js"></script> <link src="styles.css" type="text/css"> </head> <body> <img src="book.jpg"/> <img src="bag.jpg"/> </body> </html>
Who Initiates Image Downloads?
CSS 20%
HTML Parser 37%
Pre-parser 43%
Source: Ilya Grigorik, HTTP Archive
HTML Parser & Pre-parser… <img src="1px.gif" data-src=“book.jpg" onload="loadImage(this)"/> <img src="bag.jpg" data-src="bag.jpg" onload="loadImage(this)"/> …
main.js
styles.css
book.jpg
bag.jpg
11
Protip #1: LQIP Low Quality Image Placeholders
<img src=“LowQ.jpg” data-src=”HighQ.jpg” onload=“loadImage(this)”/>
Protip #1: LQIP Low Quality Image Placeholders
LowQ.jpg Quality: 25 Size: 16 KB
<img src=“LowQ.jpg” data-src=”HighQ.jpg” onload=“loadImage(this)”/>
Protip #1: LQIP Low Quality Image Placeholders
LowQ.jpg Quality: 25 Size: 16 KB
<img src=“LowQ.jpg” data-src=”HighQ.jpg” onload=“loadImage(this)”/>
HighQ.jpg Quality: 90 Size: 66 KB
Protip #2: Selective Lazy Load
<img class="responsive-img" sizes="(min-width: 980px) 460px, (min-width: 740px) 340px, 100%" srcset="/w-460/<id>/500.jpg 460w, /w-340/<id>/500.jpg 340w,
/w-445/<id>/500.jpg 445w, /w-605/<id>/620.jpg 605w" src="/w-300/<id>/500.jpg">
Protip #2: Selective Lazy Load
<img class="js-lazy-loaded-image responsive-img"
data-srcset="/w-220/<id>/1000.jpg 220w,
/w-160/<id>/1000.jpg 160w, /w-127/<id>/1000.jpg 127w"
data-sizes="(min-width: 980px) 220px,
(min-width: 740px) 160px, 127px" src="data:image/gif;base64,R0lGODlhAQABAAAAACH 5BAEKAAEALAAAAAABAAEAAAICTAEAOw==">
Protip #2: Selective Lazy Load
JS Disabled
<img class="js-lazy-loaded-image responsive-img"
data-srcset="/w-220/<id>/1000.jpg 220w,
/w-160/<id>/1000.jpg 160w, /w-127/<id>/1000.jpg 127w"
data-sizes="(min-width: 980px) 220px,
(min-width: 740px) 160px, 127px" src="data:image/gif;base64,R0lGODlhAQABAAAAACH 5BAEKAAEALAAAAAABAAEAAAICTAEAOw==">
Tip #5: Encode WellQuality Curve is NOT a Standard
“Save For Web” is NOT just quality
Decoding Is Standard, Encoding Is Not
Notable Deltas: Chroma Subsampling, Per-Region Quality, Lossy PNG, SSIM-Based Quality…
If you use one tool: ImageOptim (benchmark)
Tip #6: Image Management Service
5 breakpoints * 2 Pixel Ratios * 3 Views *5 thumbnails * 100,000 Products/Articles…
And tomorrow?
/q75/w120/book.jpg GET /book.jpg
OriginTranscoder
<Big, High Res Img><Right-Sized Img>
Image Compression
Choose The Right Format
Control Quality
Image Loading
Use Responsive Images
Prioritize Critical Content
Image Operations
Encode Well
Transcode in Proxy
Enforce a Performance Budget
Thank You! Guy Podjarny
@guypod
Guy Podjarny, Tobias Baldauf & Mike McCall
High Performance ImagesSHRINK, LOAD, AND DELIVER IMAGES FOR SPEED
Book Preview (developer.akamai.com/stuff)