Why high-DPI images still trip up developers

High pixel density displays are now the default rather than the exception, yet serving crisp images to them still feels like a chore. Many of the popular solutions—the standards-track srcset attribute, the image-set CSS property, or the <picture> element—require verbose markup or aren't reliably available yet. JavaScript and server-side approaches carry their own baggage.

For the purposes of this discussion, consider any device where window.devicePixelRatio is greater than 1 to be high DPI, because it means CSS pixels don't map one-to-one to device pixels and images are being scaled up. The good news: you can avoid most of the complexity by following a few straightforward rules:

  • Prefer CSS/SVG over raster imagery where possible.
  • Serve images optimized for high-density displays by default.
  • Use PNGs for simple drawings and pixel art—logos, for example.
  • Use compressed JPEGs for images with a wide range of colors, like photos.
  • Always set explicit width and height on image elements.

Logos and simple graphics: stick with PNG

Many small images can be eliminated entirely by using CSS features such as border-radius or web fonts instead of imaged text. But for something like a logo, an image is often unavoidable. A 256×256 PNG logo displayed at its natural size will show visible aliasing on diagonal and curved lines when viewed on a Retina display, making it look chunky next to crisply rendered text.

Serving a higher-resolution version is the obvious fix, but the file format matters. Saving a logo as a JPEG, even a highly compressed one, tends to introduce artifacts: banding on gradients, speckles on white backgrounds, and messy lines. A better approach is to use a 2x PNG and scale it down via explicit dimensions in the markup:

<img src="chrome-logo_2x.png" width="256" height="256" alt="Chrome logo">

Specifying width and height is necessary because the natural size of the image is now 512px, and it helps the rendering engine avoid extra layout work. Be aware that a 2x PNG can be significantly larger than its 1x counterpart. For a logo, that tradeoff is usually acceptable.

One worthwhile optimization is converting a 24-bit PNG to an 8-bit paletted version using a tool like pngquant. For images with a limited color count—the Chrome logo included—this can cut file size dramatically (from 83 kB to 13 kB in the example) with only minor banding.

Photos and gradients: JPEG compression helps

For images with many colors, like photographs, JPEG remains the right format. A surprising finding from testing: a highly compressed 2x JPEG can be smaller in file size and look better than an uncompressed 1x version of the same image. Doubling the dimensions and cranking up the compression can yield a file that beats the original on both counts.

Testing the tradeoff

To get a clearer picture of how compression level, dimensions, and visual quality interact, a side-by-side comparison tool was built, similar to Lightroom's compare view. It displays 1x and 2x versions of images, allows zooming into any section, and supports both JPEG and WebP formats with adjustable quality settings. You can experiment with it directly; the goal isn't to find a universal setting, but to help you develop an intuition for the tradeoffs that fit your use case.

What the tests showed

Several patterns emerged from the tests:

  • Images at quality=30 dpr=2x generally beat quality=90 dpr=1x versions in perceived detail, with comparable file sizes.
  • Highly compressed images (quality<30) with gradients suffer from color banding, regardless of scale.
  • WebP looks noticeably cleaner than JPEG at equivalent compression levels, shows less banding, and produces more compact files.

What this guide deliberately leaves out

Serving high-DPI images is only half of the responsive image problem. Sometimes you also need to serve entirely different content based on viewport size—a cropped headshot on a phone versus a wider shot on a laptop, for instance. That's a separate "art direction" concern, solvable with media queries, background images, JavaScript, image-set, or server-side logic, and is covered elsewhere.

There are also open questions worth keeping in mind: what are the decoding performance penalties for highly compressed images, and what's the cost of resizing a 2x image down when it lands on a 1x display?

For the common case, the approach is simple: use CSS and SVG when you can, reach for PNG when you need simple graphics, and use compressed JPEGs for color-rich imagery. The main work is generating 2x assets and sizing them properly in the DOM. Your markup stays mostly unchanged, and you sidestep the complexity of polyfills and half-baked browser features. For a deeper look at related techniques, Scott Jehl's article on responsive image compression is worth reading.