Getting the output resolution of a screen from a web browser is not as simple as it might sound.

The old trick of setting a div as "1in" wide and then counting pixels only ever results in 96dpi (dpi is actually "dots per CSS inch" - and a "CSS inch" is defined as 96px - defeating the purpose of the exercise) and there's no direct access via Javascript to the dpi of a screen, unless you reach back to some non-standard IE-only properties.

We've been able to create resolution dependent media queries for nearly 10 years now - and Stack Overflow is still littered with bad ideas to inaccurately measure the dpi of a viewport.

The dream of dpi

In all modern browsers except Safari, you can use a min/max-resolution media query with a dpi value. If we construct a loop starting at 650dpi and work down until we have a media query that triggers media.match, we can quickly determine the actual output dpi of a screen - it's not the most perf minded function, but there aren't a lot of other options open to us.

Having a real-world output dpi measurement could make accessibility a whole lot more predictable. Knowing how big a font will really appear on a device - or how big a touch target is on low and high dpi screens - would be great. But it isn't a demand that has exactly held web sites back as most devices are within a CSS pixel density range that makes it a design problem rather than a dev problem, and dpi doesn't have many other benefits to developing a website.

But we still have dppx

In any case, iOS doesn't support min/max-resolution media queries or dpi units, it supports -webkit-min-device-pixel-ratio and the dppx unit instead. It's a shame we can't determine dpi on iOS - that could have differentiated between an iPad and an iPad mini - the holy grail of device sniffing. Anyway, dppx still gives a lot of value. And almost all browsers support the JS property window.devicePixelRatio, so we can access screen dppx figure directly.

Using dppx for styling purposes would be madness, as we should just be using the method built for this purpose - resolution media queries. But knowing the dppx gives other possibilities inside your site/app, including differentiating (or reporting analytics on) Android phones.

Samsungs, Sonys, LGs, Huaweis - pretty much any non-Nexus phone - have the same 360px wide viewport (in CSS pixels), but have either 720p, 1080p or 1440p LCD/OLED screens. So with dppx we can differentiate between a low-end 2.00dppx Galaxy J5, a mid-range 3.00dppx Galaxy S5, or a high-end 4.00dppx Galaxy S7. Could you deduce a CPU speed difference from this? The true answer is no - but if you have no other data to go on...

Notes from the quirky edges

  1. Curiously, iOS only ever reports screen (not viewport) width/height in one orientation - portrait. If you turn an iPhone5 on it's side - landscape - it will still report width:320px, height:568px, when an equivalent Android phone would report the more expected width:568px, height:320px.
  2. It was while trying to get around the iOS screen size orientation "bug" I discovered more curiosities. All iOS devices see the portrait orientation as default - that means at 0° (and landscape as 90° or -90°). Android phones and 7" 16:9 tablets (eg Nexus 7) are exactly the same. But Android 10" 16:9 tablets (eg Nexus 10) almost always consider landscape orientation as the default 0° view. I'm not sure about 4:3 Android tablets like the Samsung Tab A, I'd expect them to follow the lead of the iPad. Even desktop monitors report orientation, the Dell monitor I use in rotated orientation reported 90° orientation to the browser.
  3. When changing the screen zoom at an OS level, browsers seem to report only the native screen dppx. For example, the 2014/15 Retina MacBook Pro 13" has a CSS pixel equivalent size of 1280x800px (2560x1600 actual pixels at 2dppx), but through system prefrences, it can be adjusted all the way up to a CSS pixel size of 1680x1050px (1.52dppx as the actual pixel size of course doesn't change!). But the browser still reports 2.00dppx, thereby calculating the actual screen size erroneously as 3360x2100px. iOS has a similar behavior, an iPhone6 in OS level "zoomed" mode (320px wide viewport instead of regular 375px) incorrectly reports 2.00dppx - resulting in an incorrect calculation of a 640x1136px screen. I haven't tried Android or Windows yet.
  4. Zooming at a browser window level appears to follow a slightly quirkier path:
    • In Chrome (55), dppx changes, dpi changes, viewport size changes, but screen.width remains at the level set by the OS.
    • In Safari (10.0), dppx DOES NOT CHANGE, dpi CAN NOT BE CALCULATED, viewport size does change, but screen.width remains at the level set by the OS.
    • In Firefox (50), IE11 and Edge (14), dppx, dpi, viewport size and screen-width all reflect the zoom level.
    • I've been trying to interpret these results - not in terms of deviation from spec, but what is most helpful to me as a developer and user - and I can't decide what should happen to the screen width/height. The other figures, dppx, dpi and viewport should of course reflect window zooming (two strikes against Safari there), but I believe FF, IE and Edge to be the most correct browsers; in a zoomed window situation, knowing the potential screen estate is more important than knowing what it is at zoom:100%. On the other hand, reporting this screen size to analytics is not going to be helpful to determine which devices are visiting you.

980 2 14