Animated WebP combines WebP's superior compression with animation support, offering files 30–80 % smaller than equivalent GIF with better quality.
Animated WebP vs GIF
| Feature | GIF | Animated WebP |
|---|---|---|
| Max colors | 256 | Millions |
| Transparency | 1-bit | Full alpha |
| Compression | Poor | Very good |
| Browser support | 100 % | 96 % |
| Size vs GIF | 100 % | 30–60 % |
GIF → animated WebP with FFmpeg
ffmpeg -i animation.gif animation.webp
ffmpeg -i animation.gif -quality 85 animation.webp
ffmpeg -i animation.gif -vf "scale=400:-1" -quality 80 animation_400.webp
ffmpeg -i animation.gif -loop 0 animation.webp # infinite loop
Video → animated WebP
# MP4 → animated WebP (first 5 seconds at 15 fps)
ffmpeg -i video.mp4 -t 5 -vf "fps=15,scale=480:-1" -quality 80 clip.webp
# Extract segment from second 10 to 15
ffmpeg -ss 10 -i video.mp4 -t 5 -vf "fps=12,scale=400:-1" -quality 80 segment.webp
Python with Pillow
from PIL import Image
def gif_to_webp(gif_path, webp_path, quality=80):
with Image.open(gif_path) as gif:
frames, durations = [], []
try:
while True:
frames.append(gif.copy().convert('RGBA'))
durations.append(gif.info.get('duration', 100))
gif.seek(gif.tell() + 1)
except EOFError:
pass
frames[0].save(
webp_path, format='WEBP', save_all=True,
append_images=frames[1:], duration=durations,
loop=0, quality=quality, method=6,
)
print(f"WebP: {webp_path} ({len(frames)} frames)")
gif_to_webp('animation.gif', 'animation.webp', quality=82)
Create from individual frames
from PIL import Image
from pathlib import Path
def frames_to_webp(directory, output, fps=12, quality=80):
exts = {'.png', '.jpg', '.jpeg'}
paths = sorted(f for f in Path(directory).iterdir() if f.suffix.lower() in exts)
frames = [Image.open(p).convert('RGBA') for p in paths]
ms = int(1000 / fps)
frames[0].save(
output, format='WEBP', save_all=True,
append_images=frames[1:], duration=ms, loop=0, quality=quality,
)
print(f"WebP: {output} ({len(frames)} frames, {fps} fps)")
frames_to_webp('frames/', 'animation.webp', fps=15)
Extract frames from animated WebP
from PIL import Image
with Image.open('animation.webp') as webp:
n = getattr(webp, 'n_frames', 1)
print(f"Frames: {n}")
for i in range(n):
webp.seek(i)
webp.copy().save(f'frames/frame_{i:04d}.png')
Compare sizes
import os, subprocess
from pathlib import Path
def compare(gif_path):
gif = Path(gif_path)
webp = gif.with_suffix('.webp')
subprocess.run(['ffmpeg', '-i', str(gif), '-quality', '80', str(webp), '-y'],
capture_output=True)
gif_size = os.path.getsize(gif)
webp_size = os.path.getsize(webp)
print(f"GIF: {gif_size//1024} KB")
print(f"WebP: {webp_size//1024} KB ({(1-webp_size/gif_size)*100:.0f}% smaller)")
compare('logo_animated.gif')
HTML with GIF fallback
<picture>
<source srcset="animation.webp" type="image/webp">
<img src="animation.gif" alt="Animation" width="400" height="300">
</picture>
When to use animated WebP
- Yes: web animations, avatars, stickers, product demos
- No: content shared via email (limited WebP support in mail clients)