Skip to content

Responsive Images

JessicaOPRD edited this page Nov 18, 2022 · 25 revisions

All about the pixels

Device (physical) pixels are determined by the physical screen. You cannot change or manipulate this without acquiring a new device. This is not to be confused with logical pixels, which are more of a relative coordinate system.

🔗 Use density descriptors — Excellent write-up/lab explaining device, physical, and CSS pixels

🔗 Mydevice.io — a "what is my" resource for checking a screen, also includes list of common devices

Some definitions — because this is confusing

Term Also called Description
Device pixel "Physical" pixel, hardware-based pixel Physical measurement — The smallest dot of color that can be displayed on a device.
Logical pixel Device-independent pixel Abstracted measurement — Relative pixel coordinate system.
CSS pixel Physical measurement — 1/96th of an inch, as defined by CSS specification.
Pixel density Screen density, display density, pixel resolution, measured in ppi (pixels per inch) Physical measurement/area — Density of device pixels in a given physical area (inch). Historically 96 ppi on external monitors, CRTs, etc. Apple popularized 192 ppi in 2010.
Device pixel ratio (DPR) dppx Ratio — Relationship between device pixels and logical pixels (1:1 or 1x, 2:1, or 2x). This is a mapping system and may be difficult to know precisely without asking the manufacturer. JavaScript supports window.devicePixelRatio — "Read-only property that returns the ratio of the (vertical) size of one physical pixel on the current display device to the size of one CSS pixel." Source
Pixel aspect ratio Ratio — Relationship between pixel width and pixel height — essentially the pixel shape.

Common densities

Density ppi (minimum) Devices Notes
1x 96 ppi Many laptops and external monitors, CRT monitors Historically introduced in 1980s as Windows default
2x 192 ppi Older iPhones, iPad, Macbook Popularized by Apple with "retina" display in 2010
3x 288 ppi Newer smartphones

There will be ratios in between as well.

To prevent upscaling on devices with higher pixel density

A 1x image displayed on a 2x device will appear upscaled. To prevent this, you must serve an image that is at least 2x the original size. Where this gets weird:

1 < dppx < 1.5

2 < dppx < 2.5

I believe this is because a ratio of, say, 1.25 will round down and serve the 1x image. This results in upscaling! I have a 1.25 device and it notoriously upscales and experiences sub-pixel rendering issues more than any of my other devices. I suspect this will often be a low- to mid-range laptop.

Using HTML attributes to pull appropriate source files

This seemingly simple topic confuses every time. A good and recent primer is A Guide to Responsive Images Syntax in HTML. Another older breakdown is Responsive Images 101.

This is tricky enough that many popular CDNs bake responsive imagery into their service. It is also not difficult to create a basic image transformation API yourself (width, height, crop, quality). However the issue of determining which images to request at all remains. Despite good intentions browsers have not determined a great way to handle this problem, as the necessary (and oft omitted) sizes attribute is tricky for humans to figure out.

Problems and Solutions

Problem Type Solution
Want to serve appropriate full-width image – same width (=100vw) as known layout breakpoints (viewport dependent) Image size 📏 Use <img> srcset, no need to use sizes
Want to serve appropriate in-column image – less than width (<100vw) of known layout breakpoints (layout dependent) Image size 📏 Use <img> srcset, will need to use sizes to guesstimate or use automated tooling; if sizes is omitted image will be served full-width to device (more of an issue for larger formats); Behance simplifies by using sizes to enforce 100vw up to the highest breakpoint (see below)

Example sizes strategies

Allow 100vw images up to max breakpoint

As seen on Behance. Notice some URI segment params in their own API. Interesting that the optimization is set to presumably 100% on larger display images (assuming better Internet?). Behance users are also more likely to be technologically privileged. This is a decent simplified approach.

<img
  src="https://mir-s3-cdn-cf.behance.net/project_modules/fs/image.png"
  srcset="
    https://mir-s3-cdn-cf.behance.net/project_modules/disp/image.png 600w,
    https://mir-s3-cdn-cf.behance.net/project_modules/max_1200/image.png 1200w,
    https://mir-s3-cdn-cf.behance.net/project_modules/1400_opt_1/image.png 1400w,
    https://mir-s3-cdn-cf.behance.net/project_modules/fs/image.png 1920w,
  "
  sizes="(max-width: 1920px) 100vw, 1920px"
>

Isolated examples

Note many examples are created to demonstrate shortcomings.

Title Method Notes
2x Responsive Example Requests image from Cloudinary API using dynamic params c_scale,f_auto,q_auto,w_600 in GET request Offers savings for 1x devices, but the image is still way too large relative to use

Sample Dynamic/Transform Media APIs

I collect these examples to get a sense of conventions used for APIs that handle responsive imagery. My first time using and becoming familiar with letter-based short-hand was with server-side image processing libraries and frameworks. I'm assuming the convention starts there.

https://res.cloudinary.com/demo/image/upload/c_scale,w_0.5/sample.jpg

  • 1-2 letter qualifier params included as URL segment
  • Comma-separated in case of multiple qualifiers: c_scale,f_auto,q_auto,w_600
Qualifier key Meaning Example Notes
ar Aspect ratio ar_1.5
c Crop mode c_crop,h_150,w_200 ⚪ Various crop "modes" (fill, fill_pad, fit, scale, etc) ⚪ Must be used in combination with 2 of 3: height, width, aspect ratio
g Gravity (origin) c_crop,g_north_west,h_150,w_150
h Height c_scale,h_150
q Quality q_100
w Width c_scale,w_100

https://assets.imgix.net/examples/leaves.jpg?w=400&dl=medium.jpg

  • Query parameters attached to end of normal image GET request
  • Many param names are 1-3 characters only, but not all
  • Unsure how this is achieved, but guessing server config and/or redirecting
  • Very intuitive
Param Meaning Example Notes
ar Aspect ratio ar=9:1&fit=crop
dl Download dl, dl=medium ⚪ Supports medium, small, and empty value for original
dpr Device pixel ratio dpr=2
fit and crop Resize/crop mode fit=clip&w=100&h=100, fit=crop&crop=left,bottom ⚪ Many modes (crop, fill, facearea, fillmax, etc) ⚪ Must be used in combination with: width, height ⚪ crop seems to really control origin
h Height h=150
q Quality q=75
w Width w=150

/images/apples.jpg?nf_resize=fit&w=300&h=400

  • Query parameters attached to end of normal image GET request
  • Width and height names represented by letter, other params longer names
  • More basic options
Param Meaning Example Notes
resize Resize/crop mode nf_resize=fit&w=300, ?nf_resize=smartcrop&w=300&h=300 ⚪ Limited modes (fit and smartcrop), but uses smartcrop.js ⚪ Depending on mode need either one dimension or both (crop)
h Height h=150
w Width w=150

Explore Netlify's demo.

image/fit=contain,width=320/assets/hero.jpg

  • Full name qualifier params AND in many cases 1-letter alternative param
  • = to define value
  • Comma-separated in case of multiple params
  • Intuitive fit/crop modes
  • Resizes/crops are cached for limited time?
  • Lots of good information/overview about a transform API in the documentation
Qualifier key Meaning Example Notes
fit Crop mode fit=scale-down,width=1920 ⚪ Various "modes" (scale-down, contain, cover, crop, pad)
gravity, g Gravity gravity=left ⚪ Weighted side, more abstract than origin, but can specify XxY point
height, h Height height=150
quality, q Quality width=80,quality=75
width, w Width width=80

Testing what this does?

Clone this wiki locally