Specifying both image size and pixel density in srcset - html

Here's my img tag:
<img src="https://myserver.com/image?w=667&h=375 667w"
srcset="https://myserver.com/image?w=1366&h=1024 1366w 1x,
https://myserver.com/image?w=1366&h=1024&dpr=2 1366w 2x,
https://myserver.com/image?w=1366&h=1024&dpr=3 1366w 3x,
https://myserver.com/image?w=1024&h=768 1024w 1x,
https://myserver.com/image?w=1024&h=768&dpr=2 1024w 2x,
https://myserver.com/image?w=1024&h=768&dpr=3 1024w 3x,
https://myserver.com/image?w=800&h=480 800w 1x,
https://myserver.com/image?w=800&h=480&dpr=2 800w 2x,
https://myserver.com/image?w=800&h=480&dpr=3 800w 3x" sizes="100%">
I'm using imgix which returns the image in correct pixel density based upon the dpr query parameter. The above does not seem to work, the image is not rendered in the right size. Am I not using the correct format?

You can't mix the x and w descriptors in your srcset attribute.
I don't know imgix, but I suppose ?w=800&h=480&dpr=2 returns an image with dimensions of 1600x960 pixels. Are https://myserver.com/image?w=800&h=480&dpr=2 and https://myserver.com/image?w=1600&h=960&dpr=1 the same image?
If the image is always the same (same content and same aspect ratio) on every visible size, you should define which visible/CSS sizes you need (depends on your design, for example 800, 1200 and 1600 pixels) and write this:
<img
src="https://myserver.com/image?w=800&h=400"
srcset="https://myserver.com/image?w=800&h=400 800w,
https://myserver.com/image?w=1200&h=600 1200w,
https://myserver.com/image?w=1600&h=800 1600w,
https://myserver.com/image?w=2000&h=1000 2000w,
https://myserver.com/image?w=2400&h=1200 2400w,
https://myserver.com/image?w=2800&h=1400 2800w,
https://myserver.com/image?w=3200&h=1600 3200w"
sizes="100vw">
The ?w=2400&h=1200 image will be downloaded by the browser for several configurations:
screen density 1 with viewport width equal to or below 2400px
screen density 2 with viewport width equal to or below 1200px
screen density 3 with viewport width equal to or below 800px
etc.

Related

HTML picture element downloading wrong image on mobile

I'm running a page through Google's Page Speed Analyzer and for mobile, it's telling me I should download the proper size images. I know the viewport size, but I don't know the width of the slot because we are using a responsive layout. I've looked at the documentation and a bunch of examples and none of them are using what I think should be the correct image.
The Network tab of developer tools is showing we are downloading about.webp for mobile and not either of the smaller images (200px or 400px). Even when changing the device in Chrome to a smaller viewport width, it still downloads about.webp.
What is the correct HTML for a picture element to get Page Speed Analyzer to use the appropriate image?
Below is my picture element. Thanks!
<div class="row"><div class="col-md-3">
<picture>
<source type="image/webp"
sizes="(max-width: 200px) 200px,
(max-width: 400px) 400px,
(max-width: 1500px) 1500px"
srcset="//cdn.storyboardthat.com/site-images/articles/education/about-sbt-w200.webp 200w,
//cdn.storyboardthat.com/site-images/articles/education/about-sbt-w400.webp 400w,
//cdn.storyboardthat.com/site-images/articles/education/about-sbt.webp 1500w">
<source type="image/png"
sizes="(max-width: 200px) 200px,
(max-width: 400px) 400px,
(max-width: 1500px) 1500px"
srcset="//cdn.storyboardthat.com/site-images/articles/education/about-sbt-w200.png 200w,
//cdn.storyboardthat.com/site-images/articles/education/about-sbt-w400.png 400w,
//cdn.storyboardthat.com/site-images/articles/education/about-sbt.png 1500w">
<img src="//cdn.storyboardthat.com/site-images/articles/education/about-sbt.png"
alt="Storyboard That"
title="Storyboard That"
class="lazyload "
style="max-width:100%;height:auto;"
width="1500" height="400" loading="lazy">
</picture>
</div>
You have forgotten about Device Pixel Ratio (DPR).
You see the <picture> element basically says to browsers "here are a few options to choose from, I have indicated my preference but it is up to you to decide which image you think is best".
As you haven't specified a DPR preference the browser is using the following logic:
"Ok so I have 3 images to choose from, what is my current DPR? Oh I have DPR set to 3.
How big is the image at this screen width (320px)? Full screen width so I need a 320px image.
Ok so it is 320px * 3 DPR to ensure I have the highest quality image that matches my current display resolution.
So I need a 960px image minimum. My options are 200, 400 or 1500, I better choose the 1500 image and down sample it."
So how do I control pixel density decisions?
You can specify pixel density with 1x, 2x, 4x etc. For example:
<img src="default.webp"
srcset="hiddef.webp 2x, heighestdef.webp 4x"
alt="Image description">
However with how you have currently structured your picture element it won't quite work as you can't combine image 200w with image 2x to do image 200w 2x.
If you want to keep the same image at all DPIs then you would specify that image 3 times.
<img src="default.webp"
srcset="default.webp 2x, default.webp 4x"
alt="Image description">
Otherwise if you want the browser to change the image automatically based on DPR just leave your <picture> element as it is.
I see that the code snippet you have used is incorrect, refer this:
<picture>
<source media="(max-width:200px)" srcset="about-w200.webp">
<source media="(max-width:400px)" srcset="about-w400.webp">
<img src="about-sbt.webp alt="About" style="width:auto;">
</picture>
A working reference link

Understanding srcset and sizes in combination with HiDPI monitors

I have been into CSS for quite a while now, but srcset and sizes for the image element confuse me. Here is an example that I thought would work.
<img alt="Background image flowers"
srcset="img/flowers-480.jpg 480w,
img/flowers-480#2x.jpg 480w 2x,
img/flowers-768.jpg 768w,
img/flowers-768#2x.jpg 768w 2x,
img/flowers-960.jpg 960w,
img/flowers-960#2x.jpg 960w 2x,
img/flowers-1280.jpg 1280w,
img/flowers-1280#2x.jpg 1280w 2x"
sizes="(max-width: 1279px) 100vw,
(min-width: 1280) 1280px"
src="img/flowers-960.jpg">
The idea is to have an image that's 100% of the viewport until the viewport is 1280px wide or wider, then the image will be fixed size. However, to compensate for higher DPI devices I thought it was recommended to add DPI descriptors (1.5x, 2x and so on), as suggested here and here.
What I thought the above code would do is:
check sizes, see what kind of size to expect for the image (and if a relative unit is given such as % or vw, calculate the pixel width)
find the images in srcset width that is closest to that width
from these images, filter out the one with a DPI descriptor closest to the device's DPI
However, when I put this through a validator I get the following error:
Error: Bad value for attribute srcset on element img: Width for image img/flowers-480#2x.jpg is identical to width for image
img/flowers-480.jpg
So clearly I am completely missing the point of how srcset and sizes work. What am I doing wrong?
As defined on MDN for <img srcset="...">:
Each string is composed of:
a URL to an image, optionally, whitespace followed by one of:
a width descriptor, or a positive integer directly
followed by 'w'. The width descriptor is divided by the source size
given in the sizes attribute to calculate the effective pixel density.
a pixel density descriptor, which is a positive floating point number
directly followed by 'x'.
You tried to use both, and that's illegal.
For a combination of HPDI settings and responsive sizes for images you should actually use <picture> with a few <source> and a fallback <img> elements.
Details are in this article.
According to MDN, "It is incorrect to mix width descriptors and pixel density descriptors in the same srcset attribute. Duplicate descriptors (for instance, two sources in the same srcset which are both described with '2x') are invalid, too."
You have 2x listed 4 times. That's invalid.
Here is an example from MDN:
Example 4: Using the srcset and sizes attributes
The src attribute is ignored in user agents that support srcset when using 'w' descriptors. When the (max-width: 600px) media condition matches, the image will be 200px wide, otherwise it will be 50vw wide (50% of the viewport width).
<img src="clock-demo-thumb-200.png"
alt="Clock"
srcset="clock-demo-thumb-200.png 200w,
clock-demo-thumb-400.png 400w"
sizes="(max-width: 600px) 200px, 50vw">
The "art direction use case", linked in #vbarinov's answer, quoted from developers.google.com, shows how to combine both width and pixel density as criteria for which image source to load and display:
<picture>
<source media="(min-width: 800px)" srcset="head.jpg, head-2x.jpg 2x">
<source media="(min-width: 450px)" srcset="head-small.jpg, head-small-2x.jpg 2x">
<img src="head-fb.jpg" srcset="head-fb-2x.jpg 2x" alt="a head carved out of wood">
</picture>
In viewports that are at least 800 pixels wide, head.jpg should be displayed on a single density display, while head-2x.jpg should be displayed on a display with double or greater density.
Viewports below 800 pixels with, but at least 450 pixels wide, single-density displays should show head-small.jpg, while head-small-2x.jpg is for double density or greater.
The fallback image definition inside the <img> tag has another srcset that defines a double-density version (head-fb-2x.jpg) for viewports below 450 pixels width.
Single-density devices and browsers that don't support source sets or picture elements will fall back to display head-fb.jpg.
In a more complex scenario, we can also add alternative image formats, to take advantage of webp compression while providing jpg versions for browsers without webp support.
<picture>
<source media="(min-width: 800px)" srcset="head.webp, head-2x.webp 2x" with="800" height="941">
<source media="(min-width: 800px)" srcset="head.jpg, head-2x.jpg 2x" with="800" height="941">
<source media="(min-width: 450px)" srcset="head-small.webp, head-small-2x.webp 2x" width="800" height="941">
<source media="(min-width: 450px)" srcset="head-small.jpg, head-small-2x.jpg 2x" width="800" height="941">
<img src="head-fb.jpg" srcset="head-fb-2x.jpg 2x" width="400" height="471" alt="a head carved out of wood">
</picture>
Here is a codepen of the complex example.
As of 2022, I'd argue it's just easier to assume high DPI screens and leverage the breakpoint idea of srcset:
<img alt="Background image flowers"
srcset="img/flowers-480.jpg 480w,
img/flowers-768.jpg 768w,
img/flowers-960.jpg 960w,
img/flowers-1280.jpg 1280w,
img/flowers-1920.jpg 1920w,
img/flowers-4096.jpg 4096w"
sizes="(max-width: 1279px) 100vw,
(min-width: 1280) 1280px"
src="img/flowers-960.jpg">

srcset and sizes attribute: Will a retina device choose the correct double size image?

Unfortunatly I do not have a retina device to test. This is my code:
<img src="http://localhost/example/wp-content/themes/example/libs/lib_cis/libs/renderer.php?src=http://localhost/example/wp-content/uploads/2017/12/dummy-960x480-Dragonfly.jpg&w=960&h=480&q=80&zc=1"
srcset="
http://localhost/example/wp-content/themes/example/libs/lib_cis/libs/renderer.php?src=http://localhost/example/wp-content/uploads/2017/12/dummy-960x480-Dragonfly.jpg&w=240&h=120&q=80&zc=1 240w,
http://localhost/example/wp-content/themes/example/libs/lib_cis/libs/renderer.php?src=http://localhost/example/wp-content/uploads/2017/12/dummy-960x480-Dragonfly.jpg&w=480&h=240&q=80&zc=1 480w,
http://localhost/example/wp-content/themes/example/libs/lib_cis/libs/renderer.php?src=http://localhost/example/wp-content/uploads/2017/12/dummy-960x480-Dragonfly.jpg&w=960&h=480&q=80&zc=1 960w,
http://localhost/example/wp-content/themes/example/libs/lib_cis/libs/renderer.php?src=http://localhost/example/wp-content/uploads/2017/12/dummy-960x480-Dragonfly.jpg&w=1440&h=720&q=80&zc=1 1440w,
http://localhost/example/wp-content/themes/example/libs/lib_cis/libs/renderer.php?src=http://localhost/example/wp-content/uploads/2017/12/dummy-960x480-Dragonfly.jpg&w=1920&h=960&q=80&zc=1 1920w"
sizes="(min-width:960px) 960px,100vw"
alt="Animal X">
Normal screens always choose the correct image as expected (tested). However I wonder if a retina device (with a resolution of 1.5x or 2x) will choose the correct image for theme?
e.g. A retina screen with 1200px in the Browser window should choose the 1920w image, not the 960w image.
Yes it will. It does calculation based on your image width and screen size and then checks with the dpi.
In your example:
1440/1200 = 1.2
1920/1200 = 1.6
So if the screensize is 1200px and non-retina it will choose the first as it's closest to 1 (non-retina). If it's retina 1.5x or 2x it will choose the second as 1.6 is close to 2.
When you use a srcset attribute in your image tag, you can add the corresponding device pixel ratio after each file (seperated from the filename by a space and followed by a comma), which will define which image is suitable for which screen. So that would for example be
<img srcset="small_image.jpg 1x, medium_image.jpg 2x, large_image.jpg 3x" src="default_image.jpg" alt="whatever">
(the regular src attribute following srcset is used by browsers which can't handle srcset)

Responsive images using srcset/sizes isn't respected by Safari iOS

I have the following
<img
src="/img/footer/logo_white.png?v=2.0"
srcset="/img/footer/logo_white.png?v=2.0 1x,
/img/footer/logo_white2x.png?v=2.0 2x"
>
which works fine on normal and hiDPI screens.
But when the viewport's very small (below 400px) the logo doesn't fit therefore I need a smaller version of the image in terms of real length, which I created. Then I tried
<img
class="biw-logo"
sizes="(max-width: 390px) 110px, 175px"
src="/img/footer/biw_logo.png?v=2.0"
srcset="/img/footer/biw_logo_small.png?v=2.0 110w,
/img/footer/biw_logo.png?v=2.0 175w,
/img/footer/biw_logo2x.png?v=2.0 350w"
>
Which works in terms of showing the _small image for viewports lower than 390 pixels - but now I've lost the "high resolution" factor; I cannot force the iOS browser in iphone5s to display a 220px image in length of 110px with the above syntax.
Could you correct my syntax?
<img class="biw-logo" sizes="(max-width: 390px) 110px, 175px" src="http://placehold.it/175x75" srcset="http://placehold.it/110x50 110w,
http://placehold.it/175x75 175w, http://placehold.it/350x150 350w">
You can do that with srcset and sizes. At first tell the browser which images you have available and how many pixels wide these images are, this can be done with srcset:
<img srcset="
/img/footer/logo_white.png?v=2.0 300w,
/img/footer/logo_white2x.png?v=2.0 600w,
/img/footer/logo_white_small.png?v=2.0 150w
">
Now the browser knows it can select from three images that are 150, 300 and 600 pixels wide (I guessed the dimensions, your actual widths might be different).
Second, you tell the browser how large the image will be displayed in the webpage, this can be achieved with sizes:
<img
sizes="(max-width: 400px) 150px, 300px"
srcset="..."
>
The browser knows now, that if the width of the viewport is 400px or less the image will be displayed 150px wide, for viewports larger than 400px it is displayed 300px wide.
This is enough information for the browser to select the right image. On a normal desktop with a normal screen it will select the 300w-image and on a HiDPI desktop it will be the 600w one. On a small viewport with a normal screen the 150w will get selected and on a small viewport with HiDPI the 300w one.
If you want more information about srcset and sizes, take a look at http://ericportis.com/posts/2014/srcset-sizes/.
You also can try using a couple more sources like that:
<picture>
<source srcset="img.png" media="(resolution: 150dpi)" type="image/png" />
<source srcset="img2x.png" media="(resolution: 300dpi)" type="image/png" />
<img src="img.png" alt="alt text" />
</picture>
Or something like that - i didn't test it, i need to learn more about resolution media query to be sure.

img srcset - disregard pixel density

I have two images, one is 1000 x 800 px ("large"), and one is 200 x 200 px ("small").
I want to use srcset / sizes / picturefill to display the small image when the screen is less than or equal to 500 CSS pixels wide, and the large image otherwise.
Here's a straw-man proposal: http://jsfiddle.net/ghhjfo4z/1/embedded/result/
<img srcset="http://i.imgur.com/hw9O9Ia.jpg 1000w, http://i.imgur.com/BgLoqRx.jpg 500w">
This works fine on my 1x pixel density display. But when I switch over to my 2x pixel density retina display, suddenly the small image is only displayed when the viewport is less than or equal to 250 CSS pixels wide.
Is there any way to make the browser use the small image on my 2x pixel density display when the viewport is less than or equal to 500px?
Basically I want to disregard the pixel density of the device, and use srcset and/or sizes to only choose an image based on CSS pixel width of a viewport.
Just for completion: I found the following solution working for me:
<img
srcset="http://i.imgur.com/hw9O9Ia.jpg 1000w,
http://i.imgur.com/BgLoqRx.jpg 500w"
sizes="(-webkit-min-device-pixel-ratio: 2) 50vw,
(min-resolution: 192dpi) 50vw,
(min-resolution: 2dppx) 50vw,
(-webkit-min-device-pixel-ratio: 3) 33.33vw,
(min-resolution: 288dpi) 33.33vw,
(min-resolution: 3dppx) 33.33vw" />
In short this is not possible. If you want to have more control you can use the picture element.
<picture>
<source srcset="img-500w.jpg" media="(max-width: 650px)" />
<img srcset="img-1000w.jpg" />
</picture>
There is also an interesting lazySizes plugin for you, optimumx. Markup would look like this:
<img
data-srcset="http://placehold.it/300x150 300w,
http://placehold.it/700x300 700w,
http://placehold.it/1400x600 1400w,
http://placehold.it/2800x1200 2800w"
data-sizes="auto"
data-optimumx="1.5"
class="lazyload"
src="http://placehold.it/300x150"
alt="flexible image" />
It would be wise to set it to 1.2 or better 1.5 instead of just 1.
For other performance vs. retina considerations have a look here.