[{"data":1,"prerenderedAt":961},["ShallowReactive",2],{"content:\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002Ffixing-blurry-images-on-high-dpi-displays\u002F":3,"surroundings:\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002Ffixing-blurry-images-on-high-dpi-displays\u002F":953},{"id":4,"title":5,"body":6,"description":934,"extension":935,"meta":936,"navigation":370,"path":947,"seo":948,"stem":951,"__hash__":952},"content\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002Ffixing-blurry-images-on-high-dpi-displays\u002Findex.md","Fixing Blurry Images on High-DPI Displays",{"type":7,"value":8,"toc":917},"minimark",[9,13,38,43,46,117,128,231,235,240,258,262,285,289,302,306,327,331,335,341,473,480,484,493,595,605,609,612,715,718,722,730,829,832,836,847,862,866,902,907,910,913],[10,11,5],"h1",{"id":12},"fixing-blurry-images-on-high-dpi-displays",[14,15,16,17,21,22,27,28,32,33,37],"p",{},"An image that looks crisp on a standard monitor but renders soft, fuzzy, or smeared on a retina laptop or a flagship phone is almost always a ",[18,19,20],"em",{},"pixel deficit",": the file you served has fewer device pixels than the screen demands. This page is the diagnostic companion to ",[23,24,26],"a",{"href":25},"\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002F","responsive images with srcset and sizes",", and it sits within the broader ",[23,29,31],{"href":30},"\u002Fimage-media-optimization\u002F","Image & Media Optimization"," guide. High-DPI displays pack 2 or 3 physical pixels into every CSS pixel, so a 400px-wide slot on a ",[34,35,36],"code",{},"devicePixelRatio: 2"," screen needs an 800px image to look sharp. Serve 400 actual pixels into that slot and the browser upscales them, producing the characteristic softness. The fix is never to sharpen the image — it is to ship enough pixels.",[39,40,42],"h2",{"id":41},"rapid-diagnosis-checklist","Rapid Diagnosis Checklist",[14,44,45],{},"Before changing markup, confirm the deficit in DevTools in under a minute:",[47,48,49,61,75,89,103],"ol",{},[50,51,52,56,57,60],"li",{},[53,54,55],"strong",{},"Read the effective DPR."," In the console run ",[34,58,59],{},"window.devicePixelRatio",". A value of 2 or 3 means the screen needs 2x or 3x the CSS-pixel count. A value of 1.25 or 1.5 (common with Windows display scaling or browser zoom) also matters and is frequently overlooked.",[50,62,63,66,67,70,71,74],{},[53,64,65],{},"Find the chosen candidate."," Run ",[34,68,69],{},"document.querySelector('img.blurry').currentSrc",". This is the exact file the browser selected — not what you intended, what it ",[18,72,73],{},"actually"," used.",[50,76,77,80,81,84,85,88],{},[53,78,79],{},"Compare intrinsic vs rendered size."," In the Elements panel, hover the ",[34,82,83],{},"\u003Cimg>"," and read the tooltip (intrinsic dimensions) against the Computed tab's ",[34,86,87],{},"Rendered size",". If intrinsic width \u003C rendered width × DPR, you have a deficit.",[50,90,91,94,95,98,99,102],{},[53,92,93],{},"Check for CSS upscaling."," In Computed styles, see whether ",[34,96,97],{},"width","\u002F",[34,100,101],{},"height"," exceed the image's intrinsic dimensions, which stretches a small file across a large box.",[50,104,105,108,109,112,113,116],{},[53,106,107],{},"Inspect the CDN response."," In the Network tab, check the requested URL's transform parameters and the ",[34,110,111],{},"content-length","; an over-aggressive ",[34,114,115],{},"w="," or quality parameter can starve the file of detail.",[14,118,119,120,123,124,127],{},"If ",[34,121,122],{},"currentSrc"," points at a file whose width is smaller than ",[34,125,126],{},"rendered width × DPR",", jump to the matching root cause below.",[14,129,130],{},[131,132,139,140,139,144,139,148,139,158,139,165,139,174,139,180,139,185,139,193,139,198,139,203,139,206,139,209,139,211,139,216,139,219,139,222,139,227,139],"svg",{"xmlns":133,"viewBox":134,"width":135,"role":136,"ariaLabel":137,"style":138},"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","0 0 760 320","100%","img","Comparison of a pixel-deficient image upscaled on a 2x display versus a correctly sized 2x image","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:#001d3d"," ",[141,142,143],"title",{},"Pixel deficit on a high-DPI display",[145,146,147],"desc",{},"A 400px file stretched into a 400px CSS slot on a 2x screen lacks half its required pixels and renders blurry; an 800px file fills the same slot sharply.",[149,150],"rect",{"x":151,"y":151,"width":152,"height":153,"rx":154,"fill":155,"stroke":156,"style":157},"1","758","318","10","none","currentColor","stroke-opacity:0.18",[159,160,164],"text",{"x":161,"y":162,"fill":156,"style":163},"24","38","font-size:18px;font-weight:700","Why it looks blurry at devicePixelRatio 2",[149,166],{"x":167,"y":168,"width":169,"height":170,"rx":171,"fill":172,"stroke":172,"style":173},"40","64","320","180","6","#0466c8","fill-opacity:0.10",[159,175,179],{"x":176,"y":177,"fill":156,"style":178},"200","92","font-size:14px;font-weight:600;text-anchor:middle","Slot: 400 CSS px wide",[159,181,184],{"x":176,"y":182,"fill":156,"style":183},"116","font-size:13px;text-anchor:middle","Needs 800 device px (400 x 2)",[149,186],{"x":187,"y":188,"width":189,"height":187,"rx":190,"fill":191,"stroke":191,"style":192},"80","136","240","5","#b8860b","fill-opacity:0.20",[159,194,197],{"x":176,"y":195,"fill":156,"style":196},"172","font-size:13px;font-weight:700;text-anchor:middle","Served: 400px file",[159,199,202],{"x":176,"y":200,"fill":156,"style":201},"194","font-size:12px;text-anchor:middle","upscaled 2x -> BLURRY",[149,204],{"x":205,"y":168,"width":169,"height":170,"rx":171,"fill":172,"stroke":172,"style":173},"400",[159,207,179],{"x":208,"y":177,"fill":156,"style":178},"560",[159,210,184],{"x":208,"y":182,"fill":156,"style":183},[149,212],{"x":213,"y":188,"width":189,"height":187,"rx":190,"fill":214,"stroke":191,"style":215},"440","#ffc300","fill-opacity:0.28",[159,217,218],{"x":208,"y":195,"fill":156,"style":196},"Served: 800px file",[159,220,221],{"x":208,"y":200,"fill":156,"style":201},"1:1 device pixels -> SHARP",[159,223,226],{"x":161,"y":224,"fill":156,"style":225},"278","font-size:13px","Fix the supply (ship 2x pixels), not the symptom (CSS sharpening filters do not add detail).",[159,228,230],{"x":161,"y":229,"fill":156,"style":225},"302","Effective DPR can be 1.25\u002F1.5 under Windows scaling or zoom — test there too.",[39,232,234],{"id":233},"root-cause-analysis","Root Cause Analysis",[236,237,239],"h3",{"id":238},"cause-1-no-2x-candidate-in-srcset-or-no-srcset-at-all","Cause 1: No 2x candidate in srcset (or no srcset at all)",[14,241,242,243,246,247,250,251,254,255,257],{},"The most common cause is a candidate set that tops out at the 1x size. If your largest ",[34,244,245],{},"srcset"," entry is ",[34,248,249],{},"image-400.jpg 400w"," and the slot is 400px, a 2x screen needs 800px and there is nothing to give it, so the browser upscales the 400w file. The same happens with a bare ",[34,252,253],{},"src"," and no ",[34,256,245],{}," — there is exactly one file and it is the 1x file. The browser is doing the right thing; the supply is simply absent.",[236,259,261],{"id":260},"cause-2-a-sizes-attribute-that-under-reports-the-slot-width","Cause 2: A sizes attribute that under-reports the slot width",[14,263,264,265,268,269,272,273,276,277,280,281,284],{},"With ",[34,266,267],{},"w"," descriptors, the browser computes the required pixel width as ",[34,270,271],{},"resolved sizes length × DPR",". If ",[34,274,275],{},"sizes"," claims the slot is narrower than it really is — for example ",[34,278,279],{},"sizes=\"50vw\""," on an image that is actually full-bleed at ",[34,282,283],{},"100vw"," — the browser computes too small a requirement and picks a candidate that is sharp by its (wrong) math but blurry on screen. This is insidious because it only shows up at certain viewport widths and DPRs, passing every desktop-at-1x review.",[236,286,288],{"id":287},"cause-3-css-stretching-the-image-past-its-intrinsic-size","Cause 3: CSS stretching the image past its intrinsic size",[14,290,291,292,294,295,297,298,301],{},"Independent of ",[34,293,245],{},", a ",[34,296,97],{}," or layout rule can render a small image into a large box. A 200px avatar forced into a 96px → 192px hover state, or a thumbnail with ",[34,299,300],{},"width: 100%"," inside a wide container, gets stretched by the renderer. Here the file may be exactly 1x-correct for its intended size but is being displayed larger than it was ever provisioned for.",[236,303,305],{"id":304},"cause-4-cdn-downscaling-or-over-compression","Cause 4: CDN downscaling or over-compression",[14,307,308,309,312,313,315,316,319,320,322,323,326],{},"An image CDN transform can silently strip pixels. A URL parameter like ",[34,310,311],{},"?w=400"," caps the delivered width at 400 regardless of the ",[34,314,245],{}," entry's stated ",[34,317,318],{},"800w",", so the descriptor and the actual bytes disagree — the browser trusts ",[34,321,318],{}," but receives a 400px file. Aggressive quality parameters (e.g. ",[34,324,325],{},"q=40",") or a forced format conversion can also smear high-frequency detail into mush even at the correct dimensions.",[39,328,330],{"id":329},"step-by-step-resolution","Step-by-Step Resolution",[236,332,334],{"id":333},"fix-1-add-the-missing-high-density-candidates","Fix 1: Add the missing high-density candidates",[14,336,337,338,340],{},"Extend the candidate ladder so it reaches at least 2× the largest CSS slot, then let ",[34,339,245],{}," supply the extra pixels.",[342,343,348],"pre",{"className":344,"code":345,"language":346,"meta":347,"style":347},"language-html shiki shiki-themes github-light-high-contrast github-light-high-contrast github-light-high-contrast","\u003C!-- Before: tops out at 1x, blurry on retina -->\n\u003C!-- \u003Cimg src=\"avatar-200.jpg\" width=\"200\" height=\"200\" alt=\"Jordan Lee\"> -->\n\n\u003C!-- After: 1x + 2x + 3x candidates for a fixed 200px slot -->\n\u003Cimg\n  src=\"avatar-200.jpg\"\n  srcset=\"avatar-200.jpg 1x, avatar-400.jpg 2x, avatar-600.jpg 3x\"\n  width=\"200\" height=\"200\"\n  alt=\"Jordan Lee\" decoding=\"async\">\n\u003C!-- trade-off: adding a 3x candidate sharpens flagship phones but the 600px file\n     is ~2.25x the bytes of the 2x file. Skip the 3x entry if your analytics show\n     few DPR-3 devices — the perceptual gain over 2x is small and rarely worth it. -->\n","html","",[34,349,350,359,365,372,378,389,403,414,433,455,461,467],{"__ignoreMap":347},[351,352,355],"span",{"class":353,"line":354},"line",1,[351,356,358],{"class":357},"sIIH1","\u003C!-- Before: tops out at 1x, blurry on retina -->\n",[351,360,362],{"class":353,"line":361},2,[351,363,364],{"class":357},"\u003C!-- \u003Cimg src=\"avatar-200.jpg\" width=\"200\" height=\"200\" alt=\"Jordan Lee\"> -->\n",[351,366,368],{"class":353,"line":367},3,[351,369,371],{"emptyLinePlaceholder":370},true,"\n",[351,373,375],{"class":353,"line":374},4,[351,376,377],{"class":357},"\u003C!-- After: 1x + 2x + 3x candidates for a fixed 200px slot -->\n",[351,379,381,385],{"class":353,"line":380},5,[351,382,384],{"class":383},"syybb","\u003C",[351,386,388],{"class":387},"s-fAs","img\n",[351,390,392,396,399],{"class":353,"line":391},6,[351,393,395],{"class":394},"sf6mN","  src",[351,397,398],{"class":383},"=",[351,400,402],{"class":401},"s-_DF","\"avatar-200.jpg\"\n",[351,404,406,409,411],{"class":353,"line":405},7,[351,407,408],{"class":394},"  srcset",[351,410,398],{"class":383},[351,412,413],{"class":401},"\"avatar-200.jpg 1x, avatar-400.jpg 2x, avatar-600.jpg 3x\"\n",[351,415,417,420,422,425,428,430],{"class":353,"line":416},8,[351,418,419],{"class":394},"  width",[351,421,398],{"class":383},[351,423,424],{"class":401},"\"200\"",[351,426,427],{"class":394}," height",[351,429,398],{"class":383},[351,431,432],{"class":401},"\"200\"\n",[351,434,436,439,441,444,447,449,452],{"class":353,"line":435},9,[351,437,438],{"class":394},"  alt",[351,440,398],{"class":383},[351,442,443],{"class":401},"\"Jordan Lee\"",[351,445,446],{"class":394}," decoding",[351,448,398],{"class":383},[351,450,451],{"class":401},"\"async\"",[351,453,454],{"class":383},">\n",[351,456,458],{"class":353,"line":457},10,[351,459,460],{"class":357},"\u003C!-- trade-off: adding a 3x candidate sharpens flagship phones but the 600px file\n",[351,462,464],{"class":353,"line":463},11,[351,465,466],{"class":357},"     is ~2.25x the bytes of the 2x file. Skip the 3x entry if your analytics show\n",[351,468,470],{"class":353,"line":469},12,[351,471,472],{"class":357},"     few DPR-3 devices — the perceptual gain over 2x is small and rarely worth it. -->\n",[14,474,475,476,479],{},"Expected outcome: on a DPR-2 screen the browser selects ",[34,477,478],{},"avatar-400.jpg",", eliminating upscaling. Softness on retina avatars disappears; the visible \"fuzzy edge\" artifact resolves to crisp 1:1 device pixels.",[236,481,483],{"id":482},"fix-2-correct-the-sizes-attribute-to-match-the-real-slot","Fix 2: Correct the sizes attribute to match the real slot",[14,485,486,487,489,490,492],{},"Measure the rendered width at each breakpoint (Elements → Computed → ",[34,488,87],{},") and write ",[34,491,275],{}," to match exactly, including container max-widths and gutters.",[342,494,496],{"className":344,"code":495,"language":346,"meta":347,"style":347},"\u003Cimg\n  src=\"hero-800.jpg\"\n  srcset=\"hero-400.jpg 400w, hero-800.jpg 800w,\n          hero-1200.jpg 1200w, hero-1600.jpg 1600w\"\n  sizes=\"(max-width: 600px) 100vw,\n         (max-width: 1024px) 100vw,\n         1100px\"\n  width=\"1600\" height=\"900\"\n  alt=\"Conference keynote stage\" decoding=\"async\">\n\u003C!-- trade-off: an accurate sizes value can push large screens to the 1600w file,\n     adding bytes. That is correct for a hero, but for a non-critical below-fold\n     image consider capping the ladder so you never ship the very top candidate. -->\n",[34,497,498,504,513,522,527,537,542,547,563,580,585,590],{"__ignoreMap":347},[351,499,500,502],{"class":353,"line":354},[351,501,384],{"class":383},[351,503,388],{"class":387},[351,505,506,508,510],{"class":353,"line":361},[351,507,395],{"class":394},[351,509,398],{"class":383},[351,511,512],{"class":401},"\"hero-800.jpg\"\n",[351,514,515,517,519],{"class":353,"line":367},[351,516,408],{"class":394},[351,518,398],{"class":383},[351,520,521],{"class":401},"\"hero-400.jpg 400w, hero-800.jpg 800w,\n",[351,523,524],{"class":353,"line":374},[351,525,526],{"class":401},"          hero-1200.jpg 1200w, hero-1600.jpg 1600w\"\n",[351,528,529,532,534],{"class":353,"line":380},[351,530,531],{"class":394},"  sizes",[351,533,398],{"class":383},[351,535,536],{"class":401},"\"(max-width: 600px) 100vw,\n",[351,538,539],{"class":353,"line":391},[351,540,541],{"class":401},"         (max-width: 1024px) 100vw,\n",[351,543,544],{"class":353,"line":405},[351,545,546],{"class":401},"         1100px\"\n",[351,548,549,551,553,556,558,560],{"class":353,"line":416},[351,550,419],{"class":394},[351,552,398],{"class":383},[351,554,555],{"class":401},"\"1600\"",[351,557,427],{"class":394},[351,559,398],{"class":383},[351,561,562],{"class":401},"\"900\"\n",[351,564,565,567,569,572,574,576,578],{"class":353,"line":435},[351,566,438],{"class":394},[351,568,398],{"class":383},[351,570,571],{"class":401},"\"Conference keynote stage\"",[351,573,446],{"class":394},[351,575,398],{"class":383},[351,577,451],{"class":401},[351,579,454],{"class":383},[351,581,582],{"class":353,"line":457},[351,583,584],{"class":357},"\u003C!-- trade-off: an accurate sizes value can push large screens to the 1600w file,\n",[351,586,587],{"class":353,"line":463},[351,588,589],{"class":357},"     adding bytes. That is correct for a hero, but for a non-critical below-fold\n",[351,591,592],{"class":353,"line":469},[351,593,594],{"class":357},"     image consider capping the ladder so you never ship the very top candidate. -->\n",[14,596,597,598,601,602,604],{},"Expected outcome: the browser now computes the true requirement (",[34,599,600],{},"100vw × DPR"," on mobile) and selects the 800w–1600w candidate instead of an under-sized one. Blur that appeared only at specific widths is eliminated; ",[34,603,122],{}," should jump one or two ladder steps higher on the affected viewport.",[236,606,608],{"id":607},"fix-3-stop-css-from-upscaling-the-image","Fix 3: Stop CSS from upscaling the image",[14,610,611],{},"Cap the rendered size to the image's intrinsic dimensions, or provision a larger file for the box it actually occupies.",[342,613,617],{"className":614,"code":615,"language":616,"meta":347,"style":347},"language-css shiki shiki-themes github-light-high-contrast github-light-high-contrast github-light-high-contrast","\u002F* Never let the rendered box exceed what the file can fill at this DPR *\u002F\n.thumb {\n  width: 100%;\n  max-width: 192px;     \u002F* matches the intrinsic 192px asset *\u002F\n  height: auto;\n  image-rendering: auto; \u002F* do NOT use crisp-edges as a \"fix\": it sharpens pixels, not detail *\u002F\n}\n\u002F* trade-off: capping max-width keeps it crisp but prevents the image from filling\n   wider containers. If the design genuinely needs a larger render, the real fix is\n   a larger source file via srcset, not relaxing this cap. *\u002F\n","css",[34,618,619,624,632,649,668,680,695,700,705,710],{"__ignoreMap":347},[351,620,621],{"class":353,"line":354},[351,622,623],{"class":357},"\u002F* Never let the rendered box exceed what the file can fill at this DPR *\u002F\n",[351,625,626,629],{"class":353,"line":361},[351,627,628],{"class":394},".thumb",[351,630,631],{"class":383}," {\n",[351,633,634,636,639,642,646],{"class":353,"line":367},[351,635,419],{"class":394},[351,637,638],{"class":383},": ",[351,640,641],{"class":394},"100",[351,643,645],{"class":644},"sP5qI","%",[351,647,648],{"class":383},";\n",[351,650,651,654,656,659,662,665],{"class":353,"line":374},[351,652,653],{"class":394},"  max-width",[351,655,638],{"class":383},[351,657,658],{"class":394},"192",[351,660,661],{"class":644},"px",[351,663,664],{"class":383},";     ",[351,666,667],{"class":357},"\u002F* matches the intrinsic 192px asset *\u002F\n",[351,669,670,673,675,678],{"class":353,"line":380},[351,671,672],{"class":394},"  height",[351,674,638],{"class":383},[351,676,677],{"class":394},"auto",[351,679,648],{"class":383},[351,681,682,685,687,689,692],{"class":353,"line":391},[351,683,684],{"class":394},"  image-rendering",[351,686,638],{"class":383},[351,688,677],{"class":394},[351,690,691],{"class":383},"; ",[351,693,694],{"class":357},"\u002F* do NOT use crisp-edges as a \"fix\": it sharpens pixels, not detail *\u002F\n",[351,696,697],{"class":353,"line":405},[351,698,699],{"class":383},"}\n",[351,701,702],{"class":353,"line":416},[351,703,704],{"class":357},"\u002F* trade-off: capping max-width keeps it crisp but prevents the image from filling\n",[351,706,707],{"class":353,"line":435},[351,708,709],{"class":357},"   wider containers. If the design genuinely needs a larger render, the real fix is\n",[351,711,712],{"class":353,"line":457},[351,713,714],{"class":357},"   a larger source file via srcset, not relaxing this cap. *\u002F\n",[14,716,717],{},"Expected outcome: the image renders at or below its intrinsic size, so no stretching occurs. The fix removes the \"stretched and soft\" look on large viewports without adding any bytes.",[236,719,721],{"id":720},"fix-4-align-cdn-transform-parameters-with-the-descriptors","Fix 4: Align CDN transform parameters with the descriptors",[14,723,724,725,729],{},"Make the CDN deliver the width the descriptor promises, and back off over-aggressive compression. See ",[23,726,728],{"href":727},"\u002Fimage-media-optimization\u002Fimage-cdns-and-fetchpriority\u002F","image CDNs and fetchpriority"," for generating the ladder on demand.",[342,731,733],{"className":344,"code":732,"language":346,"meta":347,"style":347},"\u003C!-- Descriptor width and CDN w= parameter must agree -->\n\u003Cimg\n  src=\"https:\u002F\u002Fcdn.example.com\u002Fhero.jpg?w=800&q=75&fm=auto\"\n  srcset=\"https:\u002F\u002Fcdn.example.com\u002Fhero.jpg?w=400&q=75&fm=auto 400w,\n          https:\u002F\u002Fcdn.example.com\u002Fhero.jpg?w=800&q=75&fm=auto 800w,\n          https:\u002F\u002Fcdn.example.com\u002Fhero.jpg?w=1600&q=75&fm=auto 1600w\"\n  sizes=\"(max-width: 600px) 100vw, 800px\"\n  width=\"1600\" height=\"900\" alt=\"Mountain trail at dawn\" decoding=\"async\">\n\u003C!-- trade-off: q=75 balances sharpness and weight; pushing to q=90 restores fine\n     detail but inflates bytes 30-50% and risks the LCP budget. Raise quality only\n     for images where artifacts are visibly objectionable, not site-wide. -->\n",[34,734,735,740,746,755,764,769,774,783,814,819,824],{"__ignoreMap":347},[351,736,737],{"class":353,"line":354},[351,738,739],{"class":357},"\u003C!-- Descriptor width and CDN w= parameter must agree -->\n",[351,741,742,744],{"class":353,"line":361},[351,743,384],{"class":383},[351,745,388],{"class":387},[351,747,748,750,752],{"class":353,"line":367},[351,749,395],{"class":394},[351,751,398],{"class":383},[351,753,754],{"class":401},"\"https:\u002F\u002Fcdn.example.com\u002Fhero.jpg?w=800&q=75&fm=auto\"\n",[351,756,757,759,761],{"class":353,"line":374},[351,758,408],{"class":394},[351,760,398],{"class":383},[351,762,763],{"class":401},"\"https:\u002F\u002Fcdn.example.com\u002Fhero.jpg?w=400&q=75&fm=auto 400w,\n",[351,765,766],{"class":353,"line":380},[351,767,768],{"class":401},"          https:\u002F\u002Fcdn.example.com\u002Fhero.jpg?w=800&q=75&fm=auto 800w,\n",[351,770,771],{"class":353,"line":391},[351,772,773],{"class":401},"          https:\u002F\u002Fcdn.example.com\u002Fhero.jpg?w=1600&q=75&fm=auto 1600w\"\n",[351,775,776,778,780],{"class":353,"line":405},[351,777,531],{"class":394},[351,779,398],{"class":383},[351,781,782],{"class":401},"\"(max-width: 600px) 100vw, 800px\"\n",[351,784,785,787,789,791,793,795,798,801,803,806,808,810,812],{"class":353,"line":416},[351,786,419],{"class":394},[351,788,398],{"class":383},[351,790,555],{"class":401},[351,792,427],{"class":394},[351,794,398],{"class":383},[351,796,797],{"class":401},"\"900\"",[351,799,800],{"class":394}," alt",[351,802,398],{"class":383},[351,804,805],{"class":401},"\"Mountain trail at dawn\"",[351,807,446],{"class":394},[351,809,398],{"class":383},[351,811,451],{"class":401},[351,813,454],{"class":383},[351,815,816],{"class":353,"line":435},[351,817,818],{"class":357},"\u003C!-- trade-off: q=75 balances sharpness and weight; pushing to q=90 restores fine\n",[351,820,821],{"class":353,"line":457},[351,822,823],{"class":357},"     detail but inflates bytes 30-50% and risks the LCP budget. Raise quality only\n",[351,825,826],{"class":353,"line":463},[351,827,828],{"class":357},"     for images where artifacts are visibly objectionable, not site-wide. -->\n",[14,830,831],{},"Expected outcome: the bytes delivered match the descriptor, so the browser's selection is honored and high-frequency detail survives compression. Mush from over-compression clears; the 800w slot now receives a true 800px file.",[39,833,835],{"id":834},"verification","Verification",[14,837,838,839,842,843,846],{},"Confirm the fix the same way you diagnosed it. Reload with cache disabled (the browser caches a chosen candidate per DPR, so a stale cache hides the change), then re-run ",[34,840,841],{},"document.querySelector('img').currentSrc"," and verify it points at a file whose width is ",[34,844,845],{},"≥ rendered width × devicePixelRatio",". Toggle DevTools device emulation to a DPR-3 profile and repeat — the selected candidate should step up accordingly. Visually, the softness should be gone at 100% zoom on the high-DPI display where it first appeared.",[14,848,849,850,854,855,858,859,861],{},"For field validation, watch the metric this most affects. The hero is usually the ",[23,851,853],{"href":852},"\u002Fcore-web-vitals-measurement\u002Fmeasuring-lcp-with-chrome-devtools\u002F","Largest Contentful Paint"," element, and over-correcting (shipping a 1600w file to every phone) trades blur for a slower LCP, so confirm both the sharpness and the byte budget moved in the right direction. In CI, assert Lighthouse's ",[34,856,857],{},"uses-responsive-images"," audit plus an LCP budget of 2500ms so a future regression — a deleted ",[34,860,275],{}," prop or a reverted candidate — fails the build before it ships.",[39,863,865],{"id":864},"related","Related",[867,868,869,878,884,891,897],"ul",{},[50,870,871,874,875,877],{},[23,872,873],{"href":25},"Responsive images with srcset and sizes"," — the full workflow for building the candidate ladder and writing a correct ",[34,876,275],{}," attribute.",[50,879,880,883],{},[23,881,882],{"href":727},"Image CDNs and fetchpriority"," — generate every density on demand and prioritize the LCP image's fetch.",[50,885,886,890],{},[23,887,889],{"href":888},"\u002Fimage-media-optimization\u002Fserving-avif-and-webp-with-fallbacks\u002F","Serving AVIF and WebP with fallbacks"," — recover the bytes that higher-density candidates add by switching to a denser codec.",[50,892,893,896],{},[23,894,895],{"href":852},"Measuring LCP with Chrome DevTools"," — make sure sharpening the hero did not regress the loading metric.",[50,898,899,901],{},[23,900,31],{"href":30}," — the parent guide tying media weight to Core Web Vitals.",[903,904,906],"script",{"type":905},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"Fix blurry images on high-DPI displays\",\n  \"description\": \"Diagnose and resolve soft images on retina and high-DPI screens caused by missing density candidates, wrong sizes values, CSS upscaling, and CDN downscaling.\",\n  \"step\": [\n    { \"@type\": \"HowToStep\", \"position\": 1, \"name\": \"Add missing high-density candidates\", \"text\": \"Extend the srcset ladder to at least 2x the largest CSS slot width with 2x and 3x candidates.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002Ffixing-blurry-images-on-high-dpi-displays\u002F#fix-1-add-the-missing-high-density-candidates\" },\n    { \"@type\": \"HowToStep\", \"position\": 2, \"name\": \"Correct the sizes attribute\", \"text\": \"Measure the rendered slot width per breakpoint and write sizes to match it exactly.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002Ffixing-blurry-images-on-high-dpi-displays\u002F#fix-2-correct-the-sizes-attribute-to-match-the-real-slot\" },\n    { \"@type\": \"HowToStep\", \"position\": 3, \"name\": \"Stop CSS upscaling\", \"text\": \"Cap rendered size to the image's intrinsic dimensions or provision a larger source file.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002Ffixing-blurry-images-on-high-dpi-displays\u002F#fix-3-stop-css-from-upscaling-the-image\" },\n    { \"@type\": \"HowToStep\", \"position\": 4, \"name\": \"Align CDN transforms\", \"text\": \"Make CDN width parameters match the srcset descriptors and back off over-aggressive compression.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002Ffixing-blurry-images-on-high-dpi-displays\u002F#fix-4-align-cdn-transform-parameters-with-the-descriptors\" }\n  ]\n}\n",[903,908,909],{"type":905},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"Fixing Blurry Images on High-DPI Displays\",\n  \"description\": \"A root-cause diagnosis and fix sequence for soft, blurry images on retina and high-DPI screens, covering density candidates, sizes, CSS upscaling, and CDN downscaling.\",\n  \"datePublished\": \"2026-06-18\",\n  \"dateModified\": \"2026-06-18\",\n  \"author\": { \"@type\": \"Organization\", \"name\": \"frontend-performance.com\" },\n  \"publisher\": { \"@type\": \"Organization\", \"name\": \"frontend-performance.com\" },\n  \"mainEntityOfPage\": { \"@type\": \"WebPage\", \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002Ffixing-blurry-images-on-high-dpi-displays\u002F\" }\n}\n",[903,911,912],{"type":905},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    { \"@type\": \"ListItem\", \"position\": 1, \"name\": \"Home\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\" },\n    { \"@type\": \"ListItem\", \"position\": 2, \"name\": \"Image & Media Optimization\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002F\" },\n    { \"@type\": \"ListItem\", \"position\": 3, \"name\": \"Responsive Images with srcset and sizes\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002F\" },\n    { \"@type\": \"ListItem\", \"position\": 4, \"name\": \"Fixing Blurry Images on High-DPI Displays\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002Ffixing-blurry-images-on-high-dpi-displays\u002F\" }\n  ]\n}\n",[914,915,916],"style",{},"html pre.shiki code .sIIH1, html code.shiki .sIIH1{--shiki-default:#66707B;--shiki-dark:#66707B;--shiki-light:#66707B}html pre.shiki code .syybb, html code.shiki .syybb{--shiki-default:#0E1116;--shiki-dark:#0E1116;--shiki-light:#0E1116}html pre.shiki code .s-fAs, html code.shiki .s-fAs{--shiki-default:#024C1A;--shiki-dark:#024C1A;--shiki-light:#024C1A}html pre.shiki code .sf6mN, html code.shiki .sf6mN{--shiki-default:#023B95;--shiki-dark:#023B95;--shiki-light:#023B95}html pre.shiki code .s-_DF, html code.shiki .s-_DF{--shiki-default:#032563;--shiki-dark:#032563;--shiki-light:#032563}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html pre.shiki code .sP5qI, html code.shiki .sP5qI{--shiki-default:#A0111F;--shiki-dark:#A0111F;--shiki-light:#A0111F}",{"title":347,"searchDepth":361,"depth":361,"links":918},[919,920,926,932,933],{"id":41,"depth":361,"text":42},{"id":233,"depth":361,"text":234,"children":921},[922,923,924,925],{"id":238,"depth":367,"text":239},{"id":260,"depth":367,"text":261},{"id":287,"depth":367,"text":288},{"id":304,"depth":367,"text":305},{"id":329,"depth":361,"text":330,"children":927},[928,929,930,931],{"id":333,"depth":367,"text":334},{"id":482,"depth":367,"text":483},{"id":607,"depth":367,"text":608},{"id":720,"depth":367,"text":721},{"id":834,"depth":361,"text":835},{"id":864,"depth":361,"text":865},"A root-cause diagnosis and fix sequence for soft, blurry images on retina and high-DPI screens.","md",{"slug":12,"type":937,"breadcrumb":938,"datePublished":946,"dateModified":946},"long_tail",[939,941,942,944],{"name":940,"url":98},"Home",{"name":31,"url":30},{"name":943,"url":25},"Responsive Images with srcset and sizes",{"name":5,"url":945},"\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002Ffixing-blurry-images-on-high-dpi-displays\u002F","2026-06-18","\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002Ffixing-blurry-images-on-high-dpi-displays",{"title":949,"description":950},"Fix Blurry Images on High-DPI Displays","Images look soft on retina and high-DPI screens? Diagnose missing 2x descriptors, wrong sizes, CSS upscaling, and CDN downscaling, with paste-ready fixes.","image-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002Ffixing-blurry-images-on-high-dpi-displays\u002Findex","YQv0aaTB1vk-N1TySrK9yJv1UdJMp76kQoKxG-W6Kpc",[954,957],{"title":943,"path":955,"stem":956,"children":-1},"\u002Fimage-media-optimization\u002Fresponsive-images-with-srcset-and-sizes","image-media-optimization\u002Fresponsive-images-with-srcset-and-sizes\u002Findex",{"title":958,"path":959,"stem":960,"children":-1},"Serving AVIF and WebP with Fallbacks","\u002Fimage-media-optimization\u002Fserving-avif-and-webp-with-fallbacks","image-media-optimization\u002Fserving-avif-and-webp-with-fallbacks\u002Findex",1782237171329]