Optimizations and performances
Aka webperf, web perf
respecting the medium
Loading, parsing, rendering, etc.
We don't want humans waiting on computers. We want computers waiting on humans.
— Gregory Szorc
Monitoring is your bank telling you you're overdrawn.
Observability is the ability to tell you're running out of money because you're spending too much money on chocolates, cakes and sweets because you've recorded data on what you spent your money on throughout the month.
Example of audits on famous website, applications Performance audits for the web
Progressive JPEGs decode slower than Baseline ones. [..] decoding a progressive JPEG takes about 3.3× as long as a baseline one. (I would still absolutely recommend using progressive, because they feel a lot faster than their baseline counterparts.)
— Base64 Encoding & Performance, Part 2: Gathering Data – CSS Wizardry – Web Performance Optimisation
See also Images, Content Security Policy
Small DOMs, no CSS descendant selectors (offsetTop, etc. trigger layout)
Take account traffic, processing time, etc.
Tools (audit, checklist, benchmark, best practices, etc.):
Tools collection Quelques outils en ligne pour analyser votre site | Darklg Blog
Control your content
Organization
Impacts
To mesure impacts (KPI: bounce rate, conversion rate): use A/B testing
Mobile
https://www.google.com/webmasters/tools/mobile-friendly/
https://www.google.com/webmasters/tools/mobile-usability
https://www.bing.com/webmaster/tools/mobile-friendliness
Perceived performance
Aka feedback first, skeleton UI
Reduce bytes
http://www.haratetsuo.com/wp-content/themes/haratetsuo2018_cms_v2/images/ico/arrow.svg The 30MB SVG image is a simple arrow! It's served uncompressed (gzipped would be 24MB and brotli would be 3.8MB). It contains 82 base64 encoded JPG images (one per image size). There are only 10 unique base64 images encoded in the file, so a lot of repetition...
— Paul Calvano on Twitter: "Exactly! The 30MB SVG image is a simple arrow!… https://t.co/ci2hz7WpLI"
The fastest byte is a byte not sent.
— Ilya Grigorik http://chimera.labs.oreilly.com/books/1230000000545
Reduce page weight can give a (wrong) feedback about the page load time increase:
When I was at Google, someone told me a story about a time that “they” completed a big optimization push only to find that measured page load times increased. When they dug into the data, they found that the reason load times had increased was that they got a lot more traffic from Africa after doing the optimizations. The team’s product went from being unusable for people with slow connections to usable, which caused so many users with slow connections to start using the product that load times actually increased.
— Dan Luu in Most of the web really sucks if you have a slow connection
Blocking deliberately or unintentionally device or connection capacities (browser version) will give wrong feedback about "users don't use old browser" (because they can't) or "users don't have slow connection" (because page can't load in decent time) See also Page Weight Matters.
Always reduce before encrypt (aka always compress before encode), not the inverse:
minification:
optimize/reduce without require reverse operation
remove unessary data (duplicates, unused tags, selectors)
merge/round similar data, reduce precision (
0.2345
->.23
)http://cssstats.com/
https://isellsoap.github.io/specificity-visualizer/
compact/compress (require processing to recover operable state): pre-gzip. See Precompress and Content encoding`
use different compression algorithm, or a better tool/algorithm implementation (like zopfli for deflate)
Guetzli can be use in conjunction of tools like ImageOptim
brotli (vs gzip)
Note some framework read the
zlib.output_compression
value to define the headerContent-Encoding
to gzip (setzlib.output_compression = Off
inphp.ini
)Example: compress CSS with optimized dictionary based alogrithms (like gzip use huffman tables): blocks, selectors, media queries, properties, values. See [Compression & Minification](CSS#Compression & Minification)
Optimising GIFs for the Web - use
gifsicle -O3 --lossy=80 -o compressed.gif original.gif
split commbined HTTP headers values: to use HPACK (HTTP/2.0) Instead of:
Use:
use the right format (if supported, or a fallback):
as binary representation of CSS (really usefull vs GZ?)use image as data container (as colors, but could be lossy)
transparent video: WebM or side by side channels (RGB + A as RGB) videos. See Alpha
transparent image: instead of PNG use WebP (or JPEG-XR) or combined formats. See [Alpha compression](Image#Alpha compression)
animated image: use APNG or Animated WebP or looped video instead of GIF
See [Animated image](Image#Animated image). See also Image sequence
Use a video with only 1 frame as image (ex: WebP), could done with H.264/H.265, see [Use video codec](Image#Use video codec)
Use code/lib to animate elements instead of video or sprites: https://github.com/bodymovin/bodymovin
VP9 (8bit) good on small screens (but not on big)
Don’t use JPEG-XR on the Web (JPEG-XRs are decoded on "the software-side on the CPU" by Internet Explorer and Microsoft Edge, the only browsers that support it)
HTML: remove comments, spaces, not required closing tags, attibutes, chars like
"
, entities (when not required), etc.CSS: remove comments, prefixed properties, useless selectors, regroup using shorthand properties (like
background
for:background-image
, etc.), etc.SVG:
JavaScript: optimize by remove dead code (treeshaking), uglify (rename variables for shorter name, inline function, resolve static expressions), etc.
Images:
image compression options (if applicable, cf. formats)
[Image - Compression and optimisation](Image#Compression and optimisation) and [Image - Mixed formats](Image#Mixed formats)
[Video - Compression and optimisation](Video#Compression and optimisation)
Compression by downscaling technique
Use other (experimental) formats like BPG or FLIF
To investigate: store vector using SWF, decompress on the fly (to SVG) with ServiceWorker; decompress biJPEG to PNG or webp with OfflineCanvas
use the right dimensions (ex.: thumbnails)
3D models: drops leading zeroes (
-0.5
->-.5
) and changes the scale of the modelhttps://jsfiddle.net/w43q2mac/ - optimize OBJ file
https://compress-or-die.com/public/javascript/OBMLoader.min.js - OBJ compression to the smallest file size possible – Compress-Or-Die
fonts:
use WOFF especially WOFF2 (use a Brotli instead of Deflate, a gain of ~26%). For WOFF (v1) optimize compression with Zopfli
create WOFF files from OpenType/CFF font (instead of TrueType) (OpenType/CFF use cubic Bézier vs quadratic Bézier for TrueType)
use only subset. use
pyftsubset
from fonttools (a library to manipulate font files from Python). Be carefull when set this parameter, with content changes or languages could invalidate it.reduce the
unitsPerEm
to 256 (usally 1000 or 2048). This use a byte (8 bits) instead of a short (16 bits) to store coordinates. Reduce precision, but for regular text, the difference is invisible.
other formats:
Optimize tarball by sorting by file extension and dir.
find ./source \! -type d | rev | sort | rev | tar cjf archive.tar.bz2 --files-from=-
That group similar files together, allow compression from redundancy across files similar files. (due to limited window size to 32K)remove optional filename and timestamp from gzipped stream (option
-n
):gzip -9 -n -k file
rearrange data:
Server-Timing Compression - vaz.ac - store/send
Server-Timing: cdn-cache; desc=MISS, edge; dur=23, origin; dur=129
as0,23:1,129:2
Compressing ResourceTiming | NicJ.net - store/send
{"responseEnd":2436.426999978721,"responseStart":2435.966999968514,"reque...
as370,1z,1c
. See nicjansma/resourcetiming-compression.js: ResourceTiming compression and decompression
Reduce requests
embed images in CSS using data URI, encoded with URI encoding or base64 (could have a negative impact on low end devices)
JS, CSS in HTML in tags
<script>
and<style>
CSS/HTML in JavaScript variables
HTML prerendered (HTML + JS + data)
bundle JS and/or CSS, image spriting https://github.com/clyfish/zerosprites http://yostudios.github.io/Spritemapper/
add the right header(s) for server cache strategy (see Cache)
Note: HTTP/2 multiplex and push are not always a good alternative, because compression is more efficient on larger chunk data. Musings on HTTP/2 and Bundling | CSS-Tricks
The Best Request Is No Request, Revisited · An A List Apart Article - Unbundle resources with HTTP/2.0 but be carefull
Reduce latency
Mesure latency:
Can you get a reply from a HTTPS site using the Ping command? - Super User - tcping (ping TCP ACK)
Use CDN
Use a popular TLD do use DNS resolver cache:
First packet size
Initial TCP window
Minimize ATF (above-the-fold) content size: The first TCP connection isn’t able to fully utilize a connection’s bandwidth on the first roundtrip, which means the number of packets it can send is very limited. In order to render your ATF content it needs to be 148 kb or less — 5 SEO Guidelines for Web Developers
Congestion window: initial cwnd size is 10; 14.6KB = 10 packets of 1460 Bytes.
TODO: where "148 kb" come from?
Fast Open
Keep-Alive
Use Keep-Alive connection (more useful for HTTP/1.X connection than HTTP/2):
IPv6
Edge side includes
CDN or cache proxy agregade differents resource fragments with cache
Dedicated domains cookies less
Reduce latency server side.
For statics resources (don't require cookies), use a dedicated domain.
With HTTP/2.0 it's no more useful, with header compression.
Multiple domains for static resources
Aka domain sharding, domain sharing
Allow multiple parallels requests.
Ex: static1.example.com
and static2.example.com
Not adviced if you use HTTP/2.0 or HTTPS
Serve progressive HTML document
Serve the document using chunk encoding.
Note: browser often have a buffer of 4096 bytes
Send the HTTP headers
the HTML head (title, metas, scripts, styles, etc.)
first part of the HTML body
(*n) send chunks with
<p class="progress">Progress: XX%...</p>
to show progressionsend chunk
<p class="progress">Complete</p>
send chunk
<style>.progress{display: none}</style>
send the remains HTML document part
Precompress
See Content encoding
For all text formats: CSS, SVG, JavaScript
Could also be used for generic data (other formats that don't already use compression), or if the compression mode in those format can be disabled (ex: uncompressed PNG + Content encoding)
Create a gzip without store the original file name
gzip -9 -k -n file.ext
create a TAR of all .ext
COPYFILE_DISABLE=1 tar -cvf file.tar *.ext
Content-Encoding: br
Brotlia CDN
mod_mem_cache
ormod_disk_cache
: webdirect.no Apache caching with gzip enabled | webdirect.noa proxy cache like Varnish
Server precompressed files with nginx
With nginx ngx_http_gzip_static_module
and ngx_brotli
(there is no ngx_http_brotli_static_module
)
Server precompressed files with Apache
Multiplexed / Pipelining
HTTP/1.1 has pipelining, but not well supported (by proxies, etc.) HTTP/2 is multiplexed
TLS
avoiding full TLS handshakes
TLS resumption
reduce the certificate chain
enable the correct cipher, following Mozilla's guidelines.
verify that web server is running on a system with a CPU that support correct instructions
Use dedicated servers
Load balancer, localized CDN, etc.
Cache
To control static resource version, use checksum instead of build number. Which means you only download a new copy when it actually changes (see ETag).
Use forever cache (cache immutable) for static resources.
https://redbot.org/?uri=https://example.com
https://restpatterns.mindtouch.us/Articles/Caching_Matters
Cached
max-age or expires headers, Header append Cache-Control "public"
, Header append Cache-Control "immutable"
avoid check of 304s
.htaccess
:
PHP:
Varnish script to Handle Vary: User-Agent
to create classes to reduce variations.
https://github.com/varnishcache/varnish-devicedetect
Cacheable chunks
Split resource in cacheable subresources
Not cached
Prevent back button to show cache page (ex.: after logout)
Note: Cache-Control: no-cache
is for HTTP/1.1 where Pragma: no-cache
is for HTTP/1.0. See http - Difference between Pragma and Cache-control headers? - Stack Overflow
Cache partitioning
Reduce processing
Time spent in JS parse & eval for average JS - Google Sheets - Parse times for a 1MB bundle of JavaScript across desktop & mobile devices of differing classes
Too Hot To Handle - Optimizing for Low Powered Devices // Speaker Deck - JS framework are too heavy for low-end devices
Ember.js - Glimmer.js Progress Report - Ember.js use binary templates for faster parse
Relayout, repaint, reflow
Reduce CPU/GPU usage
prefer server-side rendering for primary content
lazy load third party resources with facades
deliver ES6 modules to up-to-date browsers
Ex., code markup:
client side: Serve HTML + CSS + JS + Execute JS
server side: Serve HTML (gzipped, a tiny tiny bit more) + CSS
Jake Archibald on Twitter: "I took the code examples from https://t.co/Oyiax6163l and compared with highlighting markup, and without (https://t.co/oIYkvupr2D). After gzip: With highlighting markup: 900b. Without highlighting markup: 723b. PrismJS: 5.2k.… https://t.co/0xcp53jdrB" - About syntax highlighting (in a blog post, example, etc.)
Other:
Don’t attach tooltips to document.body - Atif Afzal - to invalidated a smaller subtree
Control loading
Load based on the device or network capabilities:
adaptive bitrate streaming (see MPEG-DASH and similar solutions)
responsive images (
srcset
attribute and@media
CSS rules)
78KB for first data (total, can be compressed) to ensure an optimal experience on LTE, for under 200ms delivery (determined empirically, 75% of handset will load in less than or equal to 200ms): MobilePerf Insights: Why LTE Has Slowed by 50% in the US This Year - Twin Prime
Full raw data vs. data + computations (ex: BMP vs PNG)
Use cache
use
font-display
property for fontsuse fallback font
download only diff of images (like videos using relative compression + keyframes): render in canvas
progressive data (JPEG, Video). It's even better with HTTP2 multiplexing
adaptative byterate with DASH https://gist.github.com/ddennedy/16b7d0c15843829b4dc4
"preload" metadata like size, duration, dominant color / thumbnail, LOD, deepth, etc.
The simple summary is
preload: when you use on the same page
prefetch: for future use (next page)
<img>
vs background-image
<img>
vs background-image
Buffering
Aka dynamic buffering
Dynamic Buffering revamping | Video Encoding & Streaming Technologies and A Dynamic Buffering strategy | Video Encoding & Streaming Technologies - prebuffer time (used to compute real required buffer size) + buffer to fill 80% at the current speed
Resource hint
See also dns-prefetch, preconnect, prerender
See Server Push
Preload: load this resource (high priority to low priority based on the value of as
attribute) Prefetch: load this resource after all other resources (very low priority), will prefetched the resource when the browser is idle for future navigation
Preload will be started immediately and prefetch will be started only after the main page finishes loading.
Preload should be rarely used and only used for small media files (< 5 MB). It's often used as band-aid for an underlying issue. See The cost of preload
Don't use both preload and prefetch in same time: <link rel="preload prefetch" as="style" href="style.css">
because they don't have the same purpose. This create potentially 2 requests.
Using JavaScript, but prefer the link / header version:
Use <link rel="preload" href="styles.css" as="style">
, work for fetch
, audio
, font
, image
, script
, style
, track
, video
and image
. See Fetch Standard
Use type parameter to specify the format of the resource. It's usefull to let the browser choose the supported format. But should be carefull defined (a browser support all available choices will preload all). In case you want to preload a font available in both WOFF and WOFF2, reclare only the last one (font/woff2
), because browsers support preload also support this more recent format.
Note about mandatory as
attribute and valid value:
Reactive prefetch: Google mobile search is getting faster - to be exact, 100-150 milliseconds fa...
instant.page - Prefetch pages under pointer before the user click (~300ms with cursor, ~90ms with touch) Technical details — instant.page instantpage/instant.page: Make your site’s pages instant in 1 minute and improve your conversion rate by 1%
Preload ASAP
As soon as possible, use UDP Priming (pre request like HEAD) to let the server prepare response
Load list by fragments 1+1+X (better than X and 3+7+X) like streaming
Download priority
HTML (highest)
CSS (highest)
images (low or medium)
XHR/Fetch (high)
fonts (highest)
scripts: low (async, defer or type module), medium (
<script src="script.js"></script>
) or high (<script src="script.js"></script>
before an image)
Note: this is how Chrome prioritize resources
Browsers can’t possibly download background-images until they’ve built the CSSOM.
Browsers shouldn’t base—thus delay—the downloading of
<img>
s on CSSOM completion.Browsers will download
<img>
completely independently of CSSOM construction. Blocking<img>
on CSSOM construction seems very inefficient, and would lead to delays in downloading content.Accordingly, browsers will download
<img>
that end up being hidden from view, i.e. display: none;. If the browser starts downloading<img>
before CSSOM construction (as I expect) then it has no way of knowing yet whether that image is needed or not.
On demand
Aka Lazyload, LOD, defered loading
Use <noscript>
tags, or handle it with Service Worker (replace all <img>
by a placeholder). See <noscript>
and search engines
Live streaming, or start play video when the file is not completely generated:
Image lazyload
Use loading="lazy"
attribute for images (and iframes)
Use a placeholder element, or at least use a SVG in data URI for the src
attribute to prevent reflow
placeholder shouldn't be visible if script is not available (inlined CSS hide the placeholder)
placeholder should have
role="img"
andaria-label
set with imagealt
(will be set by the JS)should works with complex image content (eg.
<figure>
)
TODO: support all replaced elements: video, audio and iframe. Use a weakmap or additional property to store the replacement fragment (parse noscript HTML only one time)
After a user action or intersection observation (scroll → visible in the viewport), replace the placeholder by the final HTML:
Read document visibility
document.visibilityState === "visible" || document.visibilityState === "prerender"
Listen document visibility change
document.addEventListener("visibilitychange", documentVisibilityChange);
if hidden, store which placeholder(s) must be replaced then wait the document to be visibleUse the IntersectionObserver API of observe placeholders (currently) in the DOM
Use MutationObserver to handle DOM modifications (to observe later attached placeholders or disconnect detached placeholders). See DOM mutation
Use matchMedia API to match print media (to load all images)
Replace placeholders:
placeholder.replaceWith(document.createRange().createContextualFragment(placeholder.dataset.srcdoc));
If the IntersectionObserver API is not supported:
use the polyfill with caution, it's impact performance (use
getClientBoundingRect()
andgetComputedStyle
) and it's add weight to load. Additional the existing polyfill are not spec compilant, often don't handle visibility, parent non visible overflow or crop, CSS changes, etc.or fallback to scroll listener,
setInterval()
with a not too small delay andgetClientBoundingRect()
or load all lazyloaded images, it's the default behavior before lazyload has been implemented
See also:
Progressive load
Or partial load
load a thumbnail, then load the full image: Faster Image Loading With Embedded Image Previews — Smashing Magazine
How Medium does progressive image loading – José M. Pérez - See also How to use SVG as a Placeholder, and Other Image Loading Techniques and Vectorization for previews of images (Low-Quality Image Placeholder)
Use same DCT coefficients, JPEGs with elided quant tables to reduce bytes loaded (recreate JPEG headers) Plus use blur and scale to display the thumb The technology behind preview photos - Engineering at Meta
progressive JPEG and interlaced PNG: Introduction to PNG - nuwen.net and Progressive Image Rendering. But can make file larger: png - When to interlace an image? - Stack Overflow
Benefit of HTTP2 multiplexing and progressive image to speed up first paint sooner
See also JPEG - Progressive (scan config and number of it can be tweak)
For images (works better with progressive images), in Edge Workers (Service Workers for CDN):
receive the client request (for document, images, etc.)
send first 521B/1kB (headers of image / metadata - size) for the browser to do the layout as soon as possible
wait 20ms to let the client to process CSS, JS and other critical resources
send first 15% of the resource (~15% = contains the progressive bytes of the image)
wait for other resources (for the same client/requested document/referrer) to let the browser render first layers, before it recieve the rest of the resources
send the rest
An other solution:
See also:
But this still blocking DOM parser on few browsers (IE11, Firefox 36). See https://github.com/scottjehl/css-inapplicable-load#the-bad Can be use to load fonts (inlined in CSS). Or use preload font
See <noscript>
and search engines
Critical path
Aka blocking dependencies, critical resources
To find blocking points / bottlenecks / critical rendering path
A critical request is one that contains an asset that is essential to the content within the users viewport
Example: the hero image
Optimise for the critical rendering path, get everything at the top of the page in view as fast as possible. Then lazy load the rest.
HTML parser / main scanner:
build the DOM
blocked by the network
blocked by synchronous scripts (can alter the HTML stream), (wait for script to download and parsed, not always the case with speculative parsing), executed
blocked by async JavaScript execution
recent engine preload resources even if the parser is paused
preload scanner
if speculative parsing is not used, it's the same HTML parser
discover sub resources (CSS, JS, images)
blocked by the network
doesn't always support all kind of resources Firefox and IE/Edge
often operate when the main/HTML parser/scanner is blocked
CSS and Network Performance – CSS Wizardry – CSS Architecture, Web Performance Optimisation, and more, by Harry Roberts Note all CSS will be loaded, even if that doesn't match the current media, see Why Browsers Download Stylesheets With Non-Matching Media Queries
JavaScript
load start by preload scanner
blocked by the network
if async or synchronous, ran after downloaded
if defer, ran in order after HTML parser complete
if synchronous, ran only after CSSOM of previous (synchronous) stylesheets
https://2r4s9p1yi1fa2jd7j43zph8r-wpengine.netdna-ssl.com/files/2017/09/blocking-bold@2x-1.png
tips: move the script before blocking stylesheet if no need of CSSOM (often the case)
readystatechange event (readyState = "interactive")
readystatechange event (readyState = "complete")
DOM Content Loaded event
fired after defer scripts are executed if any, else after HTML parser complete
fired after the transition to "interactive" but before the transition to "complete" of document readyState
DOM Content Loaded event execution
fired after executing all listener of DOMContentLoaded event
load event / fully loaded
after DOMContentLoaded event execution
fired when the document has finished loading
background image
load start only if an element in the page use it (based on stylesheet)
blocked by the network
stylesheet
building CSSOM
load start by preload scanner
laoded with low priorty if the media dosen't match
if in import rule, load start only after the parent stylesheet is loaded or by preload scanner if the parent stylesheet is inlined in the document
blocked by the network
blocked by import: CSSOM is complete after all sub stylesheets are loaded and parsed
image
load start by preload scanner
blocked by the network
DNS prefetch
start by preload scanner
preconnect
start by preload scanner
prefetch
start by preload scanner
preload
start by preload scanner
prerender
start by preload scanner
start Render - the time from the start of the initial navigation until the first non-white content is painted
blocked ("rendering of subsequent content" / for progressive rendering) by sync stylesheets that match current media
font
load start only if an element in the page use it
text render
if font face for custom font is declared, use it, else use fallback
if
font-display: auto
, wait for font load 3s maximum, then render the text with use the fallback if the custom font is not loadedin all major browsers, this means the browser will wait 3 seconds for the custom font to download Note: in Microsoft Edge, the font-display: auto behavior is different. With it, if the custom font is not cached, Edge immediately renders the text in the fallback font and substitutes the custom font later when it’s loaded. This is not a bug because font-display: auto lets browsers define the loading strategy.
if
font-display: fallback
, the text is rendered with custom font if is cached, else use the fallback font. The text is rerendered when the custom font is loadedif
font-display: optional
, the text is rendered with custom font if is cached, else use the fallback font.use fallback font (default serif)
Time To First Byte (TTFB)
First Meaningful Paint (TTFMP / FMP) - The paint after which the biggest above-the-fold layout change has happened, and web fonts have loaded
when some contentful thing (text, image, canvas, or SVG) is painted for the first time
blocked by loading fonts (only for font that contains more than 200 characters, until the font is displayed with loaded font or with fallback font if 3 seconds timeout exceeded, based on
font-display
?)
Time to First Paint (TTFP / FP) - The time when the browser first rendered after navigation. This excludes the default background paint, but includes non-default background paint.
when the browser first rendered after navigation. This excludes the default background paint, but includes non-default background paint and the enclosing box of an iframe.
Heuristic/guess are used when "meaningful" was (the paint after the largest layout change). See also LCP.
Time to First Contentful Paint (TTFCP / FCP) - When the browser first rendered any text, image (including background images), non-white canvas or SVG. Text and graphics start to render (but often catches non-leaningful paints, e.g. headers, nav bars)
when the browser first rendered any text, image (including background images), non-white canvas or SVG. This excludes any content of iframes, but includes text with pending webfonts "Styles are loaded and browser can paint content"
Largest Contentful Paint (LCP) - when the main content of a web page has loaded
First Input Delay (FID) First Click (Click Interaction Time), First scroll (Scroll Interaction Time), First Key (Key Interacton Time)
based on users interaction, based on RUM
FCI (First CPU Idle) / TFI (Time to First Interactive) - Page is minimally interactive, most visible UI elements are interactive, repsonds to user input reasonably quickly
Time to interactive (TTI) / Time to Consistently Interactive (TCI) - when the page is first expected to be usable and will respond to input quickly It is the first span of 5 seconds where the browser main thread is never blocked for more than 50ms after First Contentful Paint with no more than 2 in-flight requests Displays useful content, event handlers are registered for most visible elements, page responds to user interaction within 50ms
fired when page's resources are loaded (load event) and the main thread is idle (for at least 5 seconds)
Speed Index - the average time at which visible parts of the page are displayed. It is expressed in milliseconds
Visual Complete - first time when the visual progress reaches 100%
Composite metric examples (based on what the user care about):
Pinterest use Pinner Wait Time (PWT)
"For us, that’s images. Until the above-the-fold images are loaded, to our users, page load is not complete."
"Time to Interactive" and "Above-the-Fold Images"
Twitter use Time to First Tweet (TFT)
Hero Rendering Times (HRT) - Combination of when the largest IMG, H1, and background image in the viewport are rendered Don't work for image carousels and popups
max(h1, (biggest_img || bg_img))
Time To Interactive (TTI)
largest background image rendered
largest image render
H1 render
Second Meaningful Content (SMC) for page with A/B tests
Which metric is best?
deliver any content: Start render
deliver significant amount of content: speed index, FMP
deliver critical content: Hearo Rendering Times
Web Performance Calendar » Developing the Largest Contentful Paint Metric - What makes a good metric? (tl;dr: representative, accurate, stable, interpretable, simple, elastic, realtime, orthogonal)
Anatomy of a webpage
Aka wireframe of a webpage
Note: always render document server side. All the document content must be delivered. JavaScript (client side) can be used to enhance experience (loading, UI elements, etc.). See Progressive Enhancement
Have things (critical content) before 1000ms. See responsiveness
Anatomy/wireframe of a webpage:
See also Critical rendering path and Blocking resources
Use HTTP2:
Performance Optimization Strategy in 2023 – Dropbox Paper - "1. Clean up and reorder the "
Get Your Head Straight - Speaker Deck - Smashing Magazine on Twitter: "🔖 For the bookmarks: Optimum <head> order for performance: <meta /> <title> preconnect <script async></script> CSS with @imports sync JS sync CSS preload <script defer></script> prefetch / prerender everything else (SEO, icons, open graph) via @csswizardry #webexpo #webperf https://t.co/Xar1kAjCY5" / Twitter
https://github.com/bocoup/critical-css-boilerplate
Use progress events for media (img, video and audio) element. See Image dispatch progress events
Some link tag are "body-ok" (aka allowed in the body, not only in the head)
Blocking resources
Browser requests the HTML document Begins parsing and constructing DOM Discovers CSS/JS Waits for CSS response Constructs CSSOM Combines CSSOM and DOM intro render tree Font requests are dispatched after the render tree indicates which font variants are needed to render the specified text on the page.
— Slide 16 of To push, or not to push?! by Patrick Hamann
Blocking parser and blocking rendering
A friendly reminder that any
<link rel="stylesheet">
in your<head>
will block first paint until all of them are done downloading.
Stylesheet are blocking resources.
Synchronous scripts in body block rendering
Defer and async script
Prefer defer
over async
for regular scripts.
These scripts shouldn't contains window.write()
(blocking)
Defer: Use the script as non blocking script. Be executed when the HTML document is done being parsed (aka DOM Interactive or performance.timing.domInteractive
) Async: Use the script as non blocking script. Be executed as soon as possible, don't respect execution based on the order in document (out-of-order) At the bottom: Use the script as blocking script. Should be executed after all previous blocking resources being loaded and parsed. (before document load event). Prefer defer or async instead
Non-blocking stylesheet
Aka async stylesheet
Depreciated:
Reduce media memory usage
For images and videos
By drawing downscaled images when smaller image are required.
https://github.com/PixelsCommander/CanvasImage
Compressed textures
Use compressed textures and/or less bytes per channels (require canvas).
See [Texture format](Texture format)
http://wayback.archive.org/web/20141203033529/http://web5.projekti.info/webgl/examples/
Major compressed texture formats support (from WebGL Stats 02/2016):
WEBGL_compressed_texture_s3tc
84.2%WEBGL_compressed_texture_etc1
13.1%WEBGL_compressed_texture_pvrtc
7.4%WEBGL_compressed_texture_atc
2.4%WEBGL_compressed_texture_astc
(Mali GPU, ex.: Galaxy S6)
Watch texture max size gl.getParameter(gl.MAX_TEXTURE_SIZE)
. Prefer use 4096 × 4096
Error: WebGL: compressedTexImage2D: the maximum texture size for level 0 is 4096
WebGL DDS + texture atlas 4096 require bufferData
/texImage2D
+ check an OUT_OF_MEMORY
errors
RGBA4444 = 16bit but not related to paletted, use context.texImage2D(context.TEXTURE_2D, 0, context.RGBA, context.RGBA, context.UNSIGNED_SHORT_4_4_4_4, data);
where data
must be an Uint16Array
(4bits for each colors, something like Uint4ClampedArray
) with each channel must be clamped to 0xF
(not directly compatible with a PNG16 image)
Note: In WebGL 2, PALETTE8_RGBA8_OES
can be used with compressedTexImage2D()
to upload paletted colors (like from PNG8): https://www.opengl.org/registry/specs/OES/OES_compressed_paletted_texture.txt
Performance reporting
Aka client timing
See JavaScript Performance API (ex: window.performance.getEntriesByType("resource")
) and navigator.sendBeacon()
AMP
CSS is limited to 50KB and only inline, custom JS is not allowed (other than via an iframe method), and all images are guaranteed to be lazy loading. [...] Technically correct AMP pages will perform very similar to any non-horrible web page. [...] Part of that answer you can probably guess: the cache is simply very fast. It’s hard to compete with a Google-class CDN. I imagine thousands of servers strategically placed worldwide on the best connections available. [...] on Google Search on mobile [...] Pretty much anything that page needs to render is preloaded, whether you actually open it not. [...]
The AMP page, which we all believe to be super fast and optimized for slow mobiles because it is AMP, isn’t that fast. Its true speed comes from preloading.
Cache:
Others links:
Third parties webperf
See also Third parties
Performance Calendar » Reducing Single Point of Failure using Service Workers - Block slow third-party with service worker
Improving third-party web performance at The Telegraph - "delete old third party scripts and see if anyone complains"
After GDPR, The New York Times cut off ad exchanges in Europe - and kept growing ad revenue - Digiday - "serving regular, un-targeted ads could actually increase revenue" (remove cookie syncing and retargeting, that generate large amount of requests)
Loading Third-Party JavaScript | Web Fundamentals | Google Developers - "Lazy-load Third Party Resources"
Last updated