Skip to content

srcset capped at profiling container width — images served blurry on viewports wider than profiling screen #1030

@kushh23

Description

@kushh23

Description

When Optimole's JS page profiler measures an image on a desktop screen (e.g. 1440px wide), it stores srcset entries capped at that container width. Visitors on wider screens (e.g. 1750px) then receive a srcset whose largest 1x entry is 1440px. The browser has no larger 1x size available, so it stretches the 1440px image to fill 1750px, causing visible blur — even when the original source image is 2560px wide with plenty of quality available.

Expected behaviour: The srcset should include 1x entries up to the image's natural width (e.g. 1920w, 2560w), regardless of what viewport width the profiling visit happened on.

Current behaviour: The srcset is hard-capped at the img.offsetWidth at the time of profiling. Any breakpoint wider than the profiling container is silently skipped for 1x DPR.


Step-by-step reproduction instructions

  1. Upload a large image (e.g. 2560×1440px) and place it as a full-width hero on any page.
  2. Ensure Optimole lazyload / page profiler is active.
  3. Visit the page on a 1440px wide non-retina screen (DPR = 1) — this triggers the profiling run and stores the srcset.
  4. Now visit the same page on a 1750px+ wide non-retina screen (DPR = 1).
  5. Inspect the served image URL or open DevTools → Network → filter by image. Observe the image is served at w:1440 despite the viewport being 1750px.
  6. The image appears visibly blurry/stretched because 1440px is being upscaled to fill 1750px.

Screenshots / code snippet

Noticed here: https://secure.helpscout.net/conversation/3248131428/484598
The root cause is in assets/js/modules/srcset-detector.js inside _generateResponsiveSizes():

// Current code (line ~446):
if (dprValue === 1 && targetWidth > currentWidth) {
    return; // ← skips 1920w and 2560w entries when profiled on a 1440px container
}

currentWidth is read from img.offsetWidth — the CSS-rendered size at profiling time. For a full-width hero on a 1440px screen, this is 1440px. Any breakpoint wider than that (e.g. DESKTOP_LARGE: 1920, DESKTOP_XL: 2560) is skipped for DPR=1, so they are never stored in the global srcset profile.

The stored srcset ends up being e.g.: 640w, 960w, 1200w, 1440w, 2880w (2x only)

A 1750px DPR=1 visitor receives this srcset, has no 1x entry above 1440w, and the browser either stretches 1440w → blurry, or picks the 2880w 2x entry (which wastes bandwidth).

Proposed fix — one line change:

// Fixed: cap against naturalWidth instead of currentWidth
if (dprValue === 1 && targetWidth > naturalWidth) {
    return;
}

This ensures that 1x srcset variants are generated for all breakpoints up to the image's actual pixel width, regardless of how wide the screen was during profiling.

Additional notes:

  • Setting width="2560" as an HTML attribute on the <img> tag does not work around this, because the profiler reads img.offsetWidth (CSS layout), not the HTML attribute. CSS max-width: 100% overrides the HTML attribute and offsetWidth stays at container width.
  • The profiled srcset data is stored under DEVICE_TYPE_GLOBAL in Profile.php — meaning a single profiling visit on any desktop screen sets the srcset ceiling for all desktop visitors, regardless of their actual viewport width.

Is this a regression?

No — this appears to be a design limitation in the srcset profiler that has always been present.

Metadata

Metadata

Labels

bugThis label could be used to identify issues that are caused by a defect in the product.customer reportIndicates the request came from a customer.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions