Take a

(client)

Hint

A talk by Jeremy Wagner

 malchata ::  malchata ::  jeremy.codes

Hi!

I'm Jeremy!

A cartoon line art drawing of Jeremy Wagner.
An animated GIF of a caffienated Kosmo Kramer walking extremely fast.

What are

client hints?

A cartoon drawing of a browser with a sad face in the document body.
A cartoon drawing of a web server with a question mark hovering over it.

Content
negotiation

The HTTP

A cartoon drawing of a sparkle emoji.AcceptA cartoon drawing of a sparkle emoji.

header

Accept: image/webp,image/*

Value contextual to requested resource.

Actual header content truncated for brevity.
<!-- Load a WebP if we can (but fall back if we can't). -->
<picture>
  <source srcset="whats-up.webp" type="image/webp">
  <img src="whats-up.jpg" alt="I'm an image!">
</picture>
RewriteEngine On
RewriteCond %{HTTP:Accept} image/webp [NC]
RewriteCond %{DOCUMENT_ROOT}/$1.webp -f [NC]
RewriteRule (.+)\.(png|jpe?g|gif)$ $1.webp [T=image/webp,L]
<!-- If WebP is supported, this JPEG request is
     automatically rewritten to a WebP! -->
<img src="whats-up.jpg" alt="I'm an image!">
A survey by Pew Research shows that fully one-third of American adults do not subscribe to any Internet access faster than dial-up at their home at a time when many basic tasks—finding job listings, doing homework, obtaining social services, and even performing many jobs—require being online.

Adaptive
performance

An outline of the state of Wisconsin.
A map showing broadband connectivity for Buffalo County, WI in the United States.

Performance

bridges the gap

Client hints

can take us further

- Opting in

- Device Hints

- Network hints

- Parting Notes

A cartoon drawing of two checkboxes with a pencil near them. One says 'opt out', while the other says 'opt in'. The 'opt in' box is checked.

Client hints are

Opt-in only!

A cartoon drawing of a padlock.

Client hints

require HTTPS!

A cartoon drawing of the Chrome icon.

Client hints work in
Chrome only!*

*And derived browsers.
A screenshot of caniuse.com showing browser support information for client hints.

Opt in with

Accept-CH

Example header:

Accept-CH: Width, DPR

Via HTML:

<meta http-equiv="Accept-CH" content="Width, DPR">

Via PHP:

<?php header("Accept-CH: Width, DPR"); ?>

Persist hints with

Accept-CH-Lifetime

Example header:

Accept-CH-Lifetime: 86400

Via HTML:

<meta http-equiv="Accept-CH-Lifetime" content="86400">

Via PHP:

<?php header("Accept-CH-Lifetime: 86400"); ?>
A screenshot of client hint request headers shown in Chrome's developer tools.
A cartoon drawing of a construction barricade with a sign on it that reads "Oh no!".

Here we go!

- Opting in

- Device Hints

- Network hints

- Parting Notes

A cartoon drawing of a mobile device with a blank screen.

Responsive images

are hard to get right!

<!-- This is pretty complicated! -->
<picture>
  <source sizes="(min-width: 512px) 90vw,
                 (min-width: 768px) 66.66vw,
                 512px"
          srcset="/image-crop-256w.webp 256w,
                  /image-crop-512w.webp 512w,
                  /image-crop-768w.webp 768w,
                  /image-crop-1024w.webp 1024w"
          type="image/webp">
  <img sizes="(min-width: 512px) 90vw,
              (min-width: 768px) 66.66vw,
              512px"
       srcset="/image-crop-256w.jpg 256w,
               /image-crop-512w.jpg 512w,
               /image-crop-768w.jpg 768w,
               /image-crop-1024w.jpg 1024w"
       src="/image-crop-256w.jpg"
       alt="I'm an image!">
</picture>
<!-- Ah, much simpler! -->
<img src="/images/image.jpg" alt="I'm an image!">
Wouldn't it be nice?

A cartoon drawing of a sparkle emoji.DPRA cartoon drawing of a sparkle emoji.

Device Pixel Ratio.

DPR: 2

<img src="whats-up-1x.png"
     srcset="whats-up-1x.png 1x, whats-up-2x.png 2x"
     alt="I'm an image!">
<?php
// This is just an example, maybe find a different
// fallback (serve `srcset` markup or use a cookie).
$dpr = 2;

if (isset($_SERVER["HTTP_DPR"])) {
  $dpr = (int)$_SERVER["HTTP_DPR"];
}
?>
<img src="image-<?php echo($dpr); ?>x.jpg" alt="I'm an image!">

A cartoon drawing of a sparkle emoji.WidthA cartoon drawing of a sparkle emoji.

The optimal width for
a requested image resource.

Width

only appears for
image requests!

Width

requires sizes to be used

<img src="/image.jpg" sizes="(min-width: 620px) 600px, 96vw">

Width: 1200

Intrinsic size

is the size of a resource in physical pixels.

Density-corrected
Intrinsic size

is the intrinsic size, but divided by DPR to correct for pixel density.

Extrinsic size

is the size of the image in the layout
as affected by CSS.

(Or by width and height attributes.)

Width

does all the math for you!

Using `w_auto`, you can fit images to their layout width. Since the feature's debut, it has delivered a median byte-size savings of 42% (51 KB) compared to the baseline.

A cartoon drawing of a sparkle emoji.Viewport-WidthA cartoon drawing of a sparkle emoji.

The width of the viewport in CSS pixels.

A cartoon drawing of browser viewport diagram.

Viewport-Width: 320

Uses for Viewport-Width:

  • Art direction (use in concert with Width).
  • Image or video omission.
  • Functionality omission (e.g., JavaScript).
<?php
// A sensible default in the absence of `Viewport-Width`
//  _could_ be to set a high value and deliver all
//  functionality (or maybe set a cookie).
$viewportWidth = 1280;

if (isset($_SERVER["HTTP_VIEWPORT_WIDTH"])) {
  $viewportWidth = (int)$_SERVER["HTTP_VIEWPORT_WIDTH"];
}
?>
<body>
  <div class="big-hero-banner parallax"></div>
  <!-- ... -->
  <!-- This is a critical script for all devices -->
  <script src="/js/main.js"></script>
  <?php
  if ($viewportWidth >= 800) {
    // Only load this script for large screens that need it.
    ?><script src="/js/parallax.js"></script><?php
  }
  ?>
</body>
<body>
  <!-- This image is used on all devices. -->
  <img src="img/hero.jpg">
  <!-- This one is only used on large screens. -->
  <?php
  if ($viewportWidth >= 640) {
    ?><img src="img/aside.jpg" alt="I'm an image!"><?php
  }
  ?>
</body>
A cartoon drawing of a gear representing service worker technology.

Service Worker!

self.addEventListener("fetch", event => {
  // Access hints via `event.request.headers.get`
  let dpr = event.request.headers.get("DPR");
  let viewportWidth = event.request.headers.get("Viewport-Width");
  let width = event.request.headers.get("Width");

  event.respondWith(async function() {
    // Do what you will with these hints!
  }());
});

- Opting in

- Device Hints

- Network hints

- Parting Notes

A cartoon drawing of a network cable.

Networks are

fragile!

A cartoon drawing of a transmission tower.

A cartoon drawing of a sparkle emoji.Save-Data*A cartoon drawing of a sparkle emoji.

"Help me use less data!"

*not an opt-in hint.

Save-Data: on

Sent when Data Saver in Chrome for Android is turned on.

A cartoon drawing of a sparkle emoji.RTTA cartoon drawing of a sparkle emoji.

Round Trip Time

(in ms)

RTT: 150

Nearest multiple of 25 ms

A cartoon drawing of a sparkle emoji.DownlinkA cartoon drawing of a sparkle emoji.

Downstream speed

(in Mbps)

Downlink: 1.75

Nearest multiple of 25 Kbps

A cartoon drawing of a sparkle emoji.ECTA cartoon drawing of a sparkle emoji.

Effective Connection Type

ECT: 3g

Enumerated value
ECT valueRTT (ms)Downlink (Kbps)
slow-2g>= 2,000<= 50
2g>= 1,400<= 70
3g>= 270<= 700
4g>= 0

These hints should be used together!

<?php
function network_quality() {
  $quality = 1.0;

  // If the Save-Data hint is present, assume
  // the user wants the lightest experience possible.
  if (isset($_SERVER["HTTP_SAVE_DATA"]) &&
      $_SERVER["HTTP_SAVE_DATA"] === "on") {
    $quality = 0.0;
    return $quality;
  }

  // Additional network quality logic omitted...

  return $quality;
}
?>
ECTQuality Degradation
4g0.0
3g0.2
2g0.4
slow-2g0.6
RTTQuality Degradation
> 8350.1
> 17000.1
> 24000.1
DownlinkQuality Degradation
< 0.3850.05
< 0.060.05
< 0.0330.05
<?php
// Could return 0.65 on a less than optimal
// (but not _totally_ crappy) network connection
network_quality();
?>

Yeah, but...

What can we do with this?

  • Serve lower-density images for slower connections.
  • Serve still images in lieu of video.
  • Omit unnecessary resources (e.g., JavaScript).
  • Omit requests for optional resources on cross origins.
<head>
  <title>Sconnie Timber</title>
  <meta charset="utf-8">
  <?php
  // Only show fonts if the network is fast enough
  if (network_quality() > 0.5) {
    ?>
    <link rel="stylesheet"
          href="https://fonts.googleapis.com/css?family=Lato:400"
          type="text/css">
    <?php
  }
  ?>
  <link rel="stylesheet" href="css/main.css" type="text/css">
</head>

JS equivalents

(in navigator.connection)
PropertyAssociated Header
rttRTT
downlinkDownlink
effectiveTypeECT
saveDataSave-Data
Part of the Network Information API
A cartoon drawing of a gear representing service worker technology.

Service Worker!

self.addEventListener("fetch", event => {
  // Let's see about that netinfo API first...
  if ("connection" in navigator) {
    var saveData = navigator.connection.saveData;
    var ect = navigator.connection.effectiveType;
    var rtt = navigator.connection.rtt;
    var downlink = navigator.connection.downlink;
    var downlinkMax = navigator.connection.downlinkMax;
  }

  event.respondWith(async function() {
    // Do what you will with these hints!
  }());
});

When sites

don't adapt

The Sconnie Timber site when no client hints are used to modify the response.

When sites

adapt

The Sconnie Timber site when no client hints are used to modify the response.
 RequestsKiBLoad (s)Client Hints?
3G2274021.00No
3G2241415.53Yes
2G2274091.26No
2G5125.17Yes
*Tests performed on throttled connections.

- Opting in

- Device Hints

- Network hints

- Parting Notes

A cartoon drawing of a gift box with a bow wrapped around it. GET IT? WRAPPING UP?

A cartoon drawing of a sparkle emoji.VaryA cartoon drawing of a sparkle emoji.

It's Vary important!

A cartoon drawing of a sparkle emoji.VaryA cartoon drawing of a sparkle emoji.

Keys cache entries!

Modifying a response with Save-Data:

<?php
if (isset($_SERVER["HTTP_SAVE_DATA"]) === false) {
  // Show this image if the user has data saver off.
  ?><img src="some-image.jpg"><?php
}
?>

Setting Vary:

<FilesMatch "\.(html|php)$">
  Header set Vary "Accept-Encoding, Save-Data"
</FilesMatch>                       ^^^^^^^^^

Be careful what you

Vary

Only set hints you'll use!

A cartoon drawing of a padlock.

Client hints

require HTTPS!

A cartoon drawing of the Chrome icon.

Client hints work in
Chrome only!*

*And derived browsers.

Time to

wrap up

With thanks to:

A photo of Eric Portis.

Eric Portis
 etportis

A photo of Colin Bendell.

Colin Bendell
 colinbendell

A photo of Ilya Grigorik.

Ilya Grigorik
 igrigorik

...and thank you!

Slides:

jlwagner.net/talks/take-a-client-hint



Client hints spec:

tools.ietf.org/html/draft-grigorik-http-client-hints



Example code:

github.com/malchata/client-hints-example