The Big Picture

A presentation at beyond tellerrand in May 2022 in Düsseldorf, Germany by Tim Kadlec

Slide 1

Slide 1

THE BIG PICTURE TIM KADLEC @TKADLEC

Slide 2

Slide 2

Slide 3

Slide 3

Slide 4

Slide 4

Slide 5

Slide 5

Slide 6

Slide 6

Slide 7

Slide 7

Slide 8

Slide 8

Slide 9

Slide 9

Slide 10

Slide 10

How quickly does the most important content get displayed on the screen?

Slide 11

Slide 11

Largest Contentful Paint (LCP) How long until the largest piece of content is displayed on the screen.

Slide 12

Slide 12

Slide 13

Slide 13

What counts as “content”? <img> - <image> inside svg - post images of <video> elements - background images - block-level text (h1, h2, p, etc) -

Slide 14

Slide 14

LCP Types 18% 27% 46% Mobile 51% Desktop 31% 27% Text Background Image Image

Slide 15

Slide 15

73% 82% on mobile on desktop

Slide 16

Slide 16

LCP at 75th percentile 2.573s 2.861s 3.071s text image background image

Slide 17

Slide 17

LCP at 75th percentile 2.861s 3.071s image background image

Slide 18

Slide 18

LCP at 75th percentile 2.861s 3.071s image background image

Slide 19

Slide 19

Slide 20

Slide 20

Slide 21

Slide 21

Slide 22

Slide 22

Network HTML JS DOM CSS CSSOM Render Tree Layout Paint

Slide 23

Slide 23

Network HTML JS DOM CSS CSSOM Render Tree Layout Paint

Slide 24

Slide 24

Network HTML JS DOM CSS The browser doesn’t know the image exists until the CSS is downloaded and parsed… CSSOM Render Tree Layout Paint

Slide 25

Slide 25

Network HTML JS DOM CSS …and it won’t request it until it knows it needs to be displayed. CSSOM Render Tree Layout Paint

Slide 26

Slide 26

<link rel=”preload” href=”https://ethicaltraveler.org/ wp-content/themes/et2015/static/images/hero_masthead.jpg” as=”image”/>

Slide 27

Slide 27

Slide 28

Slide 28

Slide 29

Slide 29

Slide 30

Slide 30

Preload helps with discovery, but doesn’t impact priority

Slide 31

Slide 31

Network HTML JS DOM CSS CSSOM Render Tree Layout Paint

Slide 32

Slide 32

Network HTML JS DOM CSS The browser can find <img> elements pretty early…. CSSOM Render Tree Layout Paint

Slide 33

Slide 33

Network HTML JS DOM CSS CSSOM Render Tree Layout Paint …but it doesn’t want those images to delay the more important CSS and JS.

Slide 34

Slide 34

Document: High Scripts: High CSS: High Images: Low Fonts: Medium

Slide 35

Slide 35

Document: Highest Scripts: Normal CSS: Normal Images: Low Fonts: High

Slide 36

Slide 36

Document: Highest Scripts: Low-High CSS: Highest Images: Low-High Fonts: High-Highest

Slide 37

Slide 37

Slide 38

Slide 38

<head> <link rel=”preload” href=”https://ethicaltraveler.org/ wp-content/themes/et2015/static/images/hero_masthead.jpg” as=”image”/>

Slide 39

Slide 39

U … . h o h

Slide 40

Slide 40

Priority Hints to the rescue?

Slide 41

Slide 41

fetchPriority=”high” fetchPriority=”low”

Slide 42

Slide 42

Document: Highest Scripts: Low-High CSS: Highest Images: Low-High Fonts: High-Highest

Slide 43

Slide 43

<link rel=”preload” href=”https://ethicaltraveler.org/ wp-content/themes/et2015/static/images/hero_masthead.jpg” as=”image” fetchpriority=”high”/> </head>

Slide 44

Slide 44

Slide 45

Slide 45

LCP at 75th percentile 2.861s 3.071s image background

Slide 46

Slide 46

Slide 47

Slide 47

Slide 48

Slide 48

Slide 49

Slide 49

Slide 50

Slide 50

/> data-tracking-event=”button-click” data-tracking-info=”{“name”:”clickthrough”,”placeme class=”bose-zoom lazyload” data-lazy=”//assets.bose.com/content/dam/Bose_DAM/Web/consumer_electron alt=”Bose Noise Cancelling Headphones 700” data-sizes=”auto” data-zoom-image=”//assets.bose.com/content/dam/Bose_DAM/Web/consumer_el itemprop=”contentURL” data-srcset=”//assets.bose.com/content/dam/Bose_DAM/Web/consumer_electr

Slide 51

Slide 51

No src?

Slide 52

Slide 52

Network HTML JS DOM CSS CSSOM Render Tree Layout Paint

Slide 53

Slide 53

Network HTML JS DOM CSS CSSOM Render Tree Layout Paint The browser sees an <img>, but with no src, it has no idea what to request until JS is downloaded, parsed and executed.

Slide 54

Slide 54

Markup is your <friend>

Slide 55

Slide 55

From #111 and LCP of ~11.5s

Slide 56

Slide 56

…to #78 and LCP of ~6.3s

Slide 57

Slide 57

<img src=”hero.jpg” loading=”eager”> <img src=”other.jpg” loading=”lazy”>

Slide 58

Slide 58

100 cute kitten photos

Slide 59

Slide 59

Based on viewport

Slide 60

Slide 60

Based on viewport

Slide 61

Slide 61

Based on viewport & connection

Slide 62

Slide 62

4G, 5 total 3G, 8 total 2G, 21 total

Slide 63

Slide 63

Always bet on the browser

Slide 64

Slide 64

//assets.bose.com/content/dam/…

Slide 65

Slide 65

A tale of two domains…

Slide 66

Slide 66

Slide 67

Slide 67

Slide 68

Slide 68

Slide 69

Slide 69

Slide 70

Slide 70

Slide 71

Slide 71

The browser has to establish a connection to the new domain

Slide 72

Slide 72

fi https://res.cloudinary.com /webpagetest /image /upload/ f_auto,q_auto,c_ ll,w_640,h_512/Frame_1_1_dqtcfv.png

Slide 73

Slide 73

Get your buzzword bingo cards ready…..

Slide 74

Slide 74

Get your buzzword bingo cards ready….. Edge Computing

Slide 75

Slide 75

fi https://blog.webpagetest/org/cloudinary /webpagetest /image /upload/ f_auto,q_auto,c_ ll,w_640,h_512/Frame_1_1_dqtcfv.png

Slide 76

Slide 76

// cloudinary.js export default async function (request) { const url = new URL(request.url); url.pathname = url.pathname.substring(11); // remove /cloudinary url.hostname = “res.cloudinary.com”; }; const image = await fetch(url.toString(), request); return image;

Slide 77

Slide 77

// cloudinary.js export default async function (request) { url.pathname = url.pathname.substring(11); // remove /cloudinary url.hostname = “res.cloudinary.com”; };

Slide 78

Slide 78

// cloudinary.js export default async function (request) { return image;

Slide 79

Slide 79

[[edge_functions]] path = “/cloudinary/*” function = “cloudinary.js”

Slide 80

Slide 80

Slide 81

Slide 81

Slide 82

Slide 82

Largest Contentful Paint t n e m e v o r p m i n a e m s m 8 8 4 ~ of

Slide 83

Slide 83

Slide 84

Slide 84

Slide 85

Slide 85

Slide 86

Slide 86

Lots of bandwidth available

Slide 87

Slide 87

Only a couple requests early

Slide 88

Slide 88

fetchPriority=”high” fetchPriority=”low”

Slide 89

Slide 89

<img src=”/images/wpt_home_featureimg.jpg” width=”1414” height=”843” alt=”screenshot of wpt results page” />

Slide 90

Slide 90

<img src=”/images/wpt_home_featureimg.jpg” width=”1414” height=”843” alt=”screenshot of wpt results page” fetchPriority=”high” />

Slide 91

Slide 91

<img

Slide 92

Slide 92

Slide 93

Slide 93

Slide 94

Slide 94

s m 0 4 6 ~ of

Slide 95

Slide 95

First Contentful Paint n o i s s e r g e r n a me s m 5 4 1 ~ f o

Slide 96

Slide 96

Slide 97

Slide 97

Slide 98

Slide 98

Slide 99

Slide 99

3.6s to 2.8s

Slide 100

Slide 100

2.7s to 2.1s

Slide 101

Slide 101

LCP & Images Checklist ⛔ Avoid background images ✅ If you must, then pair background image + preload ✅ Use fetchpriority sparingly for a little boost in Chrome/Edge ✅ Load your LCP images from your main domain ⛔ Don’t lazy-load your LCP image! ✅ Use loading attribute to replace JS lazy-loading

Slide 102

Slide 102

Slide 103

Slide 103

Largest Contentful Paint (LCP)

Slide 104

Slide 104

Slide 105

Slide 105

Slide 106

Slide 106

A more robust measurement Measure image load even when not the LCP Measure in Firefox & Safari

Slide 107

Slide 107

<img class=”post__figure-image” src=”/cloudinary/image/upload/ f_auto,q_auto,c_fill,w_640,h_512/Frame_1_1_dqtcfv.png” srcset=”…” width=”640” height=”512” sizes=”(min-width: 44em) 40rem, 90vw” />

Slide 108

Slide 108

<img class=”post__figure-image” src=”/cloudinary/image/upload/ f_auto,q_auto,c_fill,w_640,h_512/Frame_1_1_dqtcfv.png” srcset=”…” width=”640” height=”512” sizes=”(min-width: 44em) 40rem, 90vw” elementtiming=”post-hero” />

Slide 109

Slide 109

<img elementtiming=”post-hero”

Slide 110

Slide 110

let observer = new PerformanceObserver((list) PerformanceObserver => { for (const entry of list.getEntries()){ console.info(entry); } }); observer.observe({type: type: ‘element’, ‘element’ buffered: true}); true

Slide 111

Slide 111

let observer = new PerformanceObserver((list) => {

Slide 112

Slide 112

Slide 113

Slide 113

Slide 114

Slide 114

Measure in Firefox & Safari

Slide 115

Slide 115

Slide 116

Slide 116

<img src=”hero.jpg” onload=”performance.mark(‘hero1’)”> <script>performance.mark(‘hero2’)</script>

Slide 117

Slide 117

<img class=”post__figure-image” src=”/cloudinary/image/upload/ f_auto,q_auto,c_fill,w_640,h_512/Frame_1_1_dqtcfv.png” srcset=”…” width=”640” height=”512” sizes=”(min-width: 44em) 40rem, 90vw” elementtiming=”post-hero” />

Slide 118

Slide 118

<img class=”post__figure-image” src=”/cloudinary/image/upload/ f_auto,q_auto,c_fill,w_640,h_512/Frame_1_1_dqtcfv.png” srcset=”…” width=”640” height=”512” sizes=”(min-width: 44em) 40rem, 90vw” elementtiming=”post-hero” onload=”performance.mark(‘post-hero2’)” /> <script>performance.mark(‘post-hero3’)</script>

Slide 119

Slide 119

1.377s post-hero3 <script> 1.467s post-hero2 onload

Slide 120

Slide 120

Slide 121

Slide 121

3.542s post-hero3 <script> 5.448s post-hero2 onload

Slide 122

Slide 122

Slide 123

Slide 123

A more robust measurement ✅ Measure image load even when not the LCP ✅ Measure in Firefox & Safari

Slide 124

Slide 124

Why bother?

Slide 125

Slide 125

We’re using metrics supported in two major browsers….

Slide 126

Slide 126

…optimizing for goals (CrUX) based on one browser….

Slide 127

Slide 127

…and measuring with tools that work in one browser.

Slide 128

Slide 128

Meanwhile in Germany…. 47.1% 19.1% 12.8% 6.8%

Slide 129

Slide 129

Web Performance

Slide 130

Slide 130

Remember why we’re doing it….

Slide 131

Slide 131

How easy it to use? Does the functionality meet my needs? Does the functionality work? Is the site fast enough? Is the site available?

Slide 132

Slide 132

“When we design and build our websites with the outliers in mind, whether it’s for performance or even user experience, we build an experience that can be easy for all to access and use — and that’s what the web is about, access and information for all.” - Stephanie Stimac, Program Manager for Edge Developer Experiences

Slide 133

Slide 133

Performance is foundational

Slide 134

Slide 134

THANK YOU! TIM KADLEC @TKADLEC