2026-08-11 · 4 min read
Images on mobile: density, data, and the memory ceiling
How device pixel ratio actually works, why 3x is a waste, what images cost on a metered connection, and the memory limits that crash tabs on phones.
Mobile is where image decisions have consequences. Desktop broadband forgives an oversized hero image; a phone on a congested cell connection does not, and a phone with 3 GB of RAM will simply discard your tab if you push it hard enough.
CSS pixels are not device pixels
A phone advertising a 1170 × 2532 screen presents itself to CSS as roughly 390 × 844. The ratio between them — device pixel ratio, or DPR — is 3 on that hardware.
This is why a "full width" image on that phone is 390 CSS pixels wide but needs more than 390 image pixels to look sharp. It is also why an image that looks crisp in a desktop browser at 100% zoom can look soft on a phone: the phone is packing three device pixels into each CSS pixel and has nothing to fill them with.
Why 2× is the sensible ceiling
The obvious conclusion is to export at 3× on 3× devices. In practice this is not worth it.
Perceived sharpness improves steeply from 1× to 2× and then flattens. Between 2× and 3× the difference is difficult to see at normal viewing distance, particularly on photographic content where the eye is not looking for pixel edges.
The cost is not linear. Going from 2× to 3× is 2.25× the pixel count, and therefore roughly 2.25× the bytes and 2.25× the decode memory. That is a large price for an improvement most viewers cannot detect on a photograph.
For text rendered as an image or for fine line art, 3× can be justified. For photographs, cap at 2×.
Data has a cost to real people
A 2 MB hero image is trivial on wifi. On a metered plan in a country where mobile data is expensive, it is a measurable amount of someone's money, spent without their agreement, on an image they did not ask for.
There is also the question of when it arrives. On a slow connection the image is not merely heavier — it is late, blocking the point at which the page becomes useful.
The sizes attribute is what prevents this. It tells the browser how wide the image will be in the layout, so it can choose the smallest adequate candidate from srcset rather than the largest available:
<img
src="hero-1200.webp"
srcset="hero-600.webp 600w, hero-1200.webp 1200w, hero-1800.webp 1800w"
sizes="(max-width: 700px) 100vw, 1200px"
width="1200" height="675"
alt="Fishing boats at the harbour wall"
fetchpriority="high">
Without sizes, browsers assume the image is full viewport width and download accordingly, which on a small screen means fetching a file three times larger than the layout will use.
The memory ceiling nobody mentions
File size is what you measure. Decoded size is what crashes the tab.
A JPEG is compressed on disk and uncompressed in memory, at roughly four bytes per pixel. A 4000 × 3000 photograph is a 3 MB file and about 48 MB of RAM once decoded. Hold twenty of those simultaneously and you are asking for a gigabyte of memory on a device that may not have it available.
Mobile browsers respond to memory pressure by discarding the tab, usually without warning and often without a useful error. This is the mechanism behind "the page just reloaded itself" on a gallery-heavy site.
Implications:
- Do not load dozens of full-resolution images into one page.
- Lazy-load anything below the fold, so it is never decoded until needed.
- Cap upload dimensions. There is no benefit to a 6000-pixel image on any phone screen.
- If you build tools that process images in the browser, decode in small concurrent batches and release bitmaps immediately — the same ceiling applies to processing, and it arrives faster than the CPU limit does.
Art direction, when cropping is not enough
Sometimes a landscape hero simply does not work at 390 pixels wide. The subject becomes too small to read, and no amount of scaling fixes a composition problem.
<picture> with media queries lets you serve a differently cropped file rather than a differently sized one:
<picture>
<source media="(max-width: 700px)" srcset="hero-portrait.webp">
<img src="hero-wide.webp" width="1600" height="900" alt="Fishing boats at the harbour wall">
</picture>
This is more work than responsive sizing and should be reserved for images that carry real weight — a homepage hero, a campaign banner. Producing the two crops is a batch operation with two different aspect ratios and focal points over the same source.
Testing on the actual constraint
Device emulation in desktop developer tools shows you layout. It does not show you the network or the memory ceiling, which are the two things that actually break on phones.
Throttle the connection to something pessimistic — slow 3G is a reasonable worst case — and watch when the images arrive relative to the text. Then test on a genuinely mid-range device rather than a current flagship, because the flagship has the memory headroom to hide problems that most of your audience will hit.
The short version
Export at 2×, never 3× for photographs. Always set sizes alongside srcset. Lazy-load below the fold and eagerly load above it. Set width and height so nothing reflows. And remember that the file size in your build report is not the number that determines whether a phone can hold the page — the decoded pixel count is.