[TITLE MUSIC PLAYING]
ADDY OSMANI: Hey, folks.
My name is Addy.
This is Eva.
We work on Chrome.
And today we're going to give you
a whirlwind tour of tips and tricks
for making your sites load fast.
So let's get started.
EWA GASPEROWICZ: The internet gets
heavier and heavier every year.
If we check in on the state of mobile web,
we can see that a median page of mobile
weighs just about 1.5 megs, with the majority of that
That's quite a lot.
Apart from the sheer size, though, there
are other reasons why a web page might feel sluggish and heavy.
Third-party code, network latency,
CPU limitations, partial blocking patterns.
All of these contribute to the complicated performance puzzle.
We've been pretty busy over the past year trying to figure out
how to fit this rich content of mobile web
into the smallest package possible.
In this talk, Addy and I are going
to show you what you can do in this regard today,
but also we'll take a peek into the future
and give you a highlight of what might be possible really,
ADDY OSMANI: So I'm not the biggest fan of suitcases.
The last time I was at the airport
waiting at the baggage carousel for my bags,
I looked around for my suitcase, and all I could find was this.
I, too, laughed the first four times this went around.
I was really happy to get this back.
I just wanted more of it.
Now, the experience that I had at a baggage carousel
is a lot like the experience our users have
when they're waiting for a page to load.
In fact, most users rate speed as being
at the very top of the UX hierarchy of their needs.
And this isn't too surprising, because you can't really
do a whole lot until a page is finished loading.
You can't derive value from the page.
You can't admire it's aesthetics.
Now we know that performance matters,
but it can also sometimes feel like a secret discovering
where to start optimizing.
And so we've been working on trying
to make this problem a little easier for a while.
And today we have an exciting announcement to share with you.
We like to call it a Paul Irish in your pocket.
EWA GASPEROWICZ: Well, we're happy to announce
a newly expanded set of web performance
audits in Lighthouse.
Lighthouse, as you know, is part of developer tools
that allow you to make an audit of your website,
and also gives you hints on how to make it better.
It's been around for a while, right, but as some of you
have heard today in the morning, it's
been also actively worked on.
And today, I'm happy to share with you some
of the newest audits that landed in the Lighthouse
over past quarters.
We'll try to show them to you in action
during the rest of the talk.
ADDY OSMANI: Oh, next one's yours.
EWA GASPEROWICZ: As the internet saying goes,
fixing web performance is as easy as drawing a horse, right.
You just need to follow some steps carefully.
But even though there is some truth in the picture above,
the good news is you can get pretty far
by following some simple steps.
In order to show you the steps, Addy prepared something special
ADDY OSMANI: So Ewa and I are really
big fans of Google Doodles.
Google's been doing them since 1998,
and we thought it would be fun to create a nice little app
where we can show you some of our favorite interactive
doodles from over the years.
We call it the Oodle Theater.
Here it is in action.
[TREE FROGS CHIRPING]
[CAR ENGINE RUNNING]
[EERIE STRING MUSIC PLAYING]
[MUTED TRUMPET PLAYING]
[VIDEO PLAYBACK ENDS]
ADDY OSMANI: So we kick it off with the drive-through,
and as we can see, we can now start
browsing through the application looking
at doodles over the years.
We can click through to one, and even start playing.
[VIDEO PLAYBACK BEGINS]
[BRASS FANFARE PLAYING]
[VIDEO PLAYBACK ENDS]
ADDY OSMANI: We're not going to ruin the surprise.
You can go and you can play some of these in your own time.
Just not during this talk, please.
So let's begin our journey into the Wild
West of web performance.
And it all begins with one tool--
EWA GASPEROWICZ: --of course, Lighthouse.
The initial performance of our app,
as you can see on this Lighthouse report,
was pretty terrible.
On a 3G network, the user needed to wait
for 15 seconds for the first meaningful paint
or for the app to get interactive.
Lighthouse highlighted a ton of issues with our site,
and the overall performance score of 23
mirrored exactly that.
The page weighted at about 3.4 megs.
We desperately needed to cut some fat.
This started our first performance challenge--
find things that we can easily remove
without affecting the overall performance experience.
So what is the easiest thing to remove?
And by nothing, I mean things that
do not really contribute to the code, like white space
Lighthouse highlights this opportunity in the Minify CSS
We were using Webpack for our build process,
so in order to get minification, we simply
use the Uglify JS plugin.
Minification is a common task, so you
should be able to find a ready-made solution
for whichever build process you happen to use.
Other useful audit in that space is enable text compression.
There is no reason to send uncompressed files,
and most of the CTNs support this out of the box these days.
We were using Firebase Hosting to host our code,
and Firebase actually enables GCP by default.
So by the sheer virtue of hosting
our code on a reasonable CTN, we got that for free.
And while GCP is a very popular way of compressing,
other mechanisms like Zopfli and Brotli
are getting traction as well.
Brotli enjoys support in most of the browsers these days,
and you can use binary to pre-compress
your assets before sending them to the server.
In these two ways, we make sure that our code
is nice and compact and ready to present to the user.
Our next task was to not send it twice if not necessary.
The inefficient cache policy audit in Lighthouse
helped us notice that we could be optimizing our caching
strategies in order to achieve exactly that.
By setting a max stage expiration header
in our server, we make sure that on the repeated visit,
the user can reuse the resources they have downloaded before.
Ideally, you should aim at caching as many resources
as securely possible for the longest possible period
of time, and provide validation tokens
for efficient re-validation.
All the changes we made so far were very straightforward,
They required no code whatsoever.
It was really low-hanging fruit with very little risk
of breaking anything.
So remember, always minimize the code, preferably automating it
with build tools, by using the right CDNs,
or by adding optimization modules to your own servers.
Always compress your assets and use efficient cache policies
to optimize repeated visits.
OK, this may remove the obvious part
of the unnecessary downloads.
But what about the less obvious part--
for example, unused code?
As it happens, unused code can really surprise us.
It may linger in the dark corners of your codebase,
idle and long forgotten, and yet making it
to the user's bandwidth each time the app is loaded.
This has been especially if you work on your app for a longer
period of time.
Your team or your dependencies change,
and sometimes an orphan library gets left behind.
That's exactly what happened to us.
At the beginning, we were using material components library
to quickly prototype our app.
In time, we moved to a more custom look and feel,
and of course we forgot entirely about that library.
Fortunately, the Code Coverage check
helped us rediscover it in our bundle.
You can check your Code Coverage stats in DevTools,
both for the runtime and load time of your application.
You can see the two big red stripes
in the bottom screenshot.
Lighthouse also picked up this issue in the unused CSS rules
It showed a potential savings of over 400 kilobytes.
and CSS part of that library.
This brought our CSS bundle down 20-fold,
which is pretty good for a tiny two-line commit, right?
Of course it made our performance score go up,
and also the time to interactive got much better.
However, with changes like this, it's
not enough to check your metrics and scores alone.
Removing actual code is never risk-free,
so you should always look out for potential regressions.
Remember that our code was unused in 95%?
Well, there's still this 5%, right.
Apparently one of our components was still using the slides
from that library--
the little arrows in the middle of the slide there.
Because it was so small, though, we could just go in manually
and incorporate those slides back into the patterns.
So if you remove code, just make sure you have a proper testing
workflow in place to help you guard
against potential visual regressions.
So remember, Code Coverage, both in DevTools and in Lighthouse,
is your friend when it comes to spotting and removing
Do this check regularly throughout the development
of your app to keep your code base clean and tidy,
and to test your changes thoroughly before deployment.
Well, so far, so good.
All of those changes made our app a little bit lighter.
It was still to slow to Addy, so he took it that bit farther.
Addy, how did it go?
ADDY OSMANI: It went so, so good.
Now, some white pages are like heavy suitcases.
You have some stuff that's really important,
and then you have crap and even more crap.
We know the large resources can slow down web page loads.
They can cost their users money, and they
can have a big impact on their data plans,
so it's really important to be mindful of this.
Now Lighthouse was able to detect
that we had an issue with some of our network payloads
using the enormous network payload audit.
Here we saw that we had over 3 megs worth of code
that was being shipped down, which is quite a lot,
especially on mobile.
At the very top of this list, Lighthouse
was 2 megs of uncompressed code that we
were trying to ship down.
This is also a problem highlighted by Webpack.
Now what we say is that the fastest request
is the one that's not made.
Ideally, you should be measuring the value of every single asset
you're serving down to your users,
measuring the performance of those assets,
and making the call on whether it's worth actually shipping
down with the initial experience,
because sometimes these assets could be deferred, or lazily
loaded, or processed during idle time.
In our case, because we're dealing
We started off with Webpack bundle analyzer,
which informed us that we were including a dependency called
We then went over to our editor, and using the import cost
plugin for visual code, we were able to visualize
the cost of every module we were importing.
This allowed me to discover which
component was including code that
was referencing this module.
We then switched over to another tool, Bundle Phobia.
This is a tool which allows you to enter in the name of any NPM
package and actually see what its minified and g sub
size is estimated to be.
We found a nice alternative for the slug
module we were using that only weighed 2.2 kilobytes,
and so we switched that up.
This had a big impact on our performance.
Between this change and discovering other opportunities
we saved 2.1 megabytes of code.
We saw 65% improvements overall once you
factor in the g subbed and minified size of these bundles,
and we just found that was really worth
doing as a process.
So in general, try to eliminate unnecessary downloads
in your site synapse.
In the case of the Oodle Theater, an app that already
has games and a lot of interactive multimedia content,
it's important for us to keep the application shell as
lightweight as possible.
We found that inventorying our assets
and measuring their performance impact
made a really big difference.
So just make sure that you're auditing your assets fairly
Now, although large network payloads
can have a big impact on our app,
there's another thing that can have a really big impact,
the median page includes a little bit too much of it.
On mobile, if you're sending down
delay how soon your users are able to interact with the user
That means they can be tapping on UI without anything
meaningful actually happening.
So it's important for us to understand
We first of all have to download that script.
then needs to parse that code, needs to compile it
and execute it.
Now, these phases are something that
don't take a whole lot of time on a high-end device
like a desktop machine, or a laptop, maybe
even a high-end phone.
But on a median mobile phone, this process
can take anywhere between five and 10 times longer.
This is what delays interactivity.
So it's important for us to try trimming this down.
Now, to help you discover these issues with your app,
And in the case of the Oodle app,
it told us that we had 1.8 seconds of time which was being
The way that this was happening was that we were statically
importing in all of our routes and opponents
One technique for working around this is using code-splitting.
So code-splitting is this notion of instead of giving your users
what if you only gave them one slice at a time as they needed
Code-splitting can be applied at a route level or a component
It works great with React and React Loadable, Vue.js, Angular,
Polymer, Preact, and multiple other libraries.
So we incorporated code-splitting
into our application.
We switched over from static imports to dynamic imports,
allowing us to asynchronously lazy load
code in as we needed it.
And the impact this had was both shrinking down
Took it down to 0.78 seconds, making the app 56% faster.
So in general, if you're building
sure to only send code to the user that they need.
Take advantage of concepts like code-splitting,
explore ideas like tree-shaking, and check out
this repo we have of a few ideas around how you can trim down
your library size if you happen to be using Webpack.
we know that un-optimized images can also be a problem, too.
So over to Ewa to talk about optimizing images.
EWA GASPEROWICZ: Images.
Internet loves images and so do we.
In Oodle Lab, we're using them in the background,
in the foreground, and pretty much everywhere.
Unfortunately, Lighthouse was much less enthusiastic about it
than we were.
As a matter of fact, we failed on all three
We forgot to optimize our images,
we were not sizing them correctly,
and also, we could get some gain from using other image
formats as well.
So we started optimizing our images.
For one of optimization routes, you can use visual tools
like ImageOptim or XNConvert.
A more automated approach is to add an image optimization
step to your build process, with libraries like, for example,
This way you make sure that also the images added in the future
get optimized automatically.
Some CDNs-- for example, Akamai, or third-party solutions like
Cloudinary or Fastly--
offer you comprehensive image optimization solutions.
So to save yourself some time and headache,
you can simply host your images on those services.
If you don't want to do that because of the cost or latency
issues, projects like Thumbor or Imageflow
offer self-hosted alternatives.
Here you can see a single image optimization outcome.
Our background PNG was flagged in Webpack
as big, and rightly so.
After sizing it correctly to the viewport,
and running it through ImageOptim,
we went down to 100 kilobytes, which is acceptable.
Repeating this for multiple images on our site
allowed us to bring down the overall page wait
That was pretty easy for static images,
but what about the animated content?
As much as we all love GIFs, especially the ones
with the cats in them, they can get really expensive.
Surprisingly, the GIF format was never
intended as an animation platform in the first place.
Therefore, switching to more suitable video format
offers you a large savings in terms of the file size.
In Oodle Lab, we were using a GIF
you saw earlier as an intro sequence on the home page.
According to Lighthouse, we could be saving over
seven megabytes by switching to a more efficient video format.
Our clip weighed about 7.3 megs--
way too much for any reasonable website.
So instead, we turned it into a video element
with two source files--
an MP4 and WebM for wider browser support.
Here you can see how we used FFmpeg tool
to convert our animation GIF into the MP4 file.
The WebM format offers you even larger savings,
and for example, ImageOptim API can do such conversion for you.
We managed to save over 80% of our overall weight
thanks to this conversion.
This brought us down to around 1 megabyte.
Still, 1 megabyte is a large resource
to push down the wire, especially
for the user on the restricted bandwidth.
Luckily, we could use effective type API
to realize they're on the slow bandwidth,
and give them a much, much smaller JPEG instead.
This interface uses the effective round trip time
in downloading the values to estimate the network
type the user is using.
It's simply does a string--
slow 2G, 2G, 3G, and 4G.
So depending on this value, if the user is on below 4G,
we could replace the video element with the image.
It does remove a little bit from the experience,
but at least the site is usable on a slow connection as well.
Last but not least, there is a very common problem
of off-screen images.
Carousels, sliders, or really long pages often load images
even though the user cannot see them on the page straightaway.
Lighthouse will flag this behavior
in the off-screen images audit, and you can also
see it for yourself in the network panel of DevTools.
If you see a lot of images incoming, while only a few
are visible on the page, it means
that maybe you could consider lazy loading them instead.
Lazy loading is not yet supported natively
to add this capability.
Here you can see how we use LazySizes to add lazy loading
behavior to our Oodle covers.
LazySizes is smart because it does not only
track the visibility changes of the element,
but it also proactively pre-fetches elements
that are near the view for the optimized user experience.
It also offers an optional integration
of the IntersectionObserver, which
gives you very efficient visibility lookups.
As you can see, after this change,
our images are being fetched on demand instead of straightaway.
That's a lot of good stuff about images.
So just remember, always optimize images
before pushing them to the user.
Use responsive images techniques to achieve
the right size of the image.
Use lighter formats wherever possible,
especially for animated content.
And finally, lazy load whatever is not
immediately visible to the user.
If you want to dig deeper into that topic,
here's a present for you--
a very handy and comprehensive guide written by Addy.
So you can access it at images.guide URL.
ADDY OSMANI: Cool.
So next let's talk about resources
that are discovered and delivered late by the browser.
Now, not every byte that's shipped down the wire
to the browser has the same degree of importance,
and the browser knows this.
A lot of browsers have heuristics
to decide what they should be fetching first.
So sometimes, they'll fetch CSS before images or scripts.
Now something that could be useful
is, as authors of the page, informing
the browser what's actually really important to us.
Thankfully, over the last couple of years,
browser vendors have been adding a number of features
to help us with this, so things like link rel-preconnect,
or preload, or prefetch.
These capabilities that were across the web platform
helped the browser fetch the right thing at the right time,
and they could be a little bit more efficient than some
of the custom loading logic-based approaches that
are done using script instead.
So let's see how Lighthouse actually
guides us towards using some of these features effectively.
So the first thing Lighthouse tells us to do
is avoid multiple costly round trips to any origin.
Now, in the case of the Oodle app,
we're actually heavily using Google Fonts.
Whenever you drop in a Google Fonts style sheet
into your page, it's going to connect up to two subdomains.
What Lighthouse is telling us is that if we were able to warm up
that connection, we could save anywhere up to 300 milliseconds
in our initial connection time.
Now taking advantage of link rel-preconnect,
we can effectively mask that connection latency.
Especially with something like Google Fonts where
our font face CSS is hosted on googleapis.com
and our font resources are hosted on gstatic,
this can have a really big impact.
So we applied this optimization and we shaved of a few hundred
The next thing that Lighthouse suggests
is that we preload key requests.
Now link rel-preload is really powerful.
It informs the browser that a resource
is needed as part of the current navigation,
and it tries to get the browser fetching it
as soon as possible.
Now here, Lighthouse is telling us
that we should be going and preloading our key webfont
resources because we're loading into web fonts.
Preloading in a web font looks like this, so specifying rel
equals preload, you pass in as with the type of font,
and then you specify the type of font you're trying to load in,
such as woff2.
The impact that this can have on your page is quite stark.
So normally, without using link rel-preload,
if web fonts happen to be critical to your page, what
the browser has to do is it first of all
has to fit your HTML, it has to parse your CSS,
and somewhere much later down the line,
it'll finally go and fetch your web fonts.
Using link rel-preload, as soon as the browser has parsed
your HTML, it can actually start fetching those web fonts
much earlier on.
In the case of our app, this was able to shave a second
off the time it took for us to render text
using our web fonts.
Now it's not quite that straightforward
if you're going to try preloading fonts using
There is one gotcha.
You see, the Google Font URLs that we specify
in our font faces in our style sheets happen to be something
that the fonts team update fairly regularly.
These URLs can expire or get updated on a regular frequency,
and so what we suggest you do if you
want complete control of your font loading experience
is to self-host your web fonts.
This can be great because it gives you
access to things like link rel-preload.
In our case, we found the tool Google Web Fonts Helper
really useful in just helping us offline some of those web fonts
and set them up locally, so check that tool out.
Now whether you're using web fonts
as part of your critical resources
to help the browser deliver your critical resources
as soon as possible.
If you're connecting up to multiple origins that
are critical, consider using link rel-preconnect.
If you have an asset for the current page that's
really important, use link rel-preload.
We have to have a good preload plugin available for Webpack.
And if you have an asset for future navigation,
consider using prefetch.
This is something that has good support now in the latest
version of Webpack, as well as preload.
Now we've got something special to share with you today.
In addition to features like resource hints
as well as preload, we've been working
on a brand-new experimental browser feature
we're calling Priority Hints.
This is a new feature that allows
you to hint to the browser how important a resource is.
It exposes a new attribute, importance, with the values,
low, high, or auto.
This allows us to convey lowering
the priority of less important resources,
such as non-critical styles, images, of fetch API calls,
to reduce contention.
We can also boost the priority of more important things,
like our hero images.
In the case of our little app, this actually
led to one practical place where we could optimize.
So before we added lazy loading to our images,
what the browser was doing is we had this image carousel
with all of our doodles, and the browser
was fetching all the images at the very start of the carousel
with a high priority early on.
Unfortunately, it was the images in the middle
of the carousel that were most important to the user.
So what we did was we set the importance of those background
images to very low, the foreground ones to very high,
and what this had was a two-second impact over slow 3G
in how quickly we were able to fetch and render those images.
So a nice, positive experience.
We're hoping to bring this feature
to Canary in a few weeks, so keep an eye out for that.
The next thing I want to talk about is typography.
So typography is fundamental to good design.
And if you're using web fonts, you ideally
don't want to block rendering of your text,
and you definitely don't want to show invisible text.
We highlight this in my Lighthouse
now with the avoid invisible text while web fonts are
If you load your web fonts using a font face block,
you're letting the browser decide
what to do if it takes a long time for that web font
Some browsers will wait anywhere up to three seconds for this
before falling back to a system font,
and they'll eventually swap it out to the font
once it's downloaded.
We're trying to avoid this invisible text.
So in this case, we wouldn't have
been able to see this week's classic doodles
if the web font had taken too long.
Thankfully with a new feature called font-display,
you actually get a lot more control over this process.
Font-display helps you decide how web fonts will
render or fall back based on how long it takes for them to swap.
Now in this case, we're using font-display swap.
Swap gives the font face a 0-second block period,
and an infinite swap period.
This means the browser is going to draw your text pretty
immediately with the fallback font
if the font takes a while to load,
but it's going to swap it once the font face is available.
In the case of our app, why this was so great
is that it allowed us to display some meaningful text very early
on, and transition over to the web font once it was ready.
So in general, if you happen to be using web fonts,
as a large percentage of the web does,
have a good web font loading strategy in place.
There are a lot of web platform features
you can use to optimize your loading experience for fonts.
And also check out Zach Leatherman's web font recipes
repo, because it's really great.
Next up is Ewa to talk about render-blocking scripts.
EWA GASPEROWICZ: So displaying visible text
as early as possible is really important,
but we can go farther than that.
There are other parts of our application
that we could push earlier in the download chain
to provide at least some basic user experience a little bit
Here on the Lighthouse timeline strip,
you can see that during the first few seconds when other
resources are loading, the user cannot really see any content.
Downloading and processing external style sheets
is blocking our rendering process
from making any progress.
So what can we do about it?
Well, we can try to optimize our critical rendering
path by delivering some of the styles a bit earlier.
If we extract the styles that are
responsible for this initial render,
and inline them in our HTML, the browser
is able to render them straight away
without waiting for the external style sheets to arrive.
In our case we used an MPM module
called Critical to inline our critical content in index HTML
during our build step.
While this module did most of the heavy lifting for us,
it was still a little bit tricky to get this working smoothly
across different routes.
The truth is, if you're not careful,
or your site structure is really complex,
it might be really difficult to introduce this type of pattern
if you did not plan for actual architecture
from the beginning.
This is why it's so important to take performance considerations
If you don't design for performance from the start,
there is high chance you'll run into issues doing it later.
In the end, our risk paid off.
We managed to make it working, and the app
started delivering content much earlier,
improving our first meaningful paint time significantly.
So to sum up, to unblock the rendering process,
consider inlining critical styles
in the head of your document, and preloading
or loading asynchronously the rest later on.
For non-critical script, consider marking them
with a defer attribute, or lazy load them
later during the app lifecycle.
So that's the whole story of how we drew the horse
and tried to put it in a suitcase.
So let's take a look at the outcome.
This is how our app loaded on a median mobile device
on a 3G network, before and after the optimization.
That's a pretty nice progress.
All of this progress was fueled by us continuously
checking and following the Lighthouse report.
All of the hints you've seen today
are linked in Lighthouse tool so that you
can check them in the context of your own application.
If you would like to check out how we technically implemented
all of the changes, feel free to take a look at our report,
especially at the [? PRs ?] that landed there.
The performance score went up from 23 to 91.
That's pretty nice, right?
However, our goal was never to make Lighthouse happy.
We wanted to make the user happy.
And high-level metrics included in the report
like time-to-interactive or perceptual speed index
are a good proxy for improved user experience.
Also the snapshots timeline gives you
a nice visual feedback about how much shorter the waiting
time for our app became.
This is the full story of little Oodle Lab.
Now let's take a look at some much bigger businesses.
ADDY OSMANI: So Ewa and I had the benefit
of being able to think about performance very early
on, but how does this apply at scale to much
Well, let's take a look at Nikkei.
Nikkei are Japan's largest media company.
They have a site that's got 450 million users that
are accessing it, and they spent a lot of time
optimizing their old mobile site and turning it into a new PWA.
The impact of performance optimizations was huge.
They were able to get 14 seconds faster on interactivity.
They saw a 230% increase in their organic traffic,
58% increase in conversion rates,
and their daily active users and page views also went up.
But what did they actually do to optimize their performance?
Well, if we take a look at the full list of things
that Nikkei did, they'll look a little bit familiar.
That's because many of these are things that we covered today.
They were able to shrink them down by 43%.
One big change they made was they actually
used Webpack to optimize the performance
of their third-party scripts in addition
to their first-party ones.
They were able to use link rel-prefetch
on their daily edition pages, improving next-page performance
loading by 75%.
In addition to this, they also took advantage of the tip
that Ewa was just walking through--
critical path CSS optimizations.
This is that optimization where you're
sending down 14 kilobytes for the content of your first round
If you're able to squeeze in enough of your critical styles
in there, you can actually prove first meaningful paint
by quite a lot.
So here they were able to shave off
a second on their first meaningful paint
by taking advantage of this optimization.
And finally, on Nikkei, they took
advantage of the PRPL pattern.
Now, PRPL is a pattern that was first discovered by the Polymer
team a few years ago, and it says for Push, Render,
Pre-Cache, and Lazy Load.
So what Nikkei are doing is that they're
pushing their critical resources using link
rel-preload and server push.
They're rendering their main article content quite quickly.
They're pre-caching their top stories using Service Worker.
So this gives them offline access
to read articles as well.
And they're lazy loading code.
So they're lazy loading code, but they're also
using skeleton screens to improve the perceived
performance of these pages.
So we talked about how performance can help businesses
to give users a better experience,
but on users, we have one more thing
that we think could help with the future.
EWA GASPEROWICZ: Well, as you've heard during the keynote,
we believe that machine learning represents
an exciting opportunity for future in many areas.
So what if we could take these two
worlds of machine learning and web performance
and blend them together?
Maybe it could lead us to some really, really interesting
Today we want to tell you about our experience with machine
learning, and explain why we think it has a large potential.
Here's an idea that we hope will spark more experimentation
in the future--
that real data can really guide the user experiences
Today we make a lot of arbitrary decisions
about what the user might want or need,
and therefore, what is being prefetched, or preloaded,
If we get it right, we are able to prioritize
a small amount of resources, but it's really hard
to scale it to the whole website.
At the same time, we have a wealth
of data about typical user behavior readily available.
So Addy, how can we use that data?
ADDY OSMANI: So we actually have data available to better
inform optimization today.
Using the Google Analytics reporting API,
we can take a look at the next top page
and exit percentages for any URL on our site.
In fact, we have a little tool for this anybody can check out.
Here it is for developers.google.com/web.
And as we can see here, a lot of the users
that land on the Chrome DevTools documentation
actually end up going over to Lighthouse.
So we could potentially prefetch that page.
Folks that land up on the PWA docs,
we usually check out the PWS checklist.
And we could use this data to improve our page load
This gives us the notion of data-driven loading
for improving the performance of websites.
But there is one piece missing here.
Having a good probability model is important
because we don't want to waste our user's data by aggressively
We can take advantage of that Google Analytics data and use
machine learning and models like Markov chains
or neural networks in order to implement such probability
This is a lot less subjective and error-prone than manually
deciding what we should be prefetching or preloading
for our sites.
We can then wire this all up using link rel-prefetch inside
of our sites so that as the user browses through the site,
they're able to actually fetch and cache things
that they need ahead of time, improving our page load
Now we can actually go further than this.
Earlier, we talked about code-splitting and lazy
But in a single-page app, we're often
dealing with routes and chunks in a bundler.
So instead of just prefetching pages,
we could actually go more granular.
What if we could prefetch individual resources
for a page or number of pages?
Well, across all of these ideas, we've
been focused on trying to make some
of these a little bit more low-friction for web developers
And so today we're happy to announce a new initiative we're
Guess.js is a project focused on data-driven user
experiences for the web.
We hope that it's going to inspire escalation
of using data to improve performance and go beyond that.
It's all open source, and available on GitHub today.
This was built in collaboration with the open source community
by [? Miko ?] [? Geshav, ?] Kyle Matthews from Gatsby,
Katie [? Empenius, ?] and a number of others.
Let's take a look at what Guess.js provides us out
of the box.
The first thing it provides us is a Google Analytics module
for analyzing our user navigations.
It has a parser for popular frameworks,
allowing us to map those URLs back to your frameworks router.
We then have a comprehensive Webpack plug-in
that's able to do machine learning training
on some of that data, determine all of our probabilities,
and chunks with the potential for future clustering
of those chunks, and we then are able to wire it all together
as the user navigates through the site.
We've also got experimental support
for applying these concepts to static sites.
Now that's enough talk.
What about showing you how this works in practice?
So here we have a demo of Guess.js in action.
What we're first going to do is we're going to load up our app.
We're in DevTools.
We're in low-end mobile emulation over slow 3G.
This app's rendering very quickly,
but what we can see in the network panel
is a number of routes that have already
started to be prefetched because we have high confidence they're
going to be used.
We can toggle on a visualization of this.
So in pink, we have pages we have
high confidence the user is going to want,
in green, we have low confidence,
and in yellow, we have mild confidence.
We can actually start to navigate
through this application.
So let's say we go to cheese.
We can visualize this again.
We can see that cheesecake is a page the users will often
As you can see, it loaded instantly
because it's already in the user's cache
by the time they go to it.
Contextually we're able to display to you in this demo
all the visualizations of confidence levels
we have for different pages as we navigate through the site.
Even this last page, the custard,
loaded really, really quickly using these techniques.
Now prefetching is a great idea in practice,
but we wanted to be mindful of the users' data plans.
And this is where we use navigator.connec
tion.effectivetype to make sure that we're only
prefetching things when we think that your connection can handle
So this is our demo of Guess.js using Gatsby.
By the way, this also happens to be
a PWA with a great performance score,
so thank you to both [? Miko ?] and Kyle for helping with this.
Check out Guess.js, let us know what you think.
So today we talked about quite a few things.
But at the end of the day, performance
is about inclusivity.
It's about people.
It's about all of us.
We've all experienced slow page loads on the go,
but we've gotten an opportunity today
to consider trying to give our users more
delightful experiences that load really, really quickly.
So we hope that you took something away from this talk.
Remember that improving performance is a journey.
Lots of changes can lead to really big gains.
So check out some of the things we talked about today.
Talk to us in the web sandbox if you've got any questions.
That's it from us.
[TITLE MUSIC PLAYING]