GIFs areforever…
Let’s makethem better!
CascadiaJS 2024

1987

1995

2005

2015

2024

2024

2024

256 Colors Per Frame?

256 Colors Per Frame?

256 Colors Per Frame?
Single-Bit Transparency?

256 Colors Per Frame?
Single-Bit Transparency?

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

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

Here is a cool example ofa silent GIF

I like this loop of a kitty
with a soccer ball evenbetter, though

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

Tired:

GIF, an image
file format

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

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

Animated
SilentLooping
InlineAutoplayAnimated
SilentLooping
InlineAutoplayAnimated
SilentLooping
InlineAutoplay

Text alternativeAnimated
SilentLooping
InlineAutoplay

Text alternativeOf the top 1,000,000
home pages…

Source: webaim.org/projects/million (March 2024)

Of the top 1,000,000
home pages…

54.5%
were missing
alt text!

Source: webaim.org/projects/million (March 2024)

Of the top 1,000,000
home pages…

54.5%
were missing
alt text!

Source: webaim.org/projects/million (March 2024)

Describe the image for blind andlow-vision users
A peeved groundhog repeatedly
shouts “Hey!…Hey!…Hey!”cloudfour.com/thinks/write-alt-text-like-youre-talking-to-a-friend

<img src="clip.gif"…
…
width="..."
height="..."
alt="A hypothetical example for this slide." />Animated
SilentLooping
InlineAutoplay

Text alternativeAnimated

Text alternative

SilentLooping

Higher quality

InlineAutoplayAnimated

Text alternative

SilentLooping

Higher quality
Smaller size

InlineAutoplayAnimated

Text alternative

SilentLooping

Higher quality
Smaller size

InlineAutoplay

WebP

Since 2014(v32)

Since 2018(v18)

Since 2019(v65)

Since 2022(v16.0)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)

974 KB

226 KB
66 KB

GIF

WebP

AVIF

974 KB
r
e
h
g
Hi
!
!
y
t
i
qual

226 KB
66 KB

GIF

WebP

AVIF

Locallibvipssharp
gif2webp
FFmpegImageMagick

s
e
c
i
v
r
e
S

Local

libvipssharp
gif2webp
FFmpegImageMagick

:
e
r
U
S
o
l
c
s
i
d
L
Ful
!
T
n
e
i
l
c
They’rE acloudfour.com/made…and many others!

<img src="clip.avif"
clip.gif…
…
width="..."
height="..."
alt="A hypothetical animated AVIF asset." /><img src="clip.avif"…
…
width="..."
height="..."
alt="A hypothetical animated AVIF asset." /><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>Animated

Text alternative

SilentLooping

Higher quality
Smaller size

InlineAutoplayAnimated

Text alternative

SilentLooping

Higher quality
Smaller size

InlineMotion prefs

AutoplayAnimated

Text alternative

SilentLooping

Higher quality
Smaller size

InlineMotion prefs

Autoplayclip.avif

f
i
v
a
.
c
i
t
a
t
s

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

Text alternative

SilentLooping

Higher quality
Smaller size

InlineMotion prefs

AutoplayAnimated

Text alternative

SilentLooping

Higher quality
Smaller size

InlineMotion prefs

Autoplay

Playback control

Animated

Text alternative

SilentLooping

Higher quality
Smaller size

InlineMotion prefs

Autoplay

Playback control

Motion sensitivity
often depends oncontent + context

≠

≠

<video controls>

MP4(H.264)Since 2010(v4)

Since 2015(v12)

Since 2015(v35)

Since 2008(v3.2)

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)

974 KB

226 KB

GIF

WebP

81 KB

66 KB

59 KB

MP4

AVIF

WebM

(H.264)(VP9)

LocalFFmpegHandbrake
Adobe Media
Encoder

s
e
c
i
v
r
e
S

Local

FFmpegHandbrake
Adobe Media
Encoder

:
e
r
U
S
o
l
c
s
i
d
L
Ful
!
d
E
s
A
i
b
I’m stiLlcloudfour.com/made…and even more alternatives!

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

SilentLooping

Higher quality
Smaller size

InlineMotion prefs

Autoplay

Playback control

Animated

Text alternative

SilentLooping

Higher quality
Smaller size

InlineMotion prefs

Autoplay

Playback control

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

Text alternative

SilentLooping

Higher quality
Smaller size

InlineMotion prefs

Autoplay

Playback control

Animated

Text alternative

SilentLooping

Higher quality
Smaller size

InlineMotion prefs

Autoplay

Playback control

Animated

Text alternative

SilentLooping

Higher quality
Smaller size

InlineMotion prefs

Autoplay

Playback control

<video controls autoplay loop muted playsinline…
…
width="..." height="..."><source src="clip.webm" type="video/webm" /><source src="clip.mp4" type="video/mp4" /></video><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><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><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>

Animated

Text alternative

SilentLooping

Higher quality
Smaller size

InlineMotion prefs

Autoplay

Playback control

Animated

Text alternative

SilentLooping

Higher quality
Smaller size

InlineMotion prefs

Autoplay

Playback control

<picture><sourcemedia="(prefers-reduced-motion: no-preference)"
srcset="clip.avif" /><img src="static.avif"…
…
width="..."
height="..."
alt="This meme may or may not be animated." /></picture><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>

Web components!

<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>

<video controls loop
autoplaymuted 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><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>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);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) {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) {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) {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) {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) {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) {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);toggle(state) {if (state) {this.video.play();} else {this.video.pause();}}}customElements.define("gif-like", GifLike);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AnimatedText alternative

SilentLooping

Higher quality
Smaller size

InlineMotion prefs

Autoplay

Playback control

Animated

Text alternative

SilentLooping

Higher quality
Smaller size

InlineMotion prefs

Autoplay

Playback control

Animated

Text alternative

SilentLooping

Higher quality
Smaller size

InlineMotion prefs

Autoplay

Playback control

web.dev/articles/lazy-loading-video

…
…
<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="..."Here is a cool example ofa silent GIF

I like this loop of a kitty
with a soccer ball evenbetter, though

GIF is a shorthandfor silent, inline,
animated clips

Alternative text ischronically overlooked
low-hanging fruit

974 KB

Newer formats workin all modern browsers,
load faster and look better
226 KB

GIF

WebP

81 KB

66 KB

59 KB

MP4

AVIF

WebM

(H.264)(VP9)

Video gives users
more control

<gif-like>Web components are agreat choice for progressive
enhancement</gif-like>tylersticka.com@$tylersticka$@$social.lol/in/$tylersticka

!
m
a
e
t
Hire mycloudfour.com