Making assets fly
and images a breeze with Image CDNs
Alistair Shepherd - Front-End Developer
SeriesEight
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9141155/image-with-wings.png)
Images on the web
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8970200/1-images-on-the-web.png)
<img
src="/our-image.jpg"
alt="our image"
>
Images change size depending on screen width
HTML Responsive Images
<img
src="/wp-content/uploads/our-image.jpg"
srcset="
/wp-content/uploads/our-image-300x200.jpg 300w,
/wp-content/uploads/our-image-768x512.jpg 768w,
/wp-content/uploads/our-image-1024x683.jpg 1024w,
/wp-content/uploads/our-image-1536x1024.jpg 1536w
"
sizes="(min-width: 800px) 768px, 100vw"
...
>
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8950659/otter-src.jpg)
Optimisation and Image Format
Size | |
---|---|
Source JPEG | 300kB |
Optimised JPEG | 60kB |
WebP 🙂 | 36kB |
AVIF * 😀 | 28kB |
JPEG XL * 😍 | 24kB |
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8950659/otter-src.jpg)
Size | Chrome | Firefox | Safari | |
---|---|---|---|---|
Source JPEG | 300kB | yes | yes | yes |
Optimised JPEG | 60kB | yes | yes | yes |
WebP 🙂 | 36kB | yes | yes | recent |
AVIF * 😀 | 28kB | yes | recent | no |
JPEG XL * 😍 | 24kB | no | no | no |
Size | |
---|---|
Source JPEG | 300kB |
Optimised JPEG | 60kB |
WebP 🙂 | 36kB |
AVIF * 😀 | 28kB |
JPEG XL * 😍 | 24kB |
Optimisation and Image Format
Hi DPI displays
<picture>
<source
media="(-webkit-min-device-pixel-ratio: 1.5)"
srcset="https://example.com/images/our-image_width1200_quality40.jpg 1200w"
sizes="...">
<img
srcset="https://example.com/images/our-image_width600_quality80.jpg 600w"
sizes="..."
alt="">
</picture>
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9263371/pasted-from-clipboard.png)
iPhone 12 Pro (right) screen:
- Screen width: 1170 pixels
- 'Viewport' width: 390 pixels
- Device Pixel Ratio of 3
The browser could decide to load an image with resolution 3x larger than it's displayed - 9x increase in file size!!!
We can mitigate increase in file size by increasing the image compression without visible effect.
Lazy Loading and Decoding
- "loading" attribute set to "lazy" or "eager" depending on position on page
- decoding="async" when images shouldn't delay text rendering
<img
loading="lazy"
decoding="async"
>
<picture>
<source
type="image/avif"
media="(-webkit-min-device-pixel-ratio: 1.5)"
srcset="/assets/image/otter-300-q40.avif 300w, /assets/image/otter-450-q40.avif 450w, /assets/image/otter-600-q40.avif 600w,
/assets/image/otter-800-q40.avif 800w, /assets/image/otter-1000-q40.avif 1000w, /assets/image/otter-1200-q40.avif 1200"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)">
<source
type="image/avif"
srcset="/assets/image/otter-300-q80.avif 300w, /assets/image/otter-450-q80.avif 450w, /assets/image/otter-600-q80.avif 600w,
/assets/image/otter-800-q80.avif 800w, /assets/image/otter-1000-q80.avif 1000w, /assets/image/otter-1200-q80.avif 1200"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)">
<source
type="image/webp"
media="(-webkit-min-device-pixel-ratio: 1.5)"
srcset="/assets/image/otter-300-q40.webp 300w, /assets/image/otter-450-q40.webp 450w, /assets/image/otter-600-q40.webp 600w,
/assets/image/otter-800-q40.webp 800w, /assets/image/otter-1000-q40.webp 1000w, /assets/image/otter-1200-q40.webp 1200"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)">
<source
type="image/webp"
srcset="/assets/image/otter-300-q80.webp 300w, /assets/image/otter-450-q80.webp 450w, /assets/image/otter-600-q80.webp 600w,
/assets/image/otter-800-q80.webp 800w, /assets/image/otter-1000-q80.webp 1000w, /assets/image/otter-1200-q80.webp 1200"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)">
<source
media="(-webkit-min-device-pixel-ratio: 1.5)"
srcset="/assets/image/otter-300-q40.jpg 300w, /assets/image/otter-450-q40.jpg 450w, /assets/image/otter-600-q40.jpg 600w,
/assets/image/otter-800-q40.jpg 800w, /assets/image/otter-1000-q40.jpg 1000w, /assets/image/otter-1200-q40.jpg 1200"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)">
<img
alt="otter standing on a log looking out majestically"
src="/assets/image/otter-1200-q80.jpg"
srcset="/assets/image/otter-300-q80.jpg 300w, /assets/image/otter-450-q80.jpg 450w,
/assets/image/otter-600-q80.jpg 600w, /assets/image/otter-800-q80.jpg 800w,
/assets/image/otter-1000-q80.jpg 1000w, /assets/image/otter-1200-q80.jpg 1200"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)"
width="1200"
height="784"
loading="lazy"
decoding="async"
>
</picture>
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8950845/image-list.png)
That's a lot going on!
Image processing wishlist
- Convert to several different formats, update to support new ones like AVIF and JPEG XL for browsers that support them
- Resize images to many different sizes for responsive devices
- Handle differing quality levels depending on density
- Bonus: Perform quality analysis comparing image sizes between different formats
- Optimise images and compress where possible
To display a few images on a webpage!
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8952912/images-demo.png)
Common Image systems
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9285488/image-implementation.png)
Manual
Custom code and process
Package or component in CMS, template or build tooling
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9289026/pasted-from-clipboard.png)
- Jpeg, Webp, AVIF
- Many image sizes
- Quality levels for 1x and 2x dpi
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8950845/image-list.png)
Provided or custom tooling
- May not prioritise compression, resizing or may not convert formats
- Can be expensive and time-consuming for developers to maintain
- May use large amounts of resources manipulating images on upload/build, storing every image
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270360/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9289041/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9263266/umbraco-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9263259/craft-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9289043/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9289046/pasted-from-clipboard.png)
HTTP Archive data
Source and image credit:
HTTP Archive Web Almanac, 2021 - Media Chapter
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9395097/pasted-from-clipboard.png)
Image issues in the wild
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8970214/3-the-issue-in-action.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8946394/1896gallery-1.png)
- Fresh redesign and rebuild
- Client-managed content
- Jamstack build tool with headless WordPress CMS
- Very visual featuring lots of images
- Was in a reasonably rural area so we wanted to ensure performance was very good
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8946406/1896galley-2.png)
- Around 20 artists
- Up to 40-50 pieces each
- Hundreds of studio-quality images
"The art on the new website has to be as high quality as possible"
I built my own image process
- Converted to Webp, was easy to add AVIF in future
- Resize images to many different sizes
- Handle differing quality levels depending on density
- Bonus: Perform quality analysis comparing image sizes between different formats
✅
✅
❎
❎
Results
- Created additional costs of servers, services and storage
- Created technical debt
- Increased time for site updates to go live from ~30 seconds to almost 10 minutes
- At first it was okay, improved performance slightly
Image
CDNs to
the rescue
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8970225/4-image-cdns-for-the-rescue.png)
What is an Image CDN?
- Content Delivery Network (CDN) specialising in image processing
- Sits between the web server of your site and the user, 'transforming' images on the fly by changing image URLs
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8958012/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8958013/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8958014/pasted-from-clipboard.png)
- Third-party services selling solutions to make dev experience of working with images easier and improving performance
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8958015/pasted-from-clipboard.png)
and many more
Web Server
HTML
CSS
JS
Images
Images
User's browser
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270033/Laptop.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270037/Servers.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270076/arrow-1.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270360/wp-logo.png)
Optimised
Source
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270056/Cloud.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270079/arrow-2.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270081/arrow-3.png)
Web Server
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270037/Servers.png)
User's browser
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270033/Laptop.png)
HTML
CSS
JS
Images
Images
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270076/arrow-1.png)
Web Server
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270037/Servers.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270360/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270378/aws-logo.png)
Optimised
Source
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270056/Cloud.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270079/arrow-2.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270081/arrow-3.png)
Web Server
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270037/Servers.png)
User's browser
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270033/Laptop.png)
HTML
CSS
JS
Images
Images
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270076/arrow-1.png)
Media Storage
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270037/Servers.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270360/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9270378/aws-logo.png)
How do we use them?
https://example.imagecdn.com
Start with our image
CDN domain
https://example.imagecdn.com
/https://oursite.com
/src/assets
/example.jpg
Add the URL to our
source image
https://example.imagecdn.com
/example.jpg
Many CDN providers allow
you to make presets/aliases
https://example.imagecdn.com
/example.jpg
?w=600&h=400
Add parameters to
'transform' the image
https://example.imagecdn.com/fetch
/example.jpg
/w_600,h_400
Different CDNs have different formats,
but work on the same concepts
- Handled separately from your main site or build
- Managed by image experts
- Automatic format detection
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8954478/format-negotiation.png)
.../w_300,h_600,c_thumb,g_auto/...
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8954518/otter-banner.jpg)
Tall banner with automatic gravity
.../w_1000,h_400,c_fill,g_north,
e_colorize:30,co_rgb:dd14d1/...
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9244043/otter-banner-wide-pink.jpg)
Wide banner anchored to top, coloured pink
Implementing
Image CDNs
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8970244/5-implementing-image-cdns.png)
<picture>
<source
media="(-webkit-min-device-pixel-ratio: 1.5)"
srcset="
https://example.imagecdn.com/images/w_300,q_40/otter.jpg 300w,
https://example.imagecdn.com/images/w_450,q_40/otter.jpg 450w,
https://example.imagecdn.com/images/w_600,q_40/otter.jpg 600w,
https://example.imagecdn.com/images/w_800,q_40/otter.jpg 800w,
https://example.imagecdn.com/images/w_1000,q_40/otter.jpg 1000w,
https://example.imagecdn.com/images/w_1200,q_40/otter.jpg 1200w"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)">
<img
alt="otter standing on a log looking out majestically"
src="https://example.imagecdn.com/images/w_800,q_80/otter.jpg"
srcset="
https://example.imagecdn.com/images/w_300,q_80/otter.jpg 300w,
https://example.imagecdn.com/images/w_450,q_80/otter.jpg 450w,
https://example.imagecdn.com/images/w_600,q_80/otter.jpg 600w,
https://example.imagecdn.com/images/w_800,q_80/otter.jpg 800w,
https://example.imagecdn.com/images/w_1000,q_80/otter.jpg 1000w,
https://example.imagecdn.com/images/w_1200,q_80/otter.jpg 1200w"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)"
width="1200"
height="784"
loading="lazy"
decoding="async"
>
</picture>
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9263046/cloudinary-plugin.jpg)
Cloudinary WordPress plugin
CMS' plugins or platform integrations
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9289149/pasted-from-clipboard.png)
Imagekit.io integrations
<img src="{{ image | img_url: '800x300', crop: 'center' }}" ...>
Shopify Image CDN
{% image {
name: 'otter.jpg'
alt: 'an otter standing on a log looking majestic'
srcset: [300, 450, 600, 800, 1000, 1200]
sizes: '100vw'
width: 1200
height: 800
loading: 'lazy'
class: 'image-class mb-xl'
} %}
accud.io/imagecdn-11ty
Templating language shortcodes
React Component
import Image from 'components/Image'
export default function () {
return (
<Image
src="otter.jpg"
alt="an otter standing on a log looking majestic"
srcset={[300, 450, 600, 800, 1000, 1200]}
sizes="100vw"
width="1200"
height="800"
loading="lazy"
className="image-class mb-xl"
/>
)
}
accud.io/imagecdn-react
PHP Function
<?php
echo image([
'image' => 'otter.jpg',
'alt' => 'an otter standing on a log looking majestic',
'srcset' => [300, 450, 600, 800, 1000, 1200],
'sizes' => '100vw',
'width' => 1200,
'height' => 800,
'loading' => 'lazy',
'class' => 'image-class'
]);
accud.io/imagecdn-php
Image CDNs make images
easier—everywhere!
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8956840/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8956856/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8956857/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8956878/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8958312/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9263259/craft-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9263266/umbraco-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9263268/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9395032/pasted-from-clipboard.png)
Moving to using an Image CDN
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8970251/6-moving-to-using-an-image-cdn.png)
What we did
- Used the CloudImage Image CDN
- Set up a custom code solution using a Liquid shortcode like in previous example
- Removed custom toolchain
Lighthouse performance score improved by ~19 points
Increased speed of development
Reduced site build time by 80%
Chrome Image bytes
decreased by 27%
No free lunch!
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8970262/7-limitations-and-workarounds.png)
Cost
- As an external service, there is a cost implication
- Many have a generous free tier, small site might not cost anything
- Larger sites will cost, pricing model depends on the provider
- Unless you have hundreds of thousands of images, likely cheaper than developers maintaining image processing or faffing with plugins
- Can be offset by reduced server load, storage requirements or build infrastructure
- Disable CDN for local development
- Offload images to cloud storage
- Push images to your site/secret dev URL when needed
Local dev
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9244018/s3-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9262513/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/9262514/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8956975/pasted-from-clipboard.png)
For very high performing sites different origin has an impact
Solutions for nginx and Netlify
location /cdn/ {
proxy_pass https://example.imagecdn.com/;
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8956942/noproxy.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8956946/proxy.png)
[[redirects]]
from = '/cdn/:image'
to = 'https://example.imagecdn.com/:image'
status = 200
The scores are in...
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1973703/images/8970272/8-the-scores-are-in.png)
Improve the performance of images on your websites
Make working with images easier during development
Reduce resources required for server/build
Try an Image CDN in your next project!
Thank You!
These slides, my sources and helpful links on image CDNs available at accud.io/imagecdns
On twitter and most social media @accudio
My website and blog posts alistairshepherd.uk
Making assets fly and images a breeze with Image CDNs
By Alistair Shepherd
Making assets fly and images a breeze with Image CDNs
- 334