
While developing our Page Lightning app for BigCommerce, I needed to determine appropriate responsive image attributes (sizes, srcset) to use. This resulted in my deep diving into how responsive images work and the development of the Responsive Image Analyzer to help calculate those values.
This article explains the process.
TLDR for users of the Page Lightning app for BigCommerce
Read the Page Lightning Image Loading guide to optimise some of your key responsive images.
Pixel Ratios
Before we get started, I’d like to clarify some terms. First, a browser works on two pixel systems: the CSS pixel and the device pixel. Pixel Ratio is the ratio between the two.
CSS pixel values are used in the CSS style sheets to define the layout of the content on a page. The viewport width in CSS pixels determines how the page is laid out.
Desktop monitors tend to have a large CSS pixel width as they are physically large devices. This means you can cram more on a page and make things smaller without making it hard to view. e.g. My main monitor has a CSS pixel width of 2545px, and in this case it includes a product filter and 3 products per row.

Mobile devices are physically small, so they typically provide a smaller viewport CSS pixel width. That way, the layout tends to use larger elements, making the page easier to read on a small screen. This is an iPhone 14 Pro Max with a CSS pixel width of 430px. This time, the filter is hidden, and there are only two products per row.

However, the CSS pixel width the device provides is not always the true number of physical pixels on the screen. The iPhone 14 Pro Max has a physical pixel width of 1290px, giving it a pixel ratio of 3. If it said the CSS pixel width was that, the images and text would be tiny.
So, why have more physical pixels than CSS pixels, if you’re only going to use the CSS pixel? We’ll thats not the case. CSS pixels determine how much of the screen something occupies, and the browser can leverage the physical pixels to make things sharper. e.g., the page fonts or the images are sharper.
In this article, it’s how responsive images are handled, which relates to both the CSS width of the image and the number of physical pixels the image uses.
Basics of a Responsive Image
I’m going to focus on the srcset+sizes style of responsive image.
<img src='...' sizes='...' srcset='...' />
src is just a fallback if srcset is not supported (IE, Opera Mini). The same image is used for all devices. A reasonably sized image would work here.
sizes is a formula for determining how wide, in CSS pixels, the image will be displayed. Maybe the image is always a quarter of the width of the screen:
sizes='25vw'
Or it is like the above page, where the filter disappears and the number of products on a row changes, on different device widths:
sizes='(min-width: 1286px) 1116px, (min-width: 1262px) calc(96vw - 111px), calc(100vw - 42px)'
srcset defines a list of image URLs with different physical pixel widths, along with the image’s width. This lets the browser choose the best image for the situation at hand:
srcset='https://domain.com/80w/image1.webp 80w, https://domain.com/160w/image1.webp 160w, https://domain.com/320w/image1.webp 320w, https://domain.com/640w/image1.webp 640w, https://domain.com/960w/image1.webp 960w, https://domain.com/1280w/image1.webp 1280w, https://domain.com/1920w/image1.webp 1920w, https://domain.com/2560w/image1.webp 2560w'
This is where pixel ratio comes in. sizes estimates the CSS pixel width allocated for the image, and how big it will be displayed. But the browser picks the image from the list that best suits the physical pixel width allocated.
Say with the iPhone 14 Pro Max, the sizes calculates that an image is allocated 200 CSS pixels. Its pixel ratio of 3 means there are 600 physical pixels to display it. The browser will pick the 640w image to download so that it can display a nice, crisp image.
What Types Of Devices Are Visiting Your Site
To optimise our responsive images, we need to know which device widths and pixel ratios are visiting your site. We decided to use GA4, BigQuery, and a Looker Studio report to pull the data together.

For our Tag Rocket customers, this report for your store is available in the Tag Rocket report from version 5.2.7.
I came up with the following information that would be of use, related to each page view on the website:
- Viewport CSS Pixel Width
- Device Pixel Ratio
- Network Speed
Viewport CSS Pixel Width
For most browsers window.innerWidth will get that. If you want to catch the few edge cases, document.clientWidth and document.body.clientWidth (After DCL) should capture a bit more.
Device Pixel Ratio
If you can get it (most cases), it’s at window.devicePixelRatio.
Network Speed
A measure of network speed helps estimate the cost in time of loading an image. In our case, we currently proxy this by using the inverse of Time To Last Byte (TTLB). The longer it takes to respond to the first request is an indicator of how fast images will also download. I’m using TTLB, not TTFB, because it is skewed by early hints.
I’m currently gathering effectiveType, rtt and downlink (navigator.connection) to see how useful they are. However, they cut out many devices because they are not supported by Safari or Firefox.
Gathering it in GA4
We already send a GA4 event for TTLB, rtt and downlink, so all we needed to do is make sure we include width and pixel_ratio in that event.
BigQuery
We export the data to BigQuery and process it into a table dedicated to Web Vitals. This table includes all TTFB events, along with their ttfb, rtt, downlink, width, and pixel_ratio.
Looker Studio
We then display that data in a Looker Studio report so it is easy to view and export to a CSV file.
To make the data more manageable, we group it by page type, viewport width and pixel ratio. We’re currently averaging the TTLB after capping it at 10. With counts and averages, it is easy to reaggregate the data as we process it.
Page type is added so we can filter out data to specific page types and reaggregate before analysing.

It’s interesting to see the different types of devices involved. AI Overview thinks the top one is a Samsung S2. The widest device comes in at 5112px, and the largest pixel ratio was 16.875 (196 CSS pixels)
Calculating Sizes
Getting sizes right is a critical part of optimising responsive images. If it is wrong or missing, the browser may load the wrong image size, making it look bad or wasting the user’s bandwidth.
The concept is relatively simple. Identify a responsive image via a CSS selector in a browser, iterate through a set of CSS viewport widths, and measure the image’s CSS width at each viewport width.
We use Puppeteer to automate the browser, changing viewport widths and gathering the CSS selector widths for the image.
The device CSV file we gathered was used to determine what viewport widths to test.
With some two-and-fro with ChatGPT, I managed to smooth out the data, turn it into sections of straight lines, and then calculate the sizes formula from it. Each sizes part matches a line with an angle and a pixel offset. I even created a graph from the sizes formula to confirm that it matched the image sizes discovered. Here’s a category page header image where the image takes up most of the width until it maxes out, with a bit of jitter.

sizes='(min-width: 1284px) 1116px, (min-width: 1262px) calc(100vw - 168px), calc(100vw - 42px)'
Which Responsive Image Widths To Use?
I decided to try to work out which responsive image widths would minimise wasted time downloading pixels that weren’t needed.
For each viewport width, we know the image’s CSS width. From that, we can calculate the desired physical pixel width. If there were a responsive image for that width, we would avoid waste.
If not, the device would pick the next-largest responsive image, resulting in some waste.
I calculated waste by first determining how many extra pixels would be downloaded beyond the required amount. i.e. the pixel count of the downloaded image minus the pixel count of the image required. This required knowing the image’s aspect ratio, which the browser provided.
We’re less worried about waste on a fast network than on a slow one. So I divided the waste by the network speed (for TTLB it’s multiplied) gathered earlier to arrive at a cost.
Now we have a cost calculation formula. I had ChatGPT help me develop an algorithm to minimise the total cost across all the data in the CSV file. So it takes into account frequency of widths, device ratios and network speeds to return a list of image widths that minimise the total cost for all that data. And if page type is provided, it can be filtered down to just data for a specific set of pages.
The CVS data I used came from a US-based ecommerce website, and I tested a category page’s product images with an aspect-ratio limit of 2. And asked it to aim for 8 different responsive image widths.
The test page is from our own test website, where the product images are identified by .card-image.
68,561 TTLB events for category pages were used. The TTLB values were capped at 10 seconds and averaged across each viewport width/pixel ratio combination.
If the images were not responsive but a single image of 566 pixels wide (max size at a pixel ratio of 2), the average cost per view was calculated to be 419K.
Using the current responsive image list, the average cost per view was calculated at 215K. So about half. Interestingly, only two of the widths in the list were used.
80w, 160w, 320w, 640w, 960w, 1280w, 1920w, 2560w
The new widths calculated from the data reduced this to 24K. Close to one-tenth the waste cost of the current responsive image list. This time all the widths were valuable.
272w, 304w, 336w, 368w, 400w, 464w, 528w, 576w
This chart shows the total costs for each width, with the idea of using widths to minimise its area:

The aspect ratio limit makes a big difference. If removed, the analysis has a lot larger physical pixel widths to cater for. The largest image is 1347w, causing a waste per view of 221 billion if that were the only image. The suggested widths get a lot larger, and waste increases to 43k, not bad.
272w, 336w, 400w, 448w, 480w, 560w, 656w, 1360w

Time to test this in the real world and see its impact on LCP.
Want To Play At Home?
I have developed a tool that accepts a CSV file, an image selector, and a test page. It’s private and clunky at the moment. We’re soon to release a performance app on BigCommerce, and this tool will be used to optimise responsive images, amongst other things. And we already gather core web vitals, so we can see how well it works.
References
Get speedy with responsive images automation
A video that starts by explaining responsive images and pixel ratios.
Andrea then discusses using GA4 data to analyse users’ screen widths and pixel ratios, and proposes capping image fidelity to 2 (pixel ratio max) based on a Twitter study.
He goes on to explain the responsive image automator, which helps you choose a good set of image widths for a responsive image by minimising waste.
This was a major inspiration for the direction I took my project.
Mobile Image Processing at Velocity SC, 2015
Lots of technical details on how images are processed. At about 10 minutes, Tim talks about the cost of oversized images, not only network bandwidth, but also CPU and memory.
At about 19 minutes, he talks about wasted bytes and how to reduce them by choosing better image sizes. And that there is a better impact by focusing on providing more width options for the larger images, which cause the most waste.
Responsive Images 101, Part 9: Image Breakpoints
Breakpoints are the set of image widths you pick for your responsive image. It explains why the choices are important and covers several methods for selecting breakpoints.
An interesting point is that images can be cached in CDNs, etc., and you want to reuse image URLs to benefit from that caching.