Jonesing for dpi and dppx
Getting the output resolution of a screen from a web browser is not as simple as it might seem.
The old trick of setting a div as “1in” wide and then counting pixels only ever results in 96dpi (dpi in browsers is actually “dots per CSS inch” — and a “CSS inch” is defined as 96px — defeating the purpose of the exercise).
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 real-world dpi of a viewport.
The dream of dpi
Having a real-world dpi measurement would make accessibility a whole lot more predictable. Knowing how big a font will actually appear on a device — or how big a touch target is on low and high dpi screens (in real-world units like mm or inches) would be fantastic. But at the same time, 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.
Although dpi is not accessible directly in JS, we can derive a dpi value from a browser using methods like looping “media.match” on resolution media queries, but don’t be deceived, it still counts an inch as a CSS inch, ie, 96pixels, so it’s still not terribly useful.
But we still have dppx
Dots-per-pixel (or dppx), however, is directly accessible and still gives a lot of value. It tells us how many physical pixels are hidden inside a CSS pixel measurement. In an iPhone5, the retina screen means it is 640 physical RGB LED pixels wide, but the browser viewport width is reported as exactly half that at 320px — so each “CSS pixel” contains two physical “device” pixels, hence 2.00dppx.
Almost all browsers support the JS property `window.devicePixelRatio`, so we can access screen dppx figure directly.
Using a javascript derived dppx value for styling purposes would be madness, as we should be using the method built for this purpose — resolution media queries @media (min-resolution: 2dppx) { ... }
. 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 as each other (remember, that’s in CSS pixels), but have either 720p, 1080p or super dense screens.
With dppx we can differentiate between categories like an old Galaxy S2 at 1.50dppx, a low-end 2.00dppx Galaxy J7, 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 honest answer should be that it’s not accurate, but if you have no other data to go on…
Using dppx and more to improve Google Analytics.
One potential use of dppx is to increase the depth of analysis in Google Analytics. Device analytics in GA is good but a bit hit and miss in parts. iPhone generations are generally lumped together (an iPhone 6, 6S & 7 looking exactly alike), whereas Androids often report individual model variants which give a little too much depth. Do you know which Samsung a I9300 is? How about a I9305N which is the same phone with a different radio?.
Using dppx, we at Stendahls separate Android phones into bands, based on when a certain spec of screen dppx would have constituted a “flagship” device. It’s a pretty approximate measure, but that’s the point, we only need to know the mix of users capabilities so that we target them more effectively and don’t kid ourselves into leaving people out. Along with other peripheral javascript properties like colour gamut and forceTouch, we also try to pull out more detail about iPhones and iPads: https://stendahls.github.io/ga-device-deep/
Notes from the quirky edges
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.
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 when the primary screen on the connected laptop reported 0°.
When changing the screen zoom at an OS level, browsers seem to report only the native screen dppx.
- In iOS an iPhone6 in OS level “zoomed” mode (where the home-screen icons appear larger) gives Safari a 320px wide viewport instead of regular 375px but incorrectly reports the native 2.00dppx and so a 640x1136px physical screen (the same as an iPhone5).
- On MacOS there’s a similar story. The 2014/15 Retina MacBook Pro 13" screen has a CSS pixel equivalent size of 1280x800px (2560x1600 actual pixels at 2dppx), but through system preferences, it can be adjusted all the way up to a CSS pixel size of 1680x1050px (1.52dppx as the physical pixel size of course still the same). But the browser still reports 2.00dppx, thereby calculating the actual screen size as a huge (and incorrect) 3360x2100px.
- I haven’t tried Android or Windows yet.
Zooming at a browser window level appears to follow a slightly quirkier path:
- In Chrome (55):
Changed: dppx, viewport size (in CSSpx).
Unchanged: screen size (in CSSpx). - In Safari (10.0):
Changed: viewport size (in CSSpx).
unchanged: dppx, screen size (in CSSpx). - In Firefox (50), IE11 and Edge (14):
Changed: dppx, viewport size (in CSSpx), screen size (in CSSpx)
After trying and failing to discover what the specs say should happen, I’m left with my own expectations. Dppx 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 a normal zoom level. It might confuse your analytics though, unless you’re aware of this edge case.