Sorry, we don't support your browser.  Install a modern browser

Automatic Image Format Selection Based on Browser#356

SUMMARY
It would be nice to have a format=auto option in Kirby’s thumb generator, that would automatically pick a file format based on the current browser’s capabilities - e.g., send an AVIF file for the latest Firefox, and WebP for Safari. The ‘auto’ option could be the default, but still allowing us to specify a specific format, if needed.

DETAILED USE-CASE
With version 3.6, Kirby will be able to generate images for us in WebP, AVIF, as well as JPG and PNG, which is awesome. This helps developers automatically create the vast amount of images that are needed in order to support all browsers and sizes optimally - which nowadays is a ridiculous amount. For example, to display a single image on the page, we often have to code something like this:

<picture>
    <source type="image/avif" 
            srcset="<?= $img->thumb(['width'=>300, 'format' => 'avif'])->url() ?> 300px,
                    <?= $img->thumb(['width'=>600, 'format' => 'avif'])->url() ?> 600px,
                    <?= $img->thumb(['width'=>900, 'format' => 'avif'])->url() ?> 900px"
             sizes="(min-width: 640px) 300px, 100%" >
    <source type="image/webp" 
            srcset="<?= $img->thumb(['width'=>300, 'format' => 'webp'])->url() ?> 300px,
                    <?= $img->thumb(['width'=>600, 'format' => 'webp'])->url() ?> 600px,
                    <?= $img->thumb(['width'=>900, 'format' => 'webp'])->url() ?> 900px"
             sizes="(min-width: 640px) 300px, 100%" >
    <img src="<?= $img->thumb(['width'=>300, 'format' => 'jpg'])->url() ?>"
         srcset="<?= $img->thumb(['width'=>300, 'format' => 'jpg'])->url() ?> 300px,
                 <?= $img->thumb(['width'=>600, 'format' => 'jpg'])->url() ?> 600px,
                 <?= $img->thumb(['width'=>900, 'format' => 'jpg'])->url() ?> 900px" 
         sizes="(min-width: 640px) 300px, 100%" 
         alt="tropical beach sunset">
</picture>

That is because we want to control which file format gets displayed by the browser, based on their capability. In this example, if the browser supports AVIF (the most compresed format), it will load it. If not, it will try WebP, and finally, if it is an older browser, it might fallback on JPG.

But the fact is, that we often already know which browsers support which formats - eg., based on https://caniuse.com. So, in theory, we could actually make that decision ourselves, based on the current user-agent in the request. If the file format was automatically decided based on the current browser, we wouldn’t need to use the <picture> element in the example above, and could just use the standard <img> element instead:

<img src="<?= $img->thumb(['width'=>300])->url() ?>"
         srcset="<?= $img->thumb(['width'=>300])->url() ?> 300px,
                 <?= $img->thumb(['width'=>600])->url() ?> 600px,
                 <?= $img->thumb(['width'=>900])->url() ?> 900px" 
         sizes="(min-width: 640px) 300px, 100%" 
         alt="tropical beach sunset">

There will be situations where we might still need to specifically generate a PNG, or JPG, or another format, so having the format option would still be needed.

15 days ago

After thinking some more about this, I can see some potentially serious drawbacks to this, too - namely, that it would means that pages that use this would have to be dynamically generated with every request, and would not be able to be cached.

If the developer is not using page caching at all, or if a particular page doesn’t need it, this could still be useful - but perhaps it would be more prudent not to have it as a ‘default’ setting for thumb()

15 days ago

Why would it have to be dynamically generated? It seems to me that the implementation is pretty much the same the only difference is that thumb() has to encode multiple images not one.

The main difference is on the webserver level. AFAIK the biggest trouble people mentioned is that you have to be careful about cashing of the images because you suddenly serve multiple assets under one url.

14 days ago

There is nice article here https://corydowdy.com/blog/webp-jxr-nginx-content-negotiation-test where you can test if it works in your browser.

14 days ago

The article mentioned is over 6 years old, and a lot has changed in Nginx, browsers and PHP in that time. The idea of serving multiple, varying STATIC assets (like images) from the same URL is unintuitive, and likely to cause issues with most caching mechanisms. The Nginx configuration that is suggested has potential security issues, might impact server performance, and it makes it a lot more difficult to integrate Kirby plugins like Cachebuster, which also require tweaks to routing at server-level.

It may be safer and easier to use a combination of browser-side JS and a Kirby plugin to do this. The JS library would work similarly to a lazyloading one: it could detect the browser’s support for different image formats, and then dynamically inject/replace the images on the page, by requesting an image in the appropriate format from the server. On the Kirby side, we could create routes that intercept these image requests, then create and/or return the right image based on the file extension. That would keep everything compatible with all caching mechanisms - including Kirby’s - and not require any special server configuration at all.

14 days ago

FYI: All browsers send the supported formats (mime types) in the HTTP request (FF in this case):
Accept: image/avif,image/webp,/

Since $app is aware of this infomation it could set a config value thumb() may check prior generating the actual URL for the template. This way you’d have a default format for the current browser session.
There’s no need for a JS lib and client side sniffing. It’s always been in the request header for ages.

13 days ago

And I think its actually way more supported than those 6 years ago. And many companies are using this. Considering its in googles webp docs as one of the ways how to implement new formats correctly.

I’ve tested my browsers and they all send the headers. (my safari is pretty old so thats why no webp).

I don’t think there are that many changes to make this work

  1. Nginx/Apache config for public folder setup. (already around from other projects)
  2. Change to the thumb() that checks the Accept headers header and returns/renders the appropriate file.
13 days ago