Image Load Lab

Guides Responsive Delivery Tested Oct 17, 2026 20 photos · DPR 1, 2, 3

High-DPI Images: How Many Pixels Are Worth the Bytes?

In an 800-pixel slot on a 2× screen, the 2× file scored 70.1 while a 1.5× file fell to 28.8 for 40 % fewer bytes. Doubling the width cost 3.3 times the bytes.

Phones and many laptops have screens with two or three device pixels per CSS pixel. How many pixels should an image have for a slot of a given width, and what does each step up cost in bytes and in visible detail? We prepared four candidate widths of 20 photographs for an 800 CSS-pixel slot, scaled each one exactly as a browser would for screens of 1×, 2× and 3× density, and measured both the bytes and the quality that reaches the screen.

The test

Each of our 20 CC0 photographs was cropped to 16:9 and resized from the camera original to a 2400 × 1350 lossless reference. From it we made four candidates for an 800 CSS-pixel slot: 800, 1200, 1600 and 2400 pixels wide (1×, 1.5×, 2× and 3×), all WebP at quality 75. Then, for each screen density, every candidate was resized with Lanczos3 to the slot’s physical width (800, 1600 or 2400 pixels), which is what the browser does when it draws the image, and compared with the reference at that same physical size using SSIMULACRA2 and DSSIM (see the methodology). That is 240 measurements.

What did not work first: our first run used a 3:2 crop. At 800 and 1600 pixels wide, a 3:2 image is 533.33 and 1066.67 pixels high, so every size was rounded and drifted by a fraction of a pixel against the reference. SSIMULACRA2 punished that misalignment so hard that a sharper candidate scored worse than a smaller one. We discarded that run and switched to 16:9, where every height is a whole number (450, 675, 900, 1350 pixels).

Results

Line chart: at 2x density the 800 and 1200 pixel candidates score 19 and 29, the 1600 candidate 70; at 3x only the 2400 candidate reaches 69
Quality of each candidate after the browser scales it to an 800 CSS-pixel slot at 1×, 2× and 3× density. Chart generated from the linked CSV.
800 CSS-pixel slot; median of 20 photos (SSIMULACRA2)
Candidate Median KB Score at 1× Score at 2× Score at 3×
800 px 41.6 71.5 19.2 5.2
1200 px 83.0 76.1 28.8 23.4
1600 px 137.7 78.6 70.1 30.1
2400 px 269.7 82.3 76.2 69.2

Bytes grow much faster than width. The median candidate weighed 41.6 KB at 800 pixels, 83.0 KB at 1200, 137.7 KB at 1600 and 269.7 KB at 2400. Twice the width cost 3.3 times the bytes, and three times the width cost 6.5 times.

On a 2× screen only the 2× file held up. Shown in an 800-pixel slot at 2× density, the 1600-pixel candidate scored a median 70.1, the “high quality” level. The 1200-pixel candidate, which saves 40 % of the bytes, fell to 28.8, and the 800-pixel file to 19.2. Upscaling cannot invent detail, and SSIMULACRA2 counts every lost detail that the screen could have shown.

On a 3× screen, capping at 2× is a real trade. Only the 2400-pixel candidate (269.7 KB) reached 69.2. The common practice of capping at 2× (the 1600-pixel file, 49 % fewer bytes) scored 30.1 at 3×.

On a 1× screen, bigger files are mostly wasted. The 800-pixel file scored 71.5; the 1600-pixel file, downscaled by the browser, scored 78.6 for 3.3 times the bytes.

Four crops of the street sign as a 2x screen would show them: from a 1600 pixel reference, an upscaled 800 pixel file, an upscaled 1200 pixel file and a native 1600 pixel file
Device-pixel crops of what a 2× screen displays for each candidate in an 800 CSS-pixel slot. Photo: Bernard Gagnon, CC0. Select the image to open the lossless original.

The crops show what the numbers mean on a 2× screen: the upscaled 800 and 1200-pixel files are visibly softer on the carved letters than the native 1600-pixel file.

How to read these scores

SSIMULACRA2 compares images pixel by pixel at the screen’s physical resolution, which is stricter than reading a phone at arm’s length: a 1.5× image that scores 29 here can still look acceptable to many readers. Use the numbers to compare options, not as a pass or fail. What the data does show clearly is the shape of the trade: for each density step, bytes roughly double or triple, and serving less than the screen’s density visibly reduces sharpness.

Practical conclusion

  • Offer candidates at 1× and 2× of each layout slot through srcset, and let the browser choose (see our srcset test).
  • Add a 3× candidate only for images where fine detail matters (product photos, text in images, diagrams). For decorative photos, capping at 2× saved about half the bytes in our run.
  • Never send 2× files to everyone: on 1× screens they cost 3.3 times the bytes for a modest gain.
  • Avoid sizes that do not divide evenly when you compare quality between sizes; rounding alone can skew a metric.

How this connects to our other tests

These results set the bar for the other responsive-image tests. Our srcset test showed Chrome choosing the 1600-pixel candidate on a 3× phone: by the numbers here, a 2× candidate on a 3× screen keeps file sizes about half of a full 3× file but loses visible detail. The trade is easier to accept for photographs than for screenshots or images with text, where our WebP quality test also found that screenshots need higher settings. If you decide to cap at 2×, do it through the candidate list in srcset, not by lowering the quality of every file.

Repeat this test on your own images

You can reproduce the core of this test with one photo. Make a large lossless reference, then a 1× and a 2× candidate for your slot, and simulate the 2× screen by resizing both to the 2× width:

// Node.js with sharp
await sharp('ref.png').resize(1600, 900).png().toFile('ref1600.png');
await sharp('ref.png').resize(800, 450).webp({ quality: 75 }).toFile('c800.webp');
await sharp('c800.webp').resize(1600, 900).png().toFile('c800-at-2x.png');
// then: ssimulacra2 ref1600.png c800-at-2x.png

Keep the aspect ratio such that every height is a whole number, or the comparison will measure rounding instead of sharpness, as our first run did. Looking at the two files side by side at 100 % on your own screen is the most useful check of all. Our script run_alpha_icons_dpr.mjs (the dpr step) runs this for 20 photos and three densities.

Limitations

One slot width, one encoder setting (WebP quality 75) and Lanczos3 resizing as an approximation of the browser’s scaler; Chrome’s own resampling can differ slightly. Perceived sharpness also depends on viewing distance and the screen itself, which no metric captures.

Data and files

Download dpr.csv (240 rows, 10.9 KB) · Method: testing methodology. Photos: image credits.