GIFs Are Forever, Let’s Make Them Better

A presentation at CascadiaJS in June 2024 in Seattle, WA, USA by Tyler Sticka

Slide 1

Slide 1

GIFs are forever… Let’s make them better! CascadiaJS 2024

Slide 2

Slide 2

1987

Slide 3

Slide 3

1995

Slide 4

Slide 4

2005

Slide 5

Slide 5

2015

Slide 6

Slide 6

2024

Slide 7

Slide 7

2024

Slide 8

Slide 8

2024

Slide 9

Slide 9

Slide 10

Slide 10

256 Colors Per Frame?

Slide 11

Slide 11

256 Colors Per Frame?

Slide 12

Slide 12

256 Colors Per Frame? Single-Bit Transparency?

Slide 13

Slide 13

256 Colors Per Frame? Single-Bit Transparency?

Slide 14

Slide 14

256 Colors Per Frame? Single-Bit Transparency? Image Sequences?

Slide 15

Slide 15

256 Colors Per Frame? Single-Bit Transparency? Image Sequences?

Slide 16

Slide 16

Slide 17

Slide 17

Slide 18

Slide 18

Here is a cool example of a silent GIF I like this loop of a kitty with a soccer ball even better, though

Slide 19

Slide 19

“Using emoji, emoticons and GIFs in a texted conversation instantly signals the difference between sincerity and a joke or sarcasm.” — Jenna Wortham The New York Times

Slide 20

Slide 20

Slide 21

Slide 21

Slide 22

Slide 22

Slide 23

Slide 23

Tired: GIF, an image file format

Slide 24

Slide 24

Wired: GIF, a shorthand for silent, inline animated clips

Slide 25

Slide 25

<img src=”clip.gif” … … width=”…” height=”…”>

Slide 26

Slide 26

Animated Silent Looping Inline Autoplay

Slide 27

Slide 27

Animated Silent Looping Inline Autoplay

Slide 28

Slide 28

Animated Silent Looping Inline Autoplay Text alternative

Slide 29

Slide 29

Animated Silent Looping Inline Autoplay Text alternative

Slide 30

Slide 30

Of the top 1,000,000 home pages… Source: webaim.org/projects/million (March 2024)

Slide 31

Slide 31

Of the top 1,000,000 home pages… 54.5% were missing alt text! Source: webaim.org/projects/million (March 2024)

Slide 32

Slide 32

Of the top 1,000,000 home pages… 54.5% were missing alt text! Source: webaim.org/projects/million (March 2024)

Slide 33

Slide 33

Describe the image for blind and low-vision users A peeved groundhog repeatedly shouts “Hey!…Hey!…Hey!”

Slide 34

Slide 34

cloudfour.com/thinks/write-alt-text-like-youre-talking-to-a-friend

Slide 35

Slide 35

<img src=”clip.gif” … … width=”…” height=”…” alt=”A hypothetical example for this slide.” />

Slide 36

Slide 36

Animated Silent Looping Inline Autoplay Text alternative

Slide 37

Slide 37

Animated Text alternative Silent Looping Higher quality Inline Autoplay

Slide 38

Slide 38

Animated Text alternative Silent Looping Higher quality Smaller size Inline Autoplay

Slide 39

Slide 39

Animated Text alternative Silent Looping Higher quality Smaller size Inline Autoplay

Slide 40

Slide 40

WebP Since 2014 (v32) Since 2018 (v18) Since 2019 (v65) Since 2022 (v16.0)

Slide 41

Slide 41

WebP Since 2014 (v32) Since 2018 (v18) Since 2019 (v65) Since 2022 (v16.0) AVIF Since 2020 (v85) This Year (v121) Since 2021 (v93) Since 2023 (v16.4)

Slide 42

Slide 42

974 KB 226 KB 66 KB GIF WebP AVIF

Slide 43

Slide 43

974 KB r e h g Hi ! ! y t i qual 226 KB 66 KB GIF WebP AVIF

Slide 44

Slide 44

Local libvips sharp gif2webp FFmpeg ImageMagick

Slide 45

Slide 45

s e c i v r e S Local libvips sharp gif2webp FFmpeg ImageMagick : e r U S o l c s i d L Ful ! T n e i l c They’rE a cloudfour.com/made …and many others!

Slide 46

Slide 46

<img src=”clip.avif” clip.gif … … width=”…” height=”…” alt=”A hypothetical animated AVIF asset.” />

Slide 47

Slide 47

<img src=”clip.avif” … … width=”…” height=”…” alt=”A hypothetical animated AVIF asset.” />

Slide 48

Slide 48

<picture> <source type=”image/avif” srcset=”clip.avif” /> <source type=”image/webp” srcset=”clip.webp” /> <img src=”clip.gif” … … width=”…” height=”…” alt=”Possibly AVIF, WebP or GIF” /> </picture>

Slide 49

Slide 49

Animated Text alternative Silent Looping Higher quality Smaller size Inline Autoplay

Slide 50

Slide 50

Animated Text alternative Silent Looping Higher quality Smaller size Inline Motion prefs Autoplay

Slide 51

Slide 51

Animated Text alternative Silent Looping Higher quality Smaller size Inline Motion prefs Autoplay

Slide 52

Slide 52

clip.avif f i v a . c i t a t s

Slide 53

Slide 53

<picture> <source media=”(prefers-reduced-motion: no-preference)” srcset=”clip.avif” /> <img src=”static.avif” … … width=”…” height=”…” alt=”This meme may or may not be animated.” /> </picture>

Slide 54

Slide 54

<picture> <source media=”(prefers-reduced-motion: no-preference)” srcset=”clip.avif” /> <img src=”static.avif” … … width=”…” height=”…” alt=”This meme may or may not be animated.” /> </picture>

Slide 55

Slide 55

<picture> <source media=”(prefers-reduced-motion: no-preference)” srcset=”clip.avif” /> <img src=”static.avif” … … width=”…” height=”…” alt=”This meme may or may not be animated.” /> </picture>

Slide 56

Slide 56

Animated Text alternative Silent Looping Higher quality Smaller size Inline Motion prefs Autoplay

Slide 57

Slide 57

Animated Text alternative Silent Looping Higher quality Smaller size Inline Motion prefs Autoplay Playback control

Slide 58

Slide 58

Animated Text alternative Silent Looping Higher quality Smaller size Inline Motion prefs Autoplay Playback control

Slide 59

Slide 59

Motion sensitivity often depends on content + context

Slide 60

Slide 60

Slide 61

Slide 61

Slide 62

Slide 62

Slide 63

Slide 63

Slide 64

Slide 64

<video controls>

Slide 65

Slide 65

MP4 (H.264) Since 2010 (v4) Since 2015 (v12) Since 2015 (v35) Since 2008 (v3.2)

Slide 66

Slide 66

MP4 Since 2010 (v4) Since 2015 (v12) Since 2015 (v35) Since 2008 (v3.2) WebM Since 2013 (v25) Since 2020 (v79) Since 2014 (v28) Since 2022 (v16.0) (H.264) (VP8 / VP9)

Slide 67

Slide 67

974 KB 226 KB GIF WebP 81 KB 66 KB 59 KB MP4 AVIF WebM (H.264) (VP9)

Slide 68

Slide 68

Local FFmpeg Handbrake Adobe Media Encoder

Slide 69

Slide 69

s e c i v r e S Local FFmpeg Handbrake Adobe Media Encoder : e r U S o l c s i d L Ful ! d E s A i b I’m stiLl cloudfour.com/made …and even more alternatives!

Slide 70

Slide 70

<video controls autoplay loop muted playsinline src=”clip.mp4” … … width=”…” height=”…”> </video>

Slide 71

Slide 71

<video controls autoplay loop muted playsinline src=”clip.mp4” … … width=”…” height=”…”> </video>

Slide 72

Slide 72

<video controls autoplay loop muted playsinline src=”clip.mp4” … … width=”…” height=”…”> </video>

Slide 73

Slide 73

<video controls autoplay loop muted playsinline src=”clip.mp4” … … width=”…” height=”…”> </video>

Slide 74

Slide 74

<video controls autoplay loop muted playsinline src=”clip.mp4” … … width=”…” height=”…”> </video>

Slide 75

Slide 75

<video controls autoplay loop muted playsinline src=”clip.mp4” … … width=”…” height=”…”> </video>

Slide 76

Slide 76

<video controls autoplay loop muted playsinline src=”clip.mp4” … … width=”…” height=”…”> </video>

Slide 77

Slide 77

<video controls autoplay loop muted playsinline … … width=”…” height=”…”> <source src=”clip.webm” type=”video/webm” /> <source src=”clip.mp4” type=”video/mp4” /> </video>

Slide 78

Slide 78

Slide 79

Slide 79

Slide 80

Slide 80

Slide 81

Slide 81

Slide 82

Slide 82

Animated Text alternative Silent Looping Higher quality Smaller size Inline Motion prefs Autoplay Playback control

Slide 83

Slide 83

Animated Text alternative Silent Looping Higher quality Smaller size Inline Motion prefs Autoplay Playback control

Slide 84

Slide 84

<img src=”clip.avif” … … width=”…” height=”…” alt=”I have alternative text!” />

Slide 85

Slide 85

<img src=”clip.avif” … … width=”…” height=”…” alt=”I have alternative text!” /> <video controls autoplay loop muted playsinline src=”clip.mp4” width=”…” height=”…”> </video>

Slide 86

Slide 86

<video controls autoplay loop muted playsinline src=”clip.mp4” width=”…” height=”…”> </video>

Slide 87

Slide 87

<video controls autoplay loop muted playsinline src=”clip.mp4” width=”…” height=”…”> </video>

Slide 88

Slide 88

Animated Text alternative Silent Looping Higher quality Smaller size Inline Motion prefs Autoplay Playback control

Slide 89

Slide 89

Animated Text alternative Silent Looping Higher quality Smaller size Inline Motion prefs Autoplay Playback control

Slide 90

Slide 90

Animated Text alternative Silent Looping Higher quality Smaller size Inline Motion prefs Autoplay Playback control

Slide 91

Slide 91

<video controls autoplay loop muted playsinline … … width=”…” height=”…”> <source src=”clip.webm” type=”video/webm” /> <source src=”clip.mp4” type=”video/mp4” /> </video>

Slide 92

Slide 92

<figure> <video controls autoplay loop muted playsinline … … width=”…” height=”…”> <source src=”clip.webm” type=”video/webm” /> <source src=”clip.mp4” type=”video/mp4” /> </video> <figcaption> An example of a video used as a GIF. </figcaption> </figure>

Slide 93

Slide 93

<video controls autoplay loop muted playsinline … … width=”…” height=”…” aria-label=”An example of a video used as a GIF.”> <source src=”clip.webm” type=”video/webm” /> <source src=”clip.mp4” type=”video/mp4” /> </video>

Slide 94

Slide 94

<video controls autoplay loop muted playsinline … … width=”…” height=”…” aria-labelledby=”clip-label”> <source src=”clip.webm” type=”video/webm” /> <source src=”clip.mp4” type=”video/mp4” /> </video> <div id=”clip-label” aria-hidden=”true”> An example of a video used as a GIF. </div>

Slide 95

Slide 95

Animated Text alternative Silent Looping Higher quality Smaller size Inline Motion prefs Autoplay Playback control

Slide 96

Slide 96

Animated Text alternative Silent Looping Higher quality Smaller size Inline Motion prefs Autoplay Playback control

Slide 97

Slide 97

<picture> <source media=”(prefers-reduced-motion: no-preference)” srcset=”clip.avif” /> <img src=”static.avif” … … width=”…” height=”…” alt=”This meme may or may not be animated.” /> </picture>

Slide 98

Slide 98

<video controls autoplay loop muted playsinline … … width=”…” height=”…” aria-labelledby=”clip-label”> <source src=”clip.webm” type=”video/webm” media=”(prefers-reduced-motion: no-preference)” /> <source src=”clip.mp4” type=”video/mp4” media=”(prefers-reduced-motion: no-preference)” /> </video> <div id=”clip-label” aria-hidden=”true”> An example of a video used as a GIF. </div>

Slide 99

Slide 99

Slide 100

Slide 100

Slide 101

Slide 101

Slide 102

Slide 102

Web components!

Slide 103

Slide 103

<video controls autoplay loop muted playsinline … … width=”…” height=”…” aria-labelledby=”clip-label”> <source src=”clip.webm” type=”video/webm” /> <source src=”clip.mp4” type=”video/mp4” /> </video> <div id=”clip-label” aria-hidden=”true”> An example of a video used as a GIF. </div>

Slide 104

Slide 104

<video controls loop autoplay muted playsinline … … width=”…” height=”…” aria-labelledby=”clip-label”> <source src=”clip.webm” type=”video/webm” /> <source src=”clip.mp4” type=”video/mp4” /> </video> <div id=”clip-label” aria-hidden=”true”> An example of a video used as a GIF. </div>

Slide 105

Slide 105

<gif-like> <video controls loop muted playsinline … … width=”…” height=”…” aria-labelledby=”clip-label”> <source src=”clip.webm” type=”video/webm” /> <source src=”clip.mp4” type=”video/mp4” /> </video> <div id=”clip-label” aria-hidden=”true”> An example of a video used as a GIF. </div> </gif-like>

Slide 106

Slide 106

class GifLike extends HTMLElement { static motionQuery = window.matchMedia( “(prefers-reduced-motion: no-preference)” ); connectedCallback() { this.video = this.querySelector(“video”); GifLike.motionQuery.addEventListener(“change”, (query) => { this.toggle(query.matches); }); this.toggle(GifLike.motionQuery.matches); } toggle(state) { if (state) { this.video.play(); } else { this.video.pause(); } } } customElements.define(“gif-like”, GifLike);

Slide 107

Slide 107

class GifLike extends HTMLElement { static motionQuery = window.matchMedia( “(prefers-reduced-motion: no-preference)” ); connectedCallback() { this.video = this.querySelector(“video”); GifLike.motionQuery.addEventListener(“change”, (query) => { this.toggle(query.matches); }); this.toggle(GifLike.motionQuery.matches); } toggle(state) {

Slide 108

Slide 108

class GifLike extends HTMLElement { static motionQuery = window.matchMedia( “(prefers-reduced-motion: no-preference)” ); connectedCallback() { this.video = this.querySelector(“video”); GifLike.motionQuery.addEventListener(“change”, (query) => { this.toggle(query.matches); }); this.toggle(GifLike.motionQuery.matches); } toggle(state) {

Slide 109

Slide 109

class GifLike extends HTMLElement { static motionQuery = window.matchMedia( “(prefers-reduced-motion: no-preference)” ); connectedCallback() { this.video = this.querySelector(“video”); GifLike.motionQuery.addEventListener(“change”, (query) => { this.toggle(query.matches); }); this.toggle(GifLike.motionQuery.matches); } toggle(state) {

Slide 110

Slide 110

class GifLike extends HTMLElement { static motionQuery = window.matchMedia( “(prefers-reduced-motion: no-preference)” ); connectedCallback() { this.video = this.querySelector(“video”); GifLike.motionQuery.addEventListener(“change”, (query) => { this.toggle(query.matches); }); this.toggle(GifLike.motionQuery.matches); } toggle(state) {

Slide 111

Slide 111

class GifLike extends HTMLElement { static motionQuery = window.matchMedia( “(prefers-reduced-motion: no-preference)” ); connectedCallback() { this.video = this.querySelector(“video”); GifLike.motionQuery.addEventListener(“change”, (query) => { this.toggle(query.matches); }); this.toggle(GifLike.motionQuery.matches); } toggle(state) {

Slide 112

Slide 112

class GifLike extends HTMLElement { static motionQuery = window.matchMedia( “(prefers-reduced-motion: no-preference)” ); connectedCallback() { this.video = this.querySelector(“video”); GifLike.motionQuery.addEventListener(“change”, (query) => { this.toggle(query.matches); }); this.toggle(GifLike.motionQuery.matches); } toggle(state) {

Slide 113

Slide 113

this.toggle(query.matches); }); this.toggle(GifLike.motionQuery.matches); } toggle(state) { if (state) { this.video.play(); } else { this.video.pause(); } } } customElements.define(“gif-like”, GifLike);

Slide 114

Slide 114

toggle(state) { if (state) { this.video.play(); } else { this.video.pause(); } } } customElements.define(“gif-like”, GifLike);

Slide 115

Slide 115

class GifLike extends HTMLElement { static motionQuery = window.matchMedia( “(prefers-reduced-motion: no-preference)” ); connectedCallback() { this.video = this.querySelector(“video”); GifLike.motionQuery.addEventListener(“change”, (query) => { this.toggle(query.matches); }); this.toggle(GifLike.motionQuery.matches); } toggle(state) { if (state) { this.video.play(); } else { this.video.pause(); } } } customElements.define(“gif-like”, GifLike); ! e r E h d SHaRe gist.github.com/tylersticka

Slide 116

Slide 116

Slide 117

Slide 117

Slide 118

Slide 118

Slide 119

Slide 119

Slide 120

Slide 120

Slide 121

Slide 121

Animated Text alternative Silent Looping Higher quality Smaller size Inline Motion prefs Autoplay Playback control

Slide 122

Slide 122

Animated Text alternative Silent Looping Higher quality Smaller size Inline Motion prefs Autoplay Playback control

Slide 123

Slide 123

Animated Text alternative Silent Looping Higher quality Smaller size Inline Motion prefs Autoplay Playback control

Slide 124

Slide 124

Slide 125

Slide 125

web.dev/articles/lazy-loading-video

Slide 126

Slide 126

… … <gif-like src=”…” alt=”…”></gif-like> r o c B e w y t n e v e Enhance SSR or el … r e p l e h e t a l p m e t cMs block or <gif-like> <video controls loop muted playsinline preload=”none” … poster=”…” … … width=”…” height=”…”

Slide 127

Slide 127

Slide 128

Slide 128

Slide 129

Slide 129

Here is a cool example of a silent GIF I like this loop of a kitty with a soccer ball even better, though GIF is a shorthand for silent, inline, animated clips

Slide 130

Slide 130

Alternative text is chronically overlooked low-hanging fruit

Slide 131

Slide 131

974 KB Newer formats work in all modern browsers, load faster and look better 226 KB GIF WebP 81 KB 66 KB 59 KB MP4 AVIF WebM (H.264) (VP9)

Slide 132

Slide 132

Video gives users more control

Slide 133

Slide 133

<gif-like> Web components are a great choice for progressive enhancement </gif-like>

Slide 134

Slide 134

Slide 135

Slide 135

Slide 136

Slide 136

tylersticka.com @$tylersticka$@$social.lol /in/$tylersticka ! m a e t Hire my cloudfour.com