WordPress / performance

Performance plugin from the WordPress Performance Group, which is a collection of standalone performance modules.
https://wordpress.org/plugins/performance-lab/
GNU General Public License v2.0
353 stars 95 forks source link

Add low quality image placeholders using background color #19

Closed adamsilverstein closed 2 years ago

adamsilverstein commented 2 years ago

The LQIP approach, popularized by Medium, uses a very small placeholder image to enable a quicker initial page load. The idea is the placeholder gives the user a sense of the image shapes and color while consuming minimal bytes. The hires image then replaces the placeholder later in the page load cycle.

The approach may help with perceived performance, although in general more bytes will be consumed overall - the extra image, plus the JavaScript to make the placeholder work. This approach deserves more research and might remain plugin territory.

previously: https://github.com/adamsilverstein/modern-images-wp/issues/19

mitogh commented 2 years ago

One thing to consider can be the Blurhash algorithm:

Where the hash for the image can be stored as part of the meta of the image.

westonruter commented 2 years ago

The Node.js AMP Optimizer has support for blurry image placeholders. Here's the implementation: https://github.com/ampproject/amp-toolbox/blob/main/packages/optimizer/lib/transformers/AddBlurryImagePlaceholders.js

dainemawer commented 2 years ago

The LQIP is quite a common pattern across the web now. I've worked on a few projects using https://github.com/aFarkas/lazysizes which also implements this paradigm.

One way of handling the low quality image, in order to save a server request is to encode it: data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==

This means that theres no request to the placeholder image and the browser can carry on its business without worrying about connectivity or download issues. Im sure that may increase the byte size of the final HTML resource, but if sites are using gzip or brotli I think this could be trivial.

spacedmonkey commented 2 years ago

I have implemented blurhash for the web stories plugin. I really liked it. I would be willing to work on this as a feature plugin.

adamsilverstein commented 2 years ago

I have implemented blurhash for the web stories plugin. I really liked it. I would be willing to work on this as a feature plugin.

That looks promising, can the blurhash approach use the base64 version of the placeholder mentioned above?

dainemawer commented 2 years ago

Did a bit of research on this @adamsilverstein - looks like its faster to run with blurhash as opposed to implementing a base64 image - so maybe we just run with blurhash ?

mitogh commented 2 years ago

Adding some additional ideas here in case we want to explore different alternatives:

eugene-manuilov commented 2 years ago

One thing that we should remember is that images are indexed by search engines and one of the techniques to drive traffic to the site. If we replace images with placeholders, then this may cause images not being indexed by search engines and thus negatively affect the organic traffic for some sites.

eugene-manuilov commented 2 years ago

JPEG has progressive compression that allows to achieve similar results: https://medium.com/hd-pro/jpeg-formats-progressive-vs-baseline-73b3938c2339#e3aa

Not sure how feasible it is to implement in our plugin.

adamsilverstein commented 2 years ago

JPEG has progressive compression that allows to achieve similar results: medium.com/hd-pro/jpeg-formats-progressive-vs-baseline-73b3938c2339#e3aa

Possibly we could take advantage of this with custom Image classes, we need to look into support in libgd and imagick. This wouldn't help with WebP images.

adamsilverstein commented 2 years ago

One thing that we should remember is that images are indexed by search engines and one of the techniques to drive traffic to the site. If we replace images with placeholders, then this may cause images not being indexed by search engines and thus negatively affect the organic traffic for some sites.

Good point that bears more investigation. Can the image(s) be listed in a meta tag to avoid that (ie og:image)?

My other concern about this change overall is how it might negatively impact the largest contentful paint measurement, how much of a hit will that take for this "perceived" performance boost, and is that acceptable?

pbearne commented 2 years ago

I have looked at this a bit and a bit of code that works the approach I took was to add a background image to the img tag and removed the background on image load (for transparent images) I created the image using blurhash and stored the hash as image meta and then created the base64 on the render It might be better to store the base64 in DB to save having to recreate it each time but this would take more space I hacked some proof on concept code here https://github.com/pbearne/blurhash/blob/main/blurhash.php

onload="this.style.removeProperty('background');" style="background-size: cover; background-image: url(data:image/png;base64,iVBORw0KGgoAA......ORK5C=)

I need to add a filter to the media.php in core

$filtered_image = apply_filters( 'wp_img_tag_add_adjust', $filtered_image, $context, $attachment_id );

What I like about adding the image server-side is that don't have to wait for JS to render it but the downside is more data on the wire and isn't removed if no-JS (but we could handle that with CSS maybe)

Would this approach work?

I suspect that we will need a new filter for any approach we take to allow us to add elements to the img tags similar to this comint https://github.com/xwp/wordpress-develop/commit/2efb39b5a3942b475ebb0fa43c49dc6a92d243c9

adamsilverstein commented 2 years ago

@pbearne Very interesting, I was wondering about using the background - clever!

I need to add a filter to the media.php in core

Can you point specifically to where (or is there a ticket to add)? Could you try using wp_get_attachment_image filter for now? That might be sufficient for the plugin.

Have you tested how this approach affects performance metrics?

adamsilverstein commented 2 years ago

JPEG has progressive compression that allows to achieve similar results: medium.com/hd-pro/jpeg-formats-progressive-vs-baseline-73b3938c2339#e3aa

@eugene-manuilov Good point, this is a great feature of jpegs.

When researching I found that LCP doesn't take progressive images into account currently (see https://github.com/w3c/largest-contentful-paint/issues/71) so I'm curious how any approach would affect both perceived and measured performance.

pbearne commented 2 years ago

@adamsilverstein

Can you point specifically to where (or is there a ticket to add)? Could you try using wp_get_attachment_image filter for now? That might be sufficient for the plugin.

here is the filter I was looking at adding https://github.com/xwp/wordpress-develop/commit/2efb39b5a3942b475ebb0fa43c49dc6a92d243c9 I will look at the wp_get_attachment_image filter and see if I can use that instead

Have you tested how this approach affects performance metrics?

We are looking at the metrics it does add to the content on the wire so it may well be better to use JS to convert the blurhash into the background image

pbearne commented 2 years ago

@adamsilverstein I have a working(ish) plugin https://github.com/pbearne/blurhash

Stevemoretz commented 2 years ago

@adamsilverstein I have a working(ish) plugin https://github.com/pbearne/blurhash

Was looking for this all over!

pbearne commented 2 years ago

module for performance plugin https://github.com/xwp/performance/tree/INITS-199_domiant_color

pbearne commented 2 years ago

https://user-images.githubusercontent.com/46270/161837536-b9082081-0b5e-4d56-9aeb-fd948d9c371d.mov

video of it working

adamsilverstein commented 2 years ago

interesting related ticket shared by Joy in slack: https://github.com/WICG/largest-contentful-paint/issues/68#issuecomment-742141525

adamsilverstein commented 2 years ago

@pbearne - https://github.com/WordPress/performance/pull/282 is a core patch right? Have you been able to implement this in the performance lab plugin (and if so, what is the PR for that)?

erikyo commented 2 years ago

original conversation on slack

As low-res image you can use the preview contained in the exif data of the jpg, a method to get that image is to use the function exif-thumbnail (php exif module needed). To mention, it is possible for an image to not have a preview (not all have it, especially if they were stripped of metadata before being uploaded) so better to use it in combination with other methods.

pbearne commented 2 years ago

this also allows us to color the drop shadow box-shadow: var(--dominant-color) 0px 54px 55px; or create a border
border: solid var(--dominant-color);

pbearne commented 2 years ago

I have started writing the dev notes https://docs.google.com/document/d/1C7S-5GMw9R81fQOXrc2PjzEaQniMpIgg2dT1ta69LmQ/edit#heading=h.l8wdvygotok4

mitogh commented 2 years ago

Thanks would be great to have permission to comment on the document to provide feedback, wanted to ask if we can include how the data (colors) are being stored in WordPress in order to inform plugins and external developers about this new property and how is stored in the database for later consumption.

Thanks @pbearne

felixarntz commented 2 years ago

@pbearne @spacedmonkey I've created a [Module] Dominant Color label for the eventual module here.

jonathan-dejong commented 2 years ago

Hey!

Stumbled upon this topic as I was researching why Google lighthouse did not like our implementation of lazy loading using https://imgproxy.net/

We got a response that might be interesting to this conversation as well so adding it here slightly redacted. This was in response to our questions about negative scoring due to doing a LQIP lazyloading solution using Imgproxy.

To quickly break it down: The core web vitals that are used for ranking, and appear in search console are the real user metrics that come from CrUX, so it's a reflection of what a user actually experienced.

The lighthouse powered lab test part (where recommendations for lazy-loading appear) aren't used for ranking, it's intended as a spot check to highlight best practices. Naturally an automated test can't always account for every use case, and sometimes the suggestions don't always match.

Lighthouse is open source, and you can raise an issue here: https://github.com/GoogleChrome/lighthouse/issues if you really feel the test is unfair.

However, I think in this case, you'd have a hard time convincing the team to support any kind of exception to the rules here, because I think your solution isn't without issues.

You appear to doing this low-quality > high-quality swap for the LCP element, makes it slower to actually achieve the rendering of the image, and destroys the browser preload scanning, therefore the ability to eagerly download the image and render.

I'd also argue that the pattern of instantly downloading a low quality image, then lazy-loading the higher quality version and swapping that in isn't a net gain, Ultimately you're loading more, the low quality image and the full res ones. That's really just an extra image load that's not benefiting anyone here.

Our Q: The placeholder solution gets around various cumulative layout issues because it keeps the width and height which lazy loading does not?

Thankfully all major browsers handle this for you, if you specify the native height and width, the browser calculates the aspect ratio and automatically reserves the space in layout, even if the image is resized with css to be responsive.

So I do think there are much more performant ways to do what you're trying to achieve, and you could leverage them and the excellent performance of the image proxy to make a great solution here.

Using the <picture> element (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture) with to provide the .jpg fallback when needed, would perhaps be the most performant solution here.

felixarntz commented 2 years ago

The new module from #282 in action - the comparison on an emulated slow connection nicely shows the UX enhancement from this module. Amazing work @pbearne @spacedmonkey!

Without dominant color

https://user-images.githubusercontent.com/3531426/173693912-fae77670-a16b-4457-8e20-5a572786623f.mov

With dominant color

https://user-images.githubusercontent.com/3531426/173693941-eeb46d68-ed6d-4f73-a5ec-5b27f6e8e2ac.mov

ThierryA commented 2 years ago

Love it, great work ❤️