Images: AVIF, HEIC, or JPG?

I’m at a loss!

I should have tested this earlier, but I’ve just realised HEIC format is not supported by Google. I’ve been happily going along serving up all my images in HEIC format, not realising that was Apple’s bespoke format.

For my images I’m more interested in size than quality, so should I use AVIF or JPG, rather than HEIC?

I would say it depends on how many browsers you need to support. Avif does everything jpeg does but better. If you expect these photos will only ever be viewed on browsers supporting avif I’d definitely say use avif

You could serve more than one/all <picture>: The Picture element - HTML: HyperText Markup Language | MDN

Thanks for your replies.

I am at a loss as how to you provide for every eventuality. From what I gather you can use:

<figure>
   <picture>
      <source ...>
      <source ...>
      <source ...>
      <img..>
   </picture>

   <figcaption>...</figcaption>
</figure>

But I’m unsure as to how to include an image.jpg, image.avif, image.heic, and an image.webp. I think I’ll go with .avif and hope for the best.

  • <source srcset=…> for additional types
  • <img src=…> for main/jpeg

Thanks, @charlie137.

What I’m struggling with is how to get the separate images and their extensions – .avif, .jpg, .webp – included in my shortcode:

<figure>
    <picture>
    <a href="{{ path }}">
        <source srcset="{{ path }}" type="image/avif" />
        <img src="{{ path }}" type="image/jpg" class="responsive" {%- if caption %} alt="{{ caption }}"{%- endif %} />
    </a>
        {%- if caption -%}
            <figcaption>{{ caption }}</figcaption>
        {%- endif -%}
    </picture>
</figure>

Any pointers?

Try to drop picture tag completely and add srcset into the img to use anchors:

<img src=“$MAIN” srcset=“$TYPE1, $TYPE2” width=“…” height=“…” />

You mean more like this:

<figure>
    <a href="{{ path | jpg }}">
        <img src="{{ path | jpg }}" srcset="{{ path | avif, path | webp }}" class="responsive" {%- if caption %} alt="{{ caption }}"{%- endif %} />
    </a>
        {%- if caption -%}
            <figcaption>{{ caption }}</figcaption>
        {%- endif -%}
</figure>

My strategy (not invented by me - found on the internet years ago) is to pretend serving JPEG & PNG only, but to actually serve WEBP and AVIF if the browser’s HTTP Accept header indicates interest.

In the NGINX http section, suffixes get generated :

http {
    # ...

    map $http_accept $avif_suffix {
        default ".no-avif"; # try_files fails for non-existent file.jpg.no-avif
        "~*avif" ".avif";
    }

    map $http_accept $webp_suffix {
        default ".no-webp"; # try_files fails for non-existent file.jpg.no-webp
        "~*webp" ".webp";
    }

    # ...
    
    server {
        # ...

        location /choose/your/path {
            location ~ \.(png|jpe?g)$ {
                add_header Vary "Accept-Encoding";
                log_not_found off;
                expires max;
                try_files $uri$avif_suffix $uri$webp_suffix $uri =404;
            }
        }
    }
}

The HTML then only contains JPG and PNG images. The markup is already complex enough with the srcset and sizes:

<img src="foo-512.jpg"
     srcset="
	    foo-128.jpg 128w,
		foo-256.jpg 256w,
		foo-384.jpg 384w,
		foo-512.jpg 512w
	 "
	 sizes="
	    (min-width: 1280px) 6rem,
		                    5rem"
	 width="512"
	 height="512"
	 alt="..."
	 title="..."
>

The browser might request foo-512.jpg. Depending on its Accept HTTP header, it will generate a suffix .avif or .no-avif as well as .webp or .no-webp.

In the location block, try_files will serve foo-512.jpg.avif first, if found, otherwise try foo-512.jpg.webp, and only then fall back go foo.jpg.

If AVIF is not supported by the browser as indicated by its Accept header, then try_files will look for foo-512.jpg.no-avif, which won’t exist, and go on to trying foo-512.jpg.webp and succeed (or foo-512.jpg.no-webp and fail, to then serve the actual JPG).

I’ve got a systemd scheduled task generating *.jpg.avif and *.jpg.webp from all *.jpg files, and the same for *.png.

I’m a bit late responding, but the correct way to add multiple formats to an image using only HTML is through the picture element and multiple sources. For example:

<picture>
  <source srcset="image-400.webp 400w image-800.webp 800w" type="image/webp" />
  <source srcset="image-400.jpg 400w image-800.jpg 800w" type="image/jpeg" /> 

  <img src="img/image-400.jpg" alt="Alt Text" />
</picture>

Each source contains a complete srcset for your image at every size you want to serve it, just as if you were using a single format. The browser works its way down from top to bottom, and selects the first source element in a format it supports and then selects the image to download from the srcset attribute based upon screen resolution and display size etc. (In this example, if the browser did not support webp images it would ignore that source element entirely, and select an image from the jpeg srcset).

The img element determines the alt text for the image, any classes to apply etc, and its src definition acts as a fallback in cases where the user’s browser does not support responsive images; I always set this to the smallest jpg for smallest filesize and maximum compatibility.

If you’re not familiar with responsive images, MDN’s guide is very helpful. The thing to remember, is that the img element is the only child of picture to actually render on the page. The various source elements and their srcset attributes are just hints for the browser on files it can use to replace the src attribute on the img.

So, having read the MDN docs, this should work also:

<figure>
        <img src="image.jpg" srcset="image.jpg 1024w, image.avif 1024w, image.heic 1024w, image.webp 1024w" alt="caption" />
        <figcaption>Caption</figcaption>
</figure>