KEMBAR78
HyperLight Websites | PDF
Chris Zacharias
Hyper-LightWebsites
CEO, FOUNDER @
chris@imgix.com
@zacman85
FITC 2019
1. https://en.wikipedia.org/wiki/.kkrieger
“If they can build an entire Quake clone1 in LESS than 100KB,
WHY can’t YouTube load in under 1MB?”
Mike Solomon
Early engineer at YouTube.
YouTube Feather
A hyper-light version of YouTube,
back in 2010.
Constraints
• Start the video as fast as possible.
• Maintain >80% of the user experience.
• Use the latest technologies (circa 2010).
• Make as few requests as possible.
Results
• 98KB for “everything but the video”.
• 14 total requests from 3 domains.
• First production HTML5 video player.
• Opened up new regions around the world.
YouTube Feather
A hyper-light version of YouTube,
back in 2010.
High latency environments are EVERYWHERE.
A hyper-light website is a RECONCEPTUALIZATION 

of a website, designed to push the limits of performance.
Buildingahyper-lightwebsite.
The goal is to provide a roughly consistent
version of a website that can be delivered
AS FAST AS POSSIBLE.
Steps to build a hyper-light website:
1. Identify the most active page on your website.
2. Build a new version of that page from scratch with a singular focus on optimization.
3. Assemble all of the HTML, CSS and Javascript into a single “hyper-light” HTML page.
4. Serve the hyper-light page behind a CDN with compression and HTTP/2 pipelining.
5. Measure both the original page, uncached, and the hyper-light page.
6. Analyze the results.
Things you want to KEEP doing:
• Serve real content, preferably dynamically.
• Responsive layouts (at minimum, common breakpoints).
• Responsive images.
• Sprites, in some cases.
• SVG graphics (optimized responsibly).
• Accessibility.
Things you want to STOP doing:
(for the purposes of this exercise)
• Ads and social media trackers.
• CMS integrations.
• Javascript libraries.
• CSS layouts via frameworks.
• Lazy loading content (except images).
• Data URIs, unless absolutely certain.
• Javascript and CSS compilation.
• Custom web fonts.
Arealworldexample.
Walking through an example
Original
Requests 181
Domains 50
Bandwidth
Total Content Size 8.7 MBs
Total Transfer Size 5.9 MBs
Non-Image
Content
2.0 MBs
Image Content 3.9 MBs
Billed By CDN 4.9 MBs
Response Times
(First Paint —>
Fully Loaded)
2G 22 sec —-—> 2 min 47 sec
3G 6.4 sec ——> 53 sec
Hi-Speed 1.5 sec ——-> 16.7 sec
Hyper-Light
Walking through an example
Original
Original Hyper-Light
Requests 181
🥁
Domains 50
Bandwidth
Total Content Size 8.7 MBs
Total Transfer Size 5.9 MBs
Non-Images
Content
2.0 MBs
Image Content 3.9 MBs
Billed By CDN 4.9 MBs
Response Times
(First Paint —>
Fully Loaded)
2G 22 sec —-—> 2 min 47 sec
3G 6.4 sec ——> 53 sec
Hi-Speed 1.5 sec ——-> 16.7 sec
Original Hyper-Light
Requests 181 32 -82.3%
Domains 50 2 -96.0%
Bandwidth
Total Content Size 8.7 MBs
Total Transfer Size 5.9 MBs
Non-Images
Content
2.0 MBs
Image Content 3.9 MBs
Billed By CDN 4.9 MBs
Response Times
(First Paint —>
Fully Loaded)
2G 22 sec —-—> 2 min 47 sec
3G 6.4 sec ——> 53 sec
Hi-Speed 1.5 sec ——-> 16.7 sec
Original Hyper-Light
Requests 181 32 -82.3%
Domains 50 2 -96.0%
Bandwidth
Total Content Size 8.7 MBs 1.1 MBs -87.4%
Total Transfer Size 5.9 MBs 1.0 MBs -83.1%
Non-Images
Content
2.0 MBs 19 KBs -99.1%
Image Content 3.9 MBs 999.4 KBs -74.4%
Billed By CDN 4.9 MBs 1.0 MBs -79.6%
Response Times
(First Paint —>
Fully Loaded)
2G 22 sec —-—> 2 min 47 sec
3G 6.4 sec ——> 53 sec
Hi-Speed 1.5 sec ——-> 16.7 sec
Original Hyper-Light
Requests 181 32 -82.3%
Domains 50 2 -96.0%
Bandwidth
Total Content Size 8.7 MBs 1.1 MBs -87.4%
Total Transfer Size 5.9 MBs 1.0 MBs -83.1%
Non-Images
Content
2.0 MBs 19 KBs -99.1%
Image Content 3.9 MBs 999.4 KBs -74.4%
Billed By CDN 4.9 MBs 1.0 MBs -79.6%
Response Times
(First Paint —>
Fully Loaded)
2G 22 sec —-—> 2 min 47 sec 773 msec ——-> 11 sec 15.7x
3G 6.4 sec ——> 53 sec 556 msec ——> 3.7 sec 14.3x
Hi-Speed 1.5 sec ——-> 16.7 sec 435 msec ——> 621 msec 26.9x
Techniquestouse.
<!DOCTYPE html>
<html>
<head>
<title>Car and Driver</title>
<style type=“text/css”>
{% include “styles.css” %}
</style>
<script type=“application/javascript”>
{% include “main.js” %}
</script>
</head>
<body>
{% include “sprite.svg” %}
<div>Hello World</div>
</body>
</html>
Inline as much as possible server-side.
Use HTML5 semantic elements.
<!DOCTYPE html>
<html>
<head>
<title>Car and Driver</title>
</head>
<body>
<header>
<nav></nav>
</header>
<section>
<aside>
</aside>
<article>
Hello World!
</article>
</section>
<footer>
</footer>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Car and Driver</title>
</head>
<body>
<header>
<nav></nav>
</header>
<section>
<aside>
</aside>
<article>
Hello World!
</article>
</section>
<footer>
</footer>
</body>
</html>
95%
of users
Use HTML5 semantic elements.
Relative layout using root em sizing.
<style type=“text/css”>
html {
font-size: 16px; /* root ems */
}
h1 {
font-size: 2rem; /* 32 px */
}
img.splash {
width: 50rem; /* 800 px */
height: 40rem; /* 600 px */
}
</style>
<style type=“text/css”>
html {
font-size: 16px; /* root ems */
}
h1 {
font-size: 2rem; /* 32 px */
}
img.splash {
width: 50rem; /* 800 px */
height: 40rem; /* 600 px */
}
</style>
Relative layout using root em sizing.
96%
of users
CSS grids.
1. https://material.io/design/layout/responsive-layout-grid.html
<html>
<head>
<style type=“text/css”>
#body {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 20px;
align-items: top;
}
header {
grid-column: 1 / span 3;
}
article {
grid-column: 1 / span 2;
}
</style>
</head>
<body>
<header>Header</header>
<article>Content</article>
<aside>Side</aside>
</body>
</html>
Header
Side
Content
87%
of users
CSS grids.
1. https://material.io/design/layout/responsive-layout-grid.html
<html>
<head>
<style type=“text/css”>
#body {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 20px;
align-items: top;
}
header {
grid-column: 1 / span 3;
}
article {
grid-column: 1 / span 2;
}
</style>
</head>
<body>
<header>Header</header>
<article>Content</article>
<aside>Side</aside>
</body>
</html>
Header
Side
Content
<html>
<head>
<template id=“article-template”>
<article>
<h1>Title</h1>
<p>Description</p>
</article>
</template>
<script type=“text/javascript”>
function addArticle(title, description) {
var template = document.getElementById(“article-template”);
var clone = document.importNode(template.content, true);
/* Make changes to the clone’s DOM. */
document.getElementById(“articles”).appendChild(clone);
}
</script>
</head>
<body>
<section id=“articles”></section>
</body>
</html>
HTML templates.
89%
of users
<html>
<head>
<template id=“article-template”>
<article>
<h1>Title</h1>
<p>Description</p>
</article>
</template>
<script type=“text/javascript”>
function addArticle(title, description) {
var template = document.getElementById(“article-template”);
var clone = document.importNode(template.content, true);
/* Make changes to the clone’s DOM. */
document.getElementById(“articles”).appendChild(clone);
}
</script>
</head>
<body>
<section id=“articles”></section>
</body>
</html>
HTML templates.
<style type=“text/css”>
#search-form { display: none; }
#search-form:target { display: block; }
</style>
…
<a href=“#search-form”>Show Search</a>
<form id=“search-form”>
<input type=“text” name=“search-query”>
<input type=“submit” value=“Search”>
<a href=“#”>Cancel</a>
</form>
State transitions using the CSS :target selector
Fragment identifier changes WILL get added to the browser history.
⚠
State transitions using the CSS :target selector
<style type=“text/css”>
#search-form { display: none; }
#search-form:target { display: block; }
</style>
…
<a href=“#search-form”>Show Search</a>
<form id=“search-form”>
<input type=“text” name=“search-query”>
<input type=“submit” value=“Search”>
<a href=“#”>Cancel</a>
</form>
96%
of users
Fragment identifier changes WILL get added to the browser history.
⚠
State transitions using the CSS :target selector
<style type=“text/css”>
#search-form { display: none; }
#search-form:target { display: block; }
</style>
…
<a href=“#search-form”>Show Search</a>
<form id=“search-form”>
<input type=“text” name=“search-query”>
<input type=“submit” value=“Search”>
<a href=“#”>Cancel</a>
</form>
Replacing SVG with HTML and CSS.
Original
2 x SVG arrow graphics = ~5KBs
Hyper-light
Angle quotes + border-radius = 1KB
<style type=“text/css”>
.arrow {
background-color: #4D80AB;
border-radius: 50%;
color: white;
font-weight: bold;
height: 1rem;
width: 1rem;
}
…
</style>
…
<a href=“#prev” class=“arrow”>&lasquo;</a>
<span class=“pages”><em>1</em> of <em>5</em></span>
<a href=“#next” class=“arrow”>&rasquo;</a>
93%
of users
Replacing SVG with HTML and CSS.
Original
2 x SVG arrow graphics = ~5KBs
Hyper-light
Angle quotes + border-radius = 1KB
<style type=“text/css”>
.arrow {
background-color: #4D80AB;
border-radius: 50%;
color: white;
font-weight: bold;
height: 1rem;
width: 1rem;
}
…
</style>
…
<a href=“#prev” class=“arrow”>&lasquo;</a>
<span class=“pages”><em>1</em> of <em>5</em></span>
<a href=“#next” class=“arrow”>&rasquo;</a>
Using an SVG sprite with fragment identifiers.
<svg xmlns="http://www.w3.org/2000/svg" style="display: none">
<symbol id="icon-comments" viewBox="380 0 20 20">
<title>Comments</title>
<path d=“M394.57,4.19H385a1.2,…” style=“fill: #1383b3;” />
</symbol>
…
</svg>
…
<svg><use xlink:href=“#icon-comments” /></svg>
1. https://css-tricks.com/svg-symbol-good-choice-icons/
Using an SVG sprite with fragment identifiers.
91%
of users
1. https://css-tricks.com/svg-symbol-good-choice-icons/
<svg xmlns="http://www.w3.org/2000/svg" style="display: none">
<symbol id="icon-comments" viewBox="380 0 20 20">
<title>Comments</title>
<path d=“M394.57,4.19H385a1.2,…” style=“fill: #1383b3;” />
</symbol>
…
</svg>
…
<svg><use xlink:href=“#icon-comments” /></svg>
Image-based sprites.
<style type=“text/css”>
.car-image {
background-image: url(/car-sprite.jpg);
background-size: 100%;
height: 60px;
width: 140px;
}
#sports-car.car-image {
background-position: -280px -120px;
}
</style>
<div id=“sports-car” class=“car-image”></div>
In most cases, HTTP/2 pipelining is the better approach.
⚠
Image-based sprites.
<style type=“text/css”>
.car-image {
background-image: url(/car-sprite.jpg);
background-size: 100%;
height: 60px;
width: 140px;
}
#sports-car.car-image {
background-position: -280px -120px;
}
</style>
<div id=“sports-car” class=“car-image”></div>
Image-based sprites.
96%
of users
In most cases, HTTP/2 pipelining is the better approach.
⚠
<style type=“text/css”>
.car-image {
background-image: url(/car-sprite.jpg);
background-size: 100%;
height: 60px;
width: 140px;
}
#sports-car.car-image {
background-position: -280px -120px;
}
</style>
<div id=“sports-car” class=“car-image”></div>
<img src=“https://cnd.imgix.net/honda.jpg?w=200&fm=pjpg&auto=format,compress”
srcset=“https://cnd.imgix.net/honda.jpg?w=200&fm=pjpg&auto=format,compress 1x,
https://cnd.imgix.net/honda.jpg?w=200&fm=pjpg&auto=format,compress&dpr=2 2x”
width=“200” alt=“Honda”>
Responsive images using srcset.
<img src=“https://cnd.imgix.net/honda.jpg?w=200&fm=pjpg&auto=format,compress”
srcset=“https://cnd.imgix.net/honda.jpg?w=200&fm=pjpg&auto=format,compress 1x,
https://cnd.imgix.net/honda.jpg?w=200&fm=pjpg&auto=format,compress&dpr=2 2x”
width=“200” alt=“Honda”>
Responsive images using srcset.Responsive images using srcset.
<img src=“https://cnd.imgix.net/honda.jpg?w=200&fm=pjpg&auto=format,compress”
srcset=“https://cnd.imgix.net/honda.jpg?w=200&fm=pjpg&auto=format,compress 1x,
https://cnd.imgix.net/honda.jpg?w=200&fm=pjpg&auto=format,compress&dpr=2 2x”
width=“200” alt=“Honda”>
Responsive images using srcset.Responsive images using srcset.
<img src=“https://cnd.imgix.net/honda.jpg?w=200&fm=pjpg&auto=format,compress”
srcset=“https://cnd.imgix.net/honda.jpg?w=200&fm=pjpg&auto=format,compress 1x,
https://cnd.imgix.net/honda.jpg?w=200&fm=pjpg&auto=format,compress&dpr=2 2x”
width=“200” alt=“Honda”>
Responsive images using srcset.Responsive images using srcset.
<img src=“https://cnd.imgix.net/honda.jpg?w=200&fm=pjpg&auto=format,compress”
srcset=“https://cnd.imgix.net/honda.jpg?w=200&fm=pjpg&auto=format,compress 1x,
https://cnd.imgix.net/honda.jpg?w=200&fm=pjpg&auto=format,compress&dpr=2 2x”
width=“200” alt=“Honda”>
Responsive images using srcset.
88%
of users
GZIP compression.
# A simple nginx.conf with gzip turned on.
server {
listen 8080;
server_name localhost;
gzip on;
gzip_comp_level 9;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
GZIP compression.
Make sure to vary on Accept-Encoding in your caching layers.
⚠
# A simple nginx.conf with gzip turned on.
server {
listen 8080;
server_name localhost;
gzip on;
gzip_comp_level 9;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
99%
of users
GZIP compression.
Make sure to vary on Accept-Encoding in your caching layers.
⚠
# A simple nginx.conf with gzip turned on.
server {
listen 8080;
server_name localhost;
gzip on;
gzip_comp_level 9;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
Experiment with Brotli.
tom@servo:/www$ cd /data/repos && git clone https://github.com/google/brotli.git
tom@servo:/www$ sudo ln -s /data/repos/brotli/python/bro.py /usr/local/bin/bro.py
tom@servo:/www$ bro.py -i index.html -o index.html.br
tom@servo:/www$ cd /data/repos && git clone https://github.com/google/brotli.git
tom@servo:/www$ sudo ln -s /data/repos/brotli/python/bro.py /usr/local/bin/bro.py
tom@servo:/www$ bro.py -i index.html -o index.html.br
Experiment with Brotli.
Experiment with Brotli.
84%
of users
tom@servo:/www$ cd /data/repos && git clone https://github.com/google/brotli.git
tom@servo:/www$ sudo ln -s /data/repos/brotli/python/bro.py /usr/local/bin/bro.py
tom@servo:/www$ bro.py -i index.html -o index.html.br
HTTP/2 pipelining.
HTTP/2 pipelining.
84%
of users
Measuringandacting onthedata.
Measuring the results using Sitespeed.
tom@servo:/data$ docker run -v “$(pwd)”:/sitespeed.io 
> sitespeedio/sitespeed.io:7.3.6 https://www.caranddriver.com
Zeroing in on the biggest wins.
15 images
requests
~600KBs
1 image request
~50KBs
An SVG sprite sheet would be even better at ~15KBs.
Zeroing in on the biggest wins.
15 images
requests
~600KBs
1 image request
~50KBs
Improvementsweremade!
Original Improved Hyper-Light
Requests 181
🥁
32
Domains 50 2
Bandwidth
Total Content Size 8.7 MBs 1.1 MBs
Total Transfer Size 5.9 MBs 1.0 MBs
Non-Images 2.0 MBs 19 KBs
Image Content 3.9 MBs 999.4 KBs
Billed By CDN 4.9 MBs 1.0 MBs
Response Times
(First Paint —>
Fully Loaded)
2G 22 sec —> 2 min 47 sec 773 msec —-> 11 sec
3G 6.4 sec —> 53 sec 556 msec —> 3.7 sec
Hi-Speed 1.5 sec —-> 16.7 sec 435 msec —> 621 msec
Original Improved Hyper-Light
Requests 181 234 32
Domains 50 74 2
Bandwidth
Total Content Size 8.7 MBs 1.1 MBs
Total Transfer Size 5.9 MBs 1.0 MBs
Non-Images 2.0 MBs 19 KBs
Image Content 3.9 MBs 999.4 KBs
Billed By CDN 4.9 MBs 1.0 MBs
Response Times
(First Paint —>
Fully Loaded)
2G 22 sec —> 2 min 47 sec 773 msec —-> 11 sec
3G 6.4 sec —> 53 sec 556 msec —> 3.7 sec
Hi-Speed 1.5 sec —-> 16.7 sec 435 msec —> 621 msec
Original Improved Hyper-Light
Requests 181 234 32
Domains 50 74 2
Bandwidth
Total Content Size 8.7 MBs 6.2 MBs 1.1 MBs
Total Transfer Size 5.9 MBs 2.8 MBs 1.0 MBs
Non-Images 2.0 MBs 1.9 MBs 19 KBs
Image Content 3.9 MBs 993.5 KBs 999.4 KBs
Billed By CDN 4.9 MBs 1 MBs 1.0 MBs
Response Times
(First Paint —>
Fully Loaded)
2G 22 sec —> 2 min 47 sec 773 msec —-> 11 sec
3G 6.4 sec —> 53 sec 556 msec —> 3.7 sec
Hi-Speed 1.5 sec —-> 16.7 sec 435 msec —> 621 msec
Original Improved Hyper-Light
Requests 181 234 32
Domains 50 74 2
Bandwidth
Total Content Size 8.7 MBs 6.2 MBs 1.1 MBs
Total Transfer Size 5.9 MBs 2.8 MBs 1.0 MBs
Non-Images 2.0 MBs 1.9 MBs 19 KBs
Image Content 3.9 MBs 993.5 KBs 999.4 KBs
Billed By CDN 4.9 MBs 1 MBs 1.0 MBs
Response Times
(First Paint —>
Fully Loaded)
2G 22 sec —> 2 min 47 sec 7.6 sec —-> 1 min 12 sec 773 msec —-> 11 sec
3G 6.4 sec —> 53 sec 2.7 sec —> 19.3 sec 556 msec —> 3.7 sec
Hi-Speed 1.5 sec —-> 16.7 sec 627 msec —> 9.7 sec 435 msec —> 621 msec
• Repeat the process with
other pages.
• Run a 1% test in the wild.
• Build a performance budget.
• Run Sitespeed (or similar)
regularly.
{
"browsertime.pageSummary": [{
"metric": "statistics.timings.firstPaint.median",
"max": 1500
}],
"pagexray.pageSummary": [{
"metric": "transferSize",
"max": 1000000
}, {
"metric": "requests",
"max": 45
}
}
1. https://www.sitespeed.io/documentation/sitespeed.io/performance-budget/
Taking the next steps…
Source code:
https://github.com/zacman85/hyperlight-websites
Live demo:
http://hlw.chriszacharias.com
Special thanks to Miguel Cardona.
Thankyou!
Chris Zacharias
CEO, FOUNDER @
chris@imgix.com
@zacman85

HyperLight Websites

  • 1.
    Chris Zacharias Hyper-LightWebsites CEO, FOUNDER@ chris@imgix.com @zacman85 FITC 2019
  • 2.
    1. https://en.wikipedia.org/wiki/.kkrieger “If theycan build an entire Quake clone1 in LESS than 100KB, WHY can’t YouTube load in under 1MB?” Mike Solomon Early engineer at YouTube.
  • 3.
    YouTube Feather A hyper-lightversion of YouTube, back in 2010. Constraints • Start the video as fast as possible. • Maintain >80% of the user experience. • Use the latest technologies (circa 2010). • Make as few requests as possible.
  • 4.
    Results • 98KB for“everything but the video”. • 14 total requests from 3 domains. • First production HTML5 video player. • Opened up new regions around the world. YouTube Feather A hyper-light version of YouTube, back in 2010.
  • 5.
  • 7.
    A hyper-light websiteis a RECONCEPTUALIZATION 
 of a website, designed to push the limits of performance.
  • 9.
  • 10.
    The goal isto provide a roughly consistent version of a website that can be delivered AS FAST AS POSSIBLE.
  • 11.
    Steps to builda hyper-light website: 1. Identify the most active page on your website. 2. Build a new version of that page from scratch with a singular focus on optimization. 3. Assemble all of the HTML, CSS and Javascript into a single “hyper-light” HTML page. 4. Serve the hyper-light page behind a CDN with compression and HTTP/2 pipelining. 5. Measure both the original page, uncached, and the hyper-light page. 6. Analyze the results.
  • 12.
    Things you wantto KEEP doing: • Serve real content, preferably dynamically. • Responsive layouts (at minimum, common breakpoints). • Responsive images. • Sprites, in some cases. • SVG graphics (optimized responsibly). • Accessibility.
  • 13.
    Things you wantto STOP doing: (for the purposes of this exercise) • Ads and social media trackers. • CMS integrations. • Javascript libraries. • CSS layouts via frameworks. • Lazy loading content (except images). • Data URIs, unless absolutely certain. • Javascript and CSS compilation. • Custom web fonts.
  • 14.
  • 15.
  • 16.
    Original Requests 181 Domains 50 Bandwidth TotalContent Size 8.7 MBs Total Transfer Size 5.9 MBs Non-Image Content 2.0 MBs Image Content 3.9 MBs Billed By CDN 4.9 MBs Response Times (First Paint —> Fully Loaded) 2G 22 sec —-—> 2 min 47 sec 3G 6.4 sec ——> 53 sec Hi-Speed 1.5 sec ——-> 16.7 sec
  • 17.
  • 18.
    Walking through anexample Original
  • 19.
    Original Hyper-Light Requests 181 🥁 Domains50 Bandwidth Total Content Size 8.7 MBs Total Transfer Size 5.9 MBs Non-Images Content 2.0 MBs Image Content 3.9 MBs Billed By CDN 4.9 MBs Response Times (First Paint —> Fully Loaded) 2G 22 sec —-—> 2 min 47 sec 3G 6.4 sec ——> 53 sec Hi-Speed 1.5 sec ——-> 16.7 sec
  • 20.
    Original Hyper-Light Requests 18132 -82.3% Domains 50 2 -96.0% Bandwidth Total Content Size 8.7 MBs Total Transfer Size 5.9 MBs Non-Images Content 2.0 MBs Image Content 3.9 MBs Billed By CDN 4.9 MBs Response Times (First Paint —> Fully Loaded) 2G 22 sec —-—> 2 min 47 sec 3G 6.4 sec ——> 53 sec Hi-Speed 1.5 sec ——-> 16.7 sec
  • 21.
    Original Hyper-Light Requests 18132 -82.3% Domains 50 2 -96.0% Bandwidth Total Content Size 8.7 MBs 1.1 MBs -87.4% Total Transfer Size 5.9 MBs 1.0 MBs -83.1% Non-Images Content 2.0 MBs 19 KBs -99.1% Image Content 3.9 MBs 999.4 KBs -74.4% Billed By CDN 4.9 MBs 1.0 MBs -79.6% Response Times (First Paint —> Fully Loaded) 2G 22 sec —-—> 2 min 47 sec 3G 6.4 sec ——> 53 sec Hi-Speed 1.5 sec ——-> 16.7 sec
  • 22.
    Original Hyper-Light Requests 18132 -82.3% Domains 50 2 -96.0% Bandwidth Total Content Size 8.7 MBs 1.1 MBs -87.4% Total Transfer Size 5.9 MBs 1.0 MBs -83.1% Non-Images Content 2.0 MBs 19 KBs -99.1% Image Content 3.9 MBs 999.4 KBs -74.4% Billed By CDN 4.9 MBs 1.0 MBs -79.6% Response Times (First Paint —> Fully Loaded) 2G 22 sec —-—> 2 min 47 sec 773 msec ——-> 11 sec 15.7x 3G 6.4 sec ——> 53 sec 556 msec ——> 3.7 sec 14.3x Hi-Speed 1.5 sec ——-> 16.7 sec 435 msec ——> 621 msec 26.9x
  • 23.
  • 24.
    <!DOCTYPE html> <html> <head> <title>Car andDriver</title> <style type=“text/css”> {% include “styles.css” %} </style> <script type=“application/javascript”> {% include “main.js” %} </script> </head> <body> {% include “sprite.svg” %} <div>Hello World</div> </body> </html> Inline as much as possible server-side.
  • 25.
    Use HTML5 semanticelements. <!DOCTYPE html> <html> <head> <title>Car and Driver</title> </head> <body> <header> <nav></nav> </header> <section> <aside> </aside> <article> Hello World! </article> </section> <footer> </footer> </body> </html>
  • 26.
    <!DOCTYPE html> <html> <head> <title>Car andDriver</title> </head> <body> <header> <nav></nav> </header> <section> <aside> </aside> <article> Hello World! </article> </section> <footer> </footer> </body> </html> 95% of users Use HTML5 semantic elements.
  • 27.
    Relative layout usingroot em sizing. <style type=“text/css”> html { font-size: 16px; /* root ems */ } h1 { font-size: 2rem; /* 32 px */ } img.splash { width: 50rem; /* 800 px */ height: 40rem; /* 600 px */ } </style>
  • 28.
    <style type=“text/css”> html { font-size:16px; /* root ems */ } h1 { font-size: 2rem; /* 32 px */ } img.splash { width: 50rem; /* 800 px */ height: 40rem; /* 600 px */ } </style> Relative layout using root em sizing. 96% of users
  • 29.
    CSS grids. 1. https://material.io/design/layout/responsive-layout-grid.html <html> <head> <styletype=“text/css”> #body { display: grid; grid-template-columns: repeat(3, 1fr); grid-gap: 20px; align-items: top; } header { grid-column: 1 / span 3; } article { grid-column: 1 / span 2; } </style> </head> <body> <header>Header</header> <article>Content</article> <aside>Side</aside> </body> </html> Header Side Content
  • 30.
    87% of users CSS grids. 1.https://material.io/design/layout/responsive-layout-grid.html <html> <head> <style type=“text/css”> #body { display: grid; grid-template-columns: repeat(3, 1fr); grid-gap: 20px; align-items: top; } header { grid-column: 1 / span 3; } article { grid-column: 1 / span 2; } </style> </head> <body> <header>Header</header> <article>Content</article> <aside>Side</aside> </body> </html> Header Side Content
  • 31.
    <html> <head> <template id=“article-template”> <article> <h1>Title</h1> <p>Description</p> </article> </template> <script type=“text/javascript”> functionaddArticle(title, description) { var template = document.getElementById(“article-template”); var clone = document.importNode(template.content, true); /* Make changes to the clone’s DOM. */ document.getElementById(“articles”).appendChild(clone); } </script> </head> <body> <section id=“articles”></section> </body> </html> HTML templates.
  • 32.
    89% of users <html> <head> <template id=“article-template”> <article> <h1>Title</h1> <p>Description</p> </article> </template> <scripttype=“text/javascript”> function addArticle(title, description) { var template = document.getElementById(“article-template”); var clone = document.importNode(template.content, true); /* Make changes to the clone’s DOM. */ document.getElementById(“articles”).appendChild(clone); } </script> </head> <body> <section id=“articles”></section> </body> </html> HTML templates.
  • 33.
    <style type=“text/css”> #search-form {display: none; } #search-form:target { display: block; } </style> … <a href=“#search-form”>Show Search</a> <form id=“search-form”> <input type=“text” name=“search-query”> <input type=“submit” value=“Search”> <a href=“#”>Cancel</a> </form> State transitions using the CSS :target selector
  • 34.
    Fragment identifier changesWILL get added to the browser history. ⚠ State transitions using the CSS :target selector <style type=“text/css”> #search-form { display: none; } #search-form:target { display: block; } </style> … <a href=“#search-form”>Show Search</a> <form id=“search-form”> <input type=“text” name=“search-query”> <input type=“submit” value=“Search”> <a href=“#”>Cancel</a> </form>
  • 35.
    96% of users Fragment identifierchanges WILL get added to the browser history. ⚠ State transitions using the CSS :target selector <style type=“text/css”> #search-form { display: none; } #search-form:target { display: block; } </style> … <a href=“#search-form”>Show Search</a> <form id=“search-form”> <input type=“text” name=“search-query”> <input type=“submit” value=“Search”> <a href=“#”>Cancel</a> </form>
  • 36.
    Replacing SVG withHTML and CSS. Original 2 x SVG arrow graphics = ~5KBs Hyper-light Angle quotes + border-radius = 1KB <style type=“text/css”> .arrow { background-color: #4D80AB; border-radius: 50%; color: white; font-weight: bold; height: 1rem; width: 1rem; } … </style> … <a href=“#prev” class=“arrow”>&lasquo;</a> <span class=“pages”><em>1</em> of <em>5</em></span> <a href=“#next” class=“arrow”>&rasquo;</a>
  • 37.
    93% of users Replacing SVGwith HTML and CSS. Original 2 x SVG arrow graphics = ~5KBs Hyper-light Angle quotes + border-radius = 1KB <style type=“text/css”> .arrow { background-color: #4D80AB; border-radius: 50%; color: white; font-weight: bold; height: 1rem; width: 1rem; } … </style> … <a href=“#prev” class=“arrow”>&lasquo;</a> <span class=“pages”><em>1</em> of <em>5</em></span> <a href=“#next” class=“arrow”>&rasquo;</a>
  • 38.
    Using an SVGsprite with fragment identifiers. <svg xmlns="http://www.w3.org/2000/svg" style="display: none"> <symbol id="icon-comments" viewBox="380 0 20 20"> <title>Comments</title> <path d=“M394.57,4.19H385a1.2,…” style=“fill: #1383b3;” /> </symbol> … </svg> … <svg><use xlink:href=“#icon-comments” /></svg> 1. https://css-tricks.com/svg-symbol-good-choice-icons/
  • 39.
    Using an SVGsprite with fragment identifiers. 91% of users 1. https://css-tricks.com/svg-symbol-good-choice-icons/ <svg xmlns="http://www.w3.org/2000/svg" style="display: none"> <symbol id="icon-comments" viewBox="380 0 20 20"> <title>Comments</title> <path d=“M394.57,4.19H385a1.2,…” style=“fill: #1383b3;” /> </symbol> … </svg> … <svg><use xlink:href=“#icon-comments” /></svg>
  • 40.
    Image-based sprites. <style type=“text/css”> .car-image{ background-image: url(/car-sprite.jpg); background-size: 100%; height: 60px; width: 140px; } #sports-car.car-image { background-position: -280px -120px; } </style> <div id=“sports-car” class=“car-image”></div>
  • 41.
    In most cases,HTTP/2 pipelining is the better approach. ⚠ Image-based sprites. <style type=“text/css”> .car-image { background-image: url(/car-sprite.jpg); background-size: 100%; height: 60px; width: 140px; } #sports-car.car-image { background-position: -280px -120px; } </style> <div id=“sports-car” class=“car-image”></div>
  • 42.
    Image-based sprites. 96% of users Inmost cases, HTTP/2 pipelining is the better approach. ⚠ <style type=“text/css”> .car-image { background-image: url(/car-sprite.jpg); background-size: 100%; height: 60px; width: 140px; } #sports-car.car-image { background-position: -280px -120px; } </style> <div id=“sports-car” class=“car-image”></div>
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
    GZIP compression. # Asimple nginx.conf with gzip turned on. server { listen 8080; server_name localhost; gzip on; gzip_comp_level 9; location / { root /usr/share/nginx/html; index index.html index.htm; } }
  • 49.
    GZIP compression. Make sureto vary on Accept-Encoding in your caching layers. ⚠ # A simple nginx.conf with gzip turned on. server { listen 8080; server_name localhost; gzip on; gzip_comp_level 9; location / { root /usr/share/nginx/html; index index.html index.htm; } }
  • 50.
    99% of users GZIP compression. Makesure to vary on Accept-Encoding in your caching layers. ⚠ # A simple nginx.conf with gzip turned on. server { listen 8080; server_name localhost; gzip on; gzip_comp_level 9; location / { root /usr/share/nginx/html; index index.html index.htm; } }
  • 51.
    Experiment with Brotli. tom@servo:/www$cd /data/repos && git clone https://github.com/google/brotli.git tom@servo:/www$ sudo ln -s /data/repos/brotli/python/bro.py /usr/local/bin/bro.py tom@servo:/www$ bro.py -i index.html -o index.html.br
  • 52.
    tom@servo:/www$ cd /data/repos&& git clone https://github.com/google/brotli.git tom@servo:/www$ sudo ln -s /data/repos/brotli/python/bro.py /usr/local/bin/bro.py tom@servo:/www$ bro.py -i index.html -o index.html.br Experiment with Brotli.
  • 53.
    Experiment with Brotli. 84% ofusers tom@servo:/www$ cd /data/repos && git clone https://github.com/google/brotli.git tom@servo:/www$ sudo ln -s /data/repos/brotli/python/bro.py /usr/local/bin/bro.py tom@servo:/www$ bro.py -i index.html -o index.html.br
  • 54.
  • 55.
  • 56.
  • 57.
    Measuring the resultsusing Sitespeed. tom@servo:/data$ docker run -v “$(pwd)”:/sitespeed.io > sitespeedio/sitespeed.io:7.3.6 https://www.caranddriver.com
  • 58.
    Zeroing in onthe biggest wins. 15 images requests ~600KBs 1 image request ~50KBs
  • 59.
    An SVG spritesheet would be even better at ~15KBs. Zeroing in on the biggest wins. 15 images requests ~600KBs 1 image request ~50KBs
  • 60.
  • 62.
    Original Improved Hyper-Light Requests181 🥁 32 Domains 50 2 Bandwidth Total Content Size 8.7 MBs 1.1 MBs Total Transfer Size 5.9 MBs 1.0 MBs Non-Images 2.0 MBs 19 KBs Image Content 3.9 MBs 999.4 KBs Billed By CDN 4.9 MBs 1.0 MBs Response Times (First Paint —> Fully Loaded) 2G 22 sec —> 2 min 47 sec 773 msec —-> 11 sec 3G 6.4 sec —> 53 sec 556 msec —> 3.7 sec Hi-Speed 1.5 sec —-> 16.7 sec 435 msec —> 621 msec
  • 63.
    Original Improved Hyper-Light Requests181 234 32 Domains 50 74 2 Bandwidth Total Content Size 8.7 MBs 1.1 MBs Total Transfer Size 5.9 MBs 1.0 MBs Non-Images 2.0 MBs 19 KBs Image Content 3.9 MBs 999.4 KBs Billed By CDN 4.9 MBs 1.0 MBs Response Times (First Paint —> Fully Loaded) 2G 22 sec —> 2 min 47 sec 773 msec —-> 11 sec 3G 6.4 sec —> 53 sec 556 msec —> 3.7 sec Hi-Speed 1.5 sec —-> 16.7 sec 435 msec —> 621 msec
  • 64.
    Original Improved Hyper-Light Requests181 234 32 Domains 50 74 2 Bandwidth Total Content Size 8.7 MBs 6.2 MBs 1.1 MBs Total Transfer Size 5.9 MBs 2.8 MBs 1.0 MBs Non-Images 2.0 MBs 1.9 MBs 19 KBs Image Content 3.9 MBs 993.5 KBs 999.4 KBs Billed By CDN 4.9 MBs 1 MBs 1.0 MBs Response Times (First Paint —> Fully Loaded) 2G 22 sec —> 2 min 47 sec 773 msec —-> 11 sec 3G 6.4 sec —> 53 sec 556 msec —> 3.7 sec Hi-Speed 1.5 sec —-> 16.7 sec 435 msec —> 621 msec
  • 65.
    Original Improved Hyper-Light Requests181 234 32 Domains 50 74 2 Bandwidth Total Content Size 8.7 MBs 6.2 MBs 1.1 MBs Total Transfer Size 5.9 MBs 2.8 MBs 1.0 MBs Non-Images 2.0 MBs 1.9 MBs 19 KBs Image Content 3.9 MBs 993.5 KBs 999.4 KBs Billed By CDN 4.9 MBs 1 MBs 1.0 MBs Response Times (First Paint —> Fully Loaded) 2G 22 sec —> 2 min 47 sec 7.6 sec —-> 1 min 12 sec 773 msec —-> 11 sec 3G 6.4 sec —> 53 sec 2.7 sec —> 19.3 sec 556 msec —> 3.7 sec Hi-Speed 1.5 sec —-> 16.7 sec 627 msec —> 9.7 sec 435 msec —> 621 msec
  • 66.
    • Repeat theprocess with other pages. • Run a 1% test in the wild. • Build a performance budget. • Run Sitespeed (or similar) regularly. { "browsertime.pageSummary": [{ "metric": "statistics.timings.firstPaint.median", "max": 1500 }], "pagexray.pageSummary": [{ "metric": "transferSize", "max": 1000000 }, { "metric": "requests", "max": 45 } } 1. https://www.sitespeed.io/documentation/sitespeed.io/performance-budget/ Taking the next steps…
  • 67.
    Source code: https://github.com/zacman85/hyperlight-websites Live demo: http://hlw.chriszacharias.com Specialthanks to Miguel Cardona. Thankyou! Chris Zacharias CEO, FOUNDER @ chris@imgix.com @zacman85