High Performance Images: Beautiful Shouldn't Mean Slow (Velocity EU 2015)
-
Upload
guy-podjarny -
Category
Technology
-
view
5.351 -
download
0
Transcript of High Performance Images: Beautiful Shouldn't Mean Slow (Velocity EU 2015)
@guypod
High Performance Images
Beautiful shouldn’t mean Slow
@guypod
Average Web Page Weight(Sep ’15)
Not 37%
Images 63%
Source: HTTP Archive
@guypod
Images On Average Page
Sep '12 Jun '13 Jun '14 Sep '15
696 KB891 KB
1,128 KB
1,383 KB
50 Reqs 56 Reqs 55 Reqs 54 Reqs
Requests KB
~2x!!!
Source: HTTP Archive
@guypod
Images Impact Performance
0
4750
9500
14250
19000
Load Time, 3G Speed
With Images No Images
-30%
@guypod
What Can you do?
@guypod
Image Compression
@guypod
Image Loading
@guypod
Operationalizing Image Optimization
@guypod
Image Compression
@guypod
Average Resource Size
CSS
JS
JPEG
0 10 20 30 40
37 KB
16 KB
9 KB
@guypod
RWD Sites Use Big Images
0
10
20
30
40
JPEG Size PNG Size
7.1 KB
20.8 KB
10 KB
31.6 KB
RWD Not RWD
@guypod
Tip #1: Pick The Right Format
@guypod
Image Formats On The WebOther 2%
GIF 23%
PNG 30%
JPEG 45%
@guypod
GIF• 28 Years Old (1987)
• 256 Colors
• Supports “Simple” Transparency
• Supports Animation
• Patented (now expired)
@guypod
PNG
• 19 Years Old (1996)
• 8-32 bit color palettes
• Alpha Transparency
• Not patented
@guypod
GIF -> PNG = 21% Savings
PNG File Size GIF File Size
Source: Styoan Stefanov, “Give PNG A Chance” (2009)
@guypod
JPEG
• 23 years old (1992)
• RGB Colors (24 bit)
• No Transparency Support
• A Lossy Format
@guypod
Bitmap
JPEG
Bitmap≠
Bitmap
PNG
Bitmap=
Lossy Compression
Lossless Compression
@guypod
JPEG
• 23 years old (1992)
• RGB Colors (24 bit)
• No Transparency Support
• A Lossy Format
@guypod
PNG -> JPG = MUCH SmallerPNG, 574 KB JPG, 110 KB
@guypod
JPEG: No Transparency
JPEGPNG
@guypod
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 PixelPNG WebP
2.42 bpp
3.27 bpp -26% -31%
Source: Google Studies
@guypod
WebP Browser Support
@guypod
BPG• Less than 1 year old
• Lossless & Lossy
• Based on Video encoder HEVC
• H.265, successor of H.264
• Beat WebP & J2K in Mozilla Study
• Free (LGPL)Fabrice Bellard
(Creator of ffmpeg)
@guypod
FLIF• 0 years old
• Lossless
• Progressive
• Responsive Friendly
• No browser support
• Free (GPL)
Source: FLIF Creators Jon Sneyers & Harshad RJ
@guypod
PNG vs Others
Source: Cloudinary
@guypod
New Format Bakeoff
Source: Cloudinary
@guypod
FLIF - 3rd Party Stats
Source: Cloudinary
@guypod
New Image FormatsWebP JPEG XR JPEG 2000
Support Chrome, Opera, Android 4.x IE 10+ Safari on
iOS, OS X
Savings (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+
@guypod
Using Custom FormatsClient 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>
@guypod
Using Custom FormatsServer Side, Single URL
GET /book.jpg GET /book.jpg
OriginCDN/Cache
@guypod
Using Custom FormatsServer Side, Single URL
GET /book.jpg GET /book.jpg
GET /book.jpg Accept: image/webp
GET /book.webp
OriginCDN/Cache
@guypod
Using Custom FormatsServer Side, Single URL
GET /book.jxr
GET /book.jpg GET /book.jpg
GET /book.webp
GET /book.jpg User-Agent: MSIE 10 Accept: image/jxr*
OriginCDN/Cache
* Spartan
GET /book.jpg Accept: image/webp
@guypod
Tip #1: Pick The Right Format
@guypod
Tip #2: Control Quality
@guypod
JPEG Quality
High Quality
Low Quality
@guypod
Quality: 90Size: 66 KB
Quality: 75 Size: 37 KB
Quality: 40Size: 21 KB
Quality: 25 Size: 16 KB
@guypod
Quality: 90 Size: 66 KB
Quality: 75Size: 37 KB
Quality: 40Size: 21 KB
Quality: 25Size: 16 KB
@guypod
Quality Scale Is Per Format
Source: Nick Doyle Performance Calendar
Sim
ilarit
y
0.00
0.06
0.13
0.19
0.25
Quality0 25 50 75 100
JPEGJPEG XRWebP
@guypod
Detecting Excessive QualityWebPageTest
• Some stats about image quality on the web?
@guypod
Detecting Excessive QualityPageSpeed Insights
• Some stats about image quality on the web?
@guypod
Tip #2: Control Quality
@guypod
SIZE Doesn’t matter
It’s all about
TECHNIQUE
@guypod
Image Loading
@guypod
Tip #3: Size Images To Device
@guypod
Download & Shrink
102 KB 1876 × 520
(975,520 pixels)
@guypod
Download & Shrink
866px
240px
1876px
520px
.hp07img {width: 100%}
@guypod
Download & Shrink
866px
240px
1876px
520px
207,840 pixels
975,520 pixels
79% wasted pixels (~770K)
@guypod
Download & Shrink
866px
240px
1876px
520px
35,345 Bytes
110,744 Bytes
79% wasted pixels 68% wasted bytes
(~75KB)
@guypod
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
@guypod
54IMAGES
On an Average Page
@guypod
– Jen Fitzpatrick, Google Maps
“...25% of new Android phones have only 512MB of RAM.”
@guypod
Download & ShrinkProcessing Times
Source: Tim Kadlec, “Mobile Image Processing”
@guypod
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">
@guypod
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…
@guypod
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…
@guypod
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>
@guypod
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
@guypod
Which Breakpoints To Use?What Are your Users Using?
@guypod
Which Breakpoints To Use?How Big & Complex Are Your Images?
• Your Analytics
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
@guypod
Which Breakpoints To Use?How Big & Complex Are Your Images?
• Your Analytics
point # Width Height File Size
1 320 213 9.0K2 731 487 29K3 990 660 40K
Source: Jason Grigsby, “Sensible Jumps In Responsive Image File Sizes”
@guypod
FLIF Progressive “Breakpoints”
@guypod
1969x1307 pixels 299,643 bytes
FLIF Progressive “Breakpoints”
@guypod
1969x1307 pixels All 299,643 bytes
653x985 pixels (1:2) First 80,389 bytes
FLIF Progressive “Breakpoints”
@guypod
1969x1307 pixels All 299,643 bytes
653x985 pixels (1:2) First 80,389 bytes
492x326 pixels (1:4) First 37,014 bytes
FLIF Progressive “Breakpoints”
@guypod
Tip #3: Size Images To Device
@guypod
Tip #4: Prioritize Critical Images
@guypod
Below The Fold
Etsy 82%
Velocity 91%
Guardian 92%
@guypod
Page Content
Not Visible 62%
Visible 38%
On A Typical Page & Desktop Screen…
Image Requests
Not Visible 80%
Visible 20%
@guypod
Download & Hide
@guypod
Download & Hide
img {display: none}
@guypod
Download & Hide
Image Requests 79 78
Image Weight 2,258 KB 2,251 KB
@guypod
Lazy Load Images
<img src="book.jpg" alt="A Book">
<img src="1px.gif" data-src="book.jpg" alt="A Book" onload="loadImage(this)">
@guypod
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)">
@guypod
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)">
@guypod
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>
@guypod
The Infamous
PRELOADER
@guypod
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>
@guypod
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>
@guypod
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>
@guypod
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>
@guypod
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>
@guypod
Who Initiates Image Downloads?
CSS 20%
HTML Parser 37%
Pre-parser 43%
Source: Ilya Grigorik, HTTP Archive
@guypod
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
@guypod
HTTP/1.1 HTTP/2
@guypod
ExcessImages
Pre-ParserBoost
@guypod
Protip #1: LQIPLow Quality Image Placeholders
<img src=“LowQ.jpg” data-src=“HighQ.jpg” onload=“loadImage(this)”>
@guypod
Protip #1: LQIPLow Quality Image Placeholders
LowQ.jpg Quality: 25Size: 16 KB
<img src=“LowQ.jpg” data-src=“HighQ.jpg” onload=“loadImage(this)”>
@guypod
Protip #1: LQIPLow Quality Image Placeholders
LowQ.jpg Quality: 25Size: 16 KB
HighQ.jpg Quality: 90Size: 66 KB
<img src=“LowQ.jpg” data-src=“HighQ.jpg” onload=“loadImage(this)”>
@guypod
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">
@guypod
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==">
@guypod
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==">
@guypod
Tip #4: Prioritize Critical
Images
@guypod
Operationalizing Image Optimization
@guypod
Tip #5: Encode Well• Quality 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)
@guypod
Tip #6: Use Image Management Service
5 breakpoints * 2 Pixel Ratios * 3 Views *5 thumbnails * 100,000 Products/Articles…
And tomorrow?
@guypod
Tip #6: Use 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>
@guypod
Image Manager
@guypod
Cloudinary
@guypod
What Can You Do?
Enforce a Performance Budget
Image Compression
Choose The Right Format
Control Quality
Image Loading
Use Responsive Images
Prioritize Critical Content
Image Operations
Encode Well
Transcode in Proxy
@guypod
Thank You!Guy Podjarny
@guypod
Reminder Free HPI Link:http://bit.ly/hpi-preview