-
Notifications
You must be signed in to change notification settings - Fork 9.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Audit: Serve <video>
instead of animated GIFs
#4696
Comments
+1 to @addyosmani for this idea. This is one area of performance optimization that can have a significant impact. This involves a bit more than simply checking for GIFs, as there are static and animated GIF images. This is the process I would attempt (attempt, mind you, I've never written a Lighthouse audit before):
I would probably also consider having a separate audit to check if videos |
Just going to write a possible solution for this problem. Feel free to change/improve the solution. Idea here is to fetch all rendered gif images (so use The gatherer gets all animated gifs and the audit just does the comparison. Not sure how long canvas and getImageData take to load so the gatherer might take some time. |
+1 on the overall audit, love the concept and huge potential savings to surface! ❤️ Quick note on implementation cost, if there's a hard requirement on examining the buffer to determine animation, it will likely need to be implemented on the Chrome side if we want it in the default run as we had to abandon the earlier approach to optimize images for this same reason (and those weren't even hopping over the protocol!). A suggestion for a workaround, we should be flagging GIFs as an inferior format no matter how they're used! :) so how about the audit flags all GIFs above a reasonable size, suggesting webm, and documenting that if they don't have animation, consider PNG/WebP/etc. The original reason we filter out GIFs from our next-gen formats audit at all is specifically because they might contain animation, so addressing this kills two birds with one stone 👍 |
@patrickhulce what reasonable size are we talking about? 1MB? Also would it be lots of work to introduce it in the protocol? If need be I could take a stab at it, just need someone to put me in the right direction but it might not be worth it. |
Oh much lower, I was talking about something more inline with our existing ignore thresholds (~4KB or so). Just to address the tracking pixel case @addyosmani highlighted.
Depends on how comfortable you are with C++ ;) The actual code shouldn't be that bad if all we're doing is determining if the GIF is animated. Honestly though given we should flag all GIFs regardless I personally don't think it's worth the effort when the user should easily be able to tell if they need to make it webm or png on their own. |
fine by me. I'm not comfortable at C++. I've played with it 5 years ago but you can't call it programming. I'm a pretty good script kiddy, copy and pasting + adapting. I just thought maybe I could implement the progressive jpg one at the same time but i'm all for leaving that for another day. As I think we can just start with flagging all GIFs |
Thanks for the context on having to fall back into C++ if we wanted to forge ahead with this path, @patrickhulce. That's very useful to know!.
Flagging GIFs as an inferior format (factoring in some minimal byte threshold) sounds pretty reasonable to me if we think the cost of doing something more advanced is not worth it. I have a few follow-up questions if we head down this path instead of detecting if a GIF is animated:
The one point I want to ensure we convey is that Also interested in whether @paulirish or @brendankenny have input on the direction here. |
Yes. I'm a fan of a dedicated audit for "Use better formats than animated GIF".
Nah, because it's about video typically. Plus this is actionable in different ways than the next-gen one. You can serve video to all browsers, no UA or request headers necessary.
|
<video>
instead of animated GIFs<video>
instead of animated GIFs
Did a bit of digging in HTTP archive stats around this and here's what I've found.
Already nearly all seem to be tracking pixels, of the remaining I downloaded a random sample of ~100 and did some analysis. No GIF <10kb had savings above our thresholds, so if we just focus on those...
In general the larger the GIF the greater percentage that can be saved, probably since compression overhead is dwarfed by compression savings i.e. ~2.4mb GIF with large dimensions few frames becomes ~220k webm but ~100k GIF with small dimensions many frames becomes ~50k webm. Relevant code snippetsSELECT url FROM [httparchive:requests.2018_02_01_desktop]
WHERE JSON_EXTRACT_SCALAR(payload, "$._contentType") = 'image/gif'
AND INTEGER(JSON_EXTRACT_SCALAR(payload, "$.response.bodySize")) > 4000
AND RAND() < 500/500000
LIMIT 1000 cat ./gifs-list-from-httparchive.csv | xargs -n 1 curl -O
brew install ffmpeg --with-libvpx
for f in *.gif; do ffmpeg -i "$f" -c:v libvpx -crf 12 -b:v 500K -auto-alt-ref 0 "$f.webm"; done
npm install file-type is-animated
node do-the-cool-analysis.js var fs = require('fs')
var fileType = require('file-type')
var isAnimated = require('is-animated')
let totalAnimated = 0
let totalNon = 0
let animatedSize = []
let nonSize = []
let savings = []
fs.readdirSync(__dirname).forEach(x => {
try {
const buffer = fs.readFileSync(x)
const type = fileType(buffer)
if (!type || !/image/.test(type.mime)) return
if (buffer.length < 10000) return
if (isAnimated(buffer)) {
const webmBuffer = fs.readFileSync(x + '.webm')
savings.push({name: x, orig: buffer.length, saved: buffer.length - webmBuffer.length})
animatedSize.push(buffer.length)
totalAnimated++
console.log('ANIMATED!!')
} else {
nonSize.push(buffer.length)
totalNon++
console.log('NOT ANIMATED!!')
} console.log(x, type.mime)
} catch (err) {
console.error(err)
}
})
let filteredSav = []
for (const entry of savings) {
if (entry.saved < 4000) continue
if (entry.orig < 100 * 1000) continue
const percent = entry.saved / entry.orig
console.log(percent)
filteredSav.push(percent)
}
animatedSize = animatedSize.sort((a,b) => a - b)
nonSize = nonSize.sort((a,b) => a -b)
savings = savings.sort((a,b)=> a.saved - b.saved)
console.log({animatedSize, nonSize, savings, totalAnimated, totalNon})
console.log(filteredSav.reduce((a,b)=>a+b,0) / filteredSav.length)
~ In response to your question @addyosmani I definitely think there's enough evidence here to say we should have a separate audit to flag them for conversion to webm. Additionally, we see that large GIFs are most commonly used for animated content (~64% is good enough in my book in this case 😄) and, from my perspective, could be flagged by the audit even if they're not animated at the beginning. I'd propose the following approach to not block this on Chrome-side work:
@paulirish @brendankenny does this capture what we discussed as well? |
@patrickhulce not 100% sure about how to work on bullet 2. How should I analyse this? (I don't mind doing it). I can start with bullet 1. |
@wardpeet If this direction is fair by @paulirish and @brendankenny, let's make a start on bullet 1 and follow up with 2 and 3 once the initial implementation for the GIF identification audit is written up. Does that sound reasonable? |
For me it does :) |
Some followup research that I did based on Patrick's initial run: https://docs.google.com/spreadsheets/d/1eeY1OZE_P1ilXyHtTEMNzg9v5M7d5ElZh4h6cK5_Lx0/edit?usp=sharing Looked at 1020 real GIFs sourced from HTTPA. We used this to determine the 100kb threshold. Based on the data, brendan eyeballed out a rough estimation of savings for gifs above 100kb. getPercentSavings = bytes => 29.1 * Math.log10(bytes) - 100.7 |
I was trying to follow this recommendation.
Demo with a Google Pixel: Edit 1: Cast button removed with
Edit 2: Now I just tried with an iPhone 6S Plus, and it works well: |
Could you file a new bug report with this feedback? Fyi both links above autoplay for me in Chrome 67. I'm curious if any recent changes in site engagement scoring or otherwise are causing issues here. |
I can reproduce this for both fiddles in Chrome 67.0.3396.87 on my Pixel phone. (It's a Pixel 1.) Both fiddles work fine in Chrome Canary 69.0.3481.0 on the same Pixel phone. Update: Ugh, it was the Data Saver! |
No issues on desktop in any browsers for me on macOS. But I can't get autoplay to work on any browser in iOS. My test: https://jlwagner.net/ext/vid2.html Edit: macOS chrome version is 69.0.3477.0. Tested on latest stable Chrome on iOS, Safari, and UC Browser. |
@addyosmani Chrome 67 on Android works for you? 😮 I have used Stable & Canary: About the new bug? Here https://crbug.com/wizard? |
@abdonrd IIRC, autoplaying of videos may be blocked if you have data saver turned on. Try turning that off if you have it enabled. |
@malchata ouch! Is that! The data saver was on! 🤦♂️ |
@malchata I can confirm Data Saver was the culprit. Turning it off makes the fiddles work as expected even on Chrome 67. |
@malchata @mathiasbynens exactly! Both demos works now with the Data Saver disabled. |
Thanks for helping us debug this folks. Looks like we might want to better document checking for data saver mode being on when trying this recommendation :) |
I'll update the relevant guide today, @addyosmani. |
Thank you all! 🙂 It would be great to mention the data saver on the article, yes! |
@malchata I would also like to propose that <video autoplay loop muted playsinline disableRemotePlayback></video> <video autoplay loop muted playsinline disableRemotePlayback>
<source src="oneDoesNotSimply.webm" type="video/webm">
<source src="oneDoesNotSimply.mp4" type="video/mp4">
</video> |
@addyosmani, any thoughts on |
I don't know if it affects something else, but if we don't add
|
And what is the fallback recommendation if the data saver is enabled? Just the <video autoplay loop muted playsinline disableRemotePlayback poster="oneDoesNotSimply.png">
<source src="oneDoesNotSimply.webm" type="video/webm">
<source src="oneDoesNotSimply.mp4" type="video/mp4">
</video> |
What's changed, or what was fixed? - Added a note about data saver disabling autoplaying video in Chrome for Android (per the discussion in [this issue](GoogleChrome/lighthouse#4696)) **Target Live Date:** Whenever convenient. - [x] This has been reviewed and approved by @addyosmani - [x] I have run `npm test` locally and all tests pass. - [x] I've staged the site and manually verified that my content displays correctly. **CC:** @petele @addyosmani
@abdonrd I've experienced letting browser load preview image via (now Chromium default) |
hey @midzer I am interested in your method (I added |
Hmm, strange. What browser are you using, @huygn ? Using For more info https://www.html5rocks.com/en/tutorials/video/basics/ |
Seems like we should file this as a bug against data saver :) Blindly loading large GIFs while blocking the smaller video versions is incompatible with a data saving mindset. Also, I seem to be able to avoid the data saver issue somehow when the video is standalone and not in a Is this true for anyone else? That might be another workaround if so. |
If it's useful, happy to kick off a thread with the Data Saver team to discuss.
Hmm. Edge case in their heuristics? :) This appears to work fine for me with data saver on. |
Suggested audit
Serve
<video>
instead of animated GIFsBackground
Animated GIFs are suboptimal for quality and performance. While they are a popular option for creative expression, they are awful for web performance - they're large in size, impact cellular data bills, require additional CPU and memory, cause repaints and kill battery. Often, GIFs can be up to 10-12x larger than H.264 videos and take more energy to load and be displayed in mobile browsers. Those resources are being spent on something that has severe color limitations.
Using the
<video>
tag is significantly better for performance than animated GIFs. GIF sites have been shipped videos down instead of GIFs for years and back in 2014, Twitter even added animated GIF support using MP4s. They transcoded GIFs to MP4 on the fly, delivering them in<video>
tags. This is a trend we're seeing, but are still running into plenty of examples of sites accidentally shipping megs of animated GIFs down. The AMP team recently published a blog post that included 52MB of animated GIFs without realizing this.Explanation of how it’s different from other audits
Current audits will capture unoptimized images and resources with large payloads but do not specifically call out animated GIFs that could be served down as
<video>
instead. This is a more targeted recommendation than current audits expose.GIFs alone would not be flagged by this audit (we probably don't want to flag 1x1 pixel tracking pixels..) so detecting if an image is an animated GIF using animated-gif-detector, is-animated or detectanimation might be worthwhile.
We've been considering implementing a feature policy for this idea so having something related in Lighthouse could be useful.
What % of developers/pages will this impact (estimates OK, data points preferred)
According to Android stable, GIFs account for approximately 4-5% of images loaded by Chrome. Although we do not have statistics on animated GIFs as a subset of this population, shifting animated GIFs over to video has the potential to impact a large number of requests on the web.
According to HTTP Archive, GIFs account for 23% of the total image formats being served on sites tracked by HA.
Advanced
If we wanted to do something really clever here, we could use FFMPEG.js (or similar) to try encoding animated GIFs as videos to demonstrate the byte savings, however, this is probably going to be too expensive for most pages.
What is the resourcing situation (who will create the audits, maintain the audits, and write/maintain the documentation)
This is an audit that could be authored by our Lighthouse contractor @wardpeet with input from myself and the image compression team (and Bonsai team). We have staffing to assist with documentation if needed and are planning to write up a best practice doc on using
<video>
vs animated GIFs for /web.Do you envision this audit in the Lighthouse report or the full config in the CLI? If in the report, which section?
The default Lighthouse report. I would place this in the Opportunities section alongside other image optimization opportunities.
How much support is needed from the Lighthouse team?
Evaluation of the technical approach for the audit will be needed. Although there are OSS modules available to assist with detecting whether an image is an animated GIF, these may be 1) too large/unsuitable, 2) require additional thought on the performance of the solution for a page with a number of animated GIFs present.
Note: I noticed this audit was also filed in #2586. I am refiling here using the "new audits criteria" doc that the team published in the docs repo.
The text was updated successfully, but these errors were encountered: