Detecting the system DPI/PPI from JS/CSS?
Solution 1
<div id='testdiv' style='height: 1in; left: -100%; position: absolute; top: -100%; width: 1in;'></div>
<script type='text/javascript'>
var devicePixelRatio = window.devicePixelRatio || 1;
dpi_x = document.getElementById('testdiv').offsetWidth * devicePixelRatio;
dpi_y = document.getElementById('testdiv').offsetHeight * devicePixelRatio;
console.log(dpi_x, dpi_y);
</script>
grabbed from here http://www.infobyip.com/detectmonitordpi.php. Works on mobile devices! (android 4.2.2 tested)
Solution 2
I came up with a way that doesn't require the DOM... at all
The DOM can be messy, requiring you to append stuff to the body without knowing what stuff is going on with width: x !important
in your stylesheet. You would also have to wait for the DOM to be ready to use...
/**
* Binary search for a max value without knowing the exact value, only that it can be under or over
* It dose not test every number but instead looks for 1,2,4,8,16,32,64,128,96,95 to figure out that
* you thought about #96 from 0-infinity
*
* @example findFirstPositive(x => matchMedia(`(max-resolution: ${x}dpi)`).matches)
* @author Jimmy Wärting
* @see {@link https://stackoverflow.com/a/35941703/1008999}
* @param {function} fn The function to run the test on (should return truthy or falsy values)
* @param {number} start=1 Where to start looking from
* @param {function} _ (private)
* @returns {number} Intenger
*/
function findFirstPositive (f,b=1,d=(e,g,c)=>g<e?-1:0<f(c=e+g>>>1)?c==e||0>=f(c-1)?c:d(e,c-1):d(c+1,g)) {
for (;0>=f(b);b<<=1);return d(b>>>1,b)|0
}
var dpi = findFirstPositive(x => matchMedia(`(max-resolution: ${x}dpi)`).matches)
console.log(dpi)
Solution 3
There is the resolution
CSS media query — it allows you to limit CSS styles to specific resolutions:
However, it’s only supported by Firefox 3.5 and above, Opera 9 and above, and IE 9. Other browsers won’t apply your resolution-specific styles at all (although I haven’t checked non-desktop browsers).
Solution 4
Here is what works for me (but didn't test it on mobile phones):
<body><div id="ppitest" style="width:1in;visible:hidden;padding:0px"></div></body>
Then I put in the .js: screenPPI = document.getElementById('ppitest').offsetWidth;
This got me 96, which corresponds to my system's ppi.
Solution 5
I also needed to display the same image at the same size at different screen dpi but only for Windows IE. I used:
<img src="image.jpg" style=" height:expression(scale(438, 192)); width:expression(scale(270, 192))" /> function scale(x, dpi) { // dpi is for orignal dimensions of the image return x * screen.deviceXDPI/dpi; }
In this case the original image width/height are 270 and 438 and the image was developed on 192dpi screen. screen.deviceXDPI is not defined in Chrome and the scale function would need to be updated to support browsers other than IE
Admin
Updated on March 22, 2021Comments
-
Admin over 3 years
I'm working on a kind of unique app which needs to generate images at specific resolutions according to the device they are displayed on. So the output is different on a regular Windows browser (96ppi), iPhone (163ppi), Android G1 (180ppi), and other devices. I'm wondering if there's a way to detect this automatically.
My initial research seems to say no. The only suggestion I've seen is to make an element whose width is specified as "1in" in CSS, then check its offsetWidth (see also How to access screen display’s DPI settings via javascript?). Makes sense, but iPhone is lying to me with that technique, saying it's 96ppi.
Another approach might be to get the dimensions of the display in inches and then divide by the width in pixels, but I'm not sure how to do that either.
-
Pointy over 12 yearsThis doesn't work on all devices. My Android phone reports 96dpi too.
-
mckamey over 11 yearsThis doesn't seem to work on any devices. Everything seems to report 96ppi: codepen.io/anon/full/mrfvg
-
mistertodd about 11 yearsDPI by definition is not the
numberOfPixels / sizeOfMonitorInInches
. DPI is defined such that 10 point text appears on your screen to be the same size as standard 10 point text in a book. In print, and typography,1 inch = 72 points
. The critical distinction is that people usually have their monitor's further away than they hold a book (33% farther, in fact). That is where Microsoft got the value value96 dpi
from:72 * 33% = 96
. Ideally you would have your monitor as far away from your face as you typically hold a book. But you don't, so that is why you have 96dpi. -
Yukulélé about 10 yearsIt SHOULD works, but unfortunately all browsers consider 1in = 96px and 1cm == 37.8px. Bug?
-
rbncrthms about 10 yearsThat comment is full of errors. DPI is an abbreviation that means dots per inch. With a screen, you are usually provided with the size of the diagonal in inches. To calculate how many pixels lie on this diagonal, you must work out the square root of (x^2 + y^2) where x and y are the horizontal and vertical pixels. For example, for 1920x1080 on a 24" display, the DPI is approx 91.8. And 72*33% is approx 24; you mean 72*133%. Probably 96DPI was no more than the mean scale of monitors at the time, and has nothing to do with the distance you hold a book compared to that of a monitor.
-
nmz787 about 10 yearsDoesn't work, gives 96 on both my 1080p android smartphone as well as my laptop.
-
Takol over 9 yearsNo they cannot tell physical device diagonal and always show up 13.3" on all of my devices. You can calculate the "pixel per inch" by it but truly it just cannot detect the device size.
-
Rémi over 9 yearsHere is a playground to test it home.heeere.com/tech-ppi-aware-css-media-query.html
-
Mathew over 9 yearsNot a bug - "px" isn't screen pixels, it is a fixed length of 0.75 pt, which itself is 1/72th of an inch.
-
Andy about 9 yearsthe CSS spec defines 1 px as 1/96th of an inch, which is why your calculation will always return 96. That inch is not an inch on-screen, but takes the distance to your eye into account, meaning that it's different on different devices. The browser vendors need to set that factor to something meaningful, which is not always the case.
-
Frederik Krautwald about 9 years@Takol It’s just the default value. You are meant to change it. Diagonals cannot be detected by JavaScript. See github.com/LeaVerou/dpi/issues/36
-
jrmgx over 7 yearsThis code works pretty well indeed, but is not readable at all
-
Supersharp over 7 yearsShould the result be exact? My monitor documentation says 93 while your function says 96.
-
Endless over 7 yearsThis is definitely more readable. But the different in mine and yours are that mine only executed matchMedia 11 times to figure out that it's 96 where as your code tries every possible number between 56 and 2000
-
Endless over 7 yearsThe number should be exact. It makes assumption by doing n^2 upon till the function returns false and then works closer towards the number by taking the middle number. You can try it here: fiddle. If the function says it's 93 then it should be 93. Think there is something fundamental broken with the browser in that way browser don't know what the screen resolution is so it just assumes it's 96
-
GreySage about 7 yearsAll you are doing is multiplying the constant 96 by the devicePixelRatio (which can be overridden). This is not a consistent way of getting the accurate ppi.
-
GreySage about 7 yearsThis always returns 96, even on mobile devices. This falls victim to the classic blunder of not realizing that inch is defined at 96px.
-
MaxXx1313 almost 7 yearsI can't figure out why you think so.
-
GreySage almost 7 yearsbecause the words that I typed have meanings? On all devices, 1in in css is defined to be 96px, so measuring a div with a width=1in in px will always give 96. THen you are just multiplying that (which again, will always be 96) by the devicePixelRatio which is not subject to standards or limitations, it could be anything and in particular anyone could overwrite it with whatever value they want. In short, this method isn't reliable.
-
MaxXx1313 almost 7 yearsActually, it doesn't work in firefox. Firefox itself looks weird on 4k display. In chrome and ie it forks perfectly fine.
-
Michael over 6 yearsWow, I can only imaging creating a style sheet listing every possible resolution in 1 or 0.1dpi range increments and inferring the closest actual screen resolution based on which style was applied...
-
Michael over 6 years+1 for effort, but... both this code and the code linked by @eltomito claim my phone PPI is ~336 when it's actually 518. And they both incorrectly claim my Mac and PC (with have the same raw resolution but different screen sizes) both have a PPI of 96 when in fact the actual values are 102 and 157, respectively.
-
Michael over 6 yearsThis doesn't seem accurate at all... the answer by Endless appears to do a binary search with this query, yet across my devices the playground linked by @Rémi gives the same invalid results...
-
Michael over 6 years@FrederikKrautwald And yet the question is "Detecting the system DPI/PPI from JS/CSS?" which this is not an answer to.
-
Eugen over 6 yearsthis will not work on WebKit/Safari (iOS 11), it appears max-resolution is not supported there yet
-
Eugen over 6 yearsthis will not work on WebKit/Safari (iOS 11), it appears max-resolution is not supported there yet
-
Frederik Krautwald over 6 years@Michael What? I was replying to Takoi in using Lea Verou's tool as linked to in the answer. I didn't provide an answer to the SO question.
-
eltomito over 6 yearsOkay, so it seems the problem is that mobile browsers render sites into a hidden invisible viewport and then rescale them and display them on the actual screen. The DPI and dimensions the browser reveals to the javascript code and CSS are that of the viewport, not of the actual display (which IMHO means the DPI is totally useless). The dimensions of the viewport can be controlled by the <meta name="viewport"> tag. Read more about it on MDN: developer.mozilla.org/en-US/docs/Mozilla/Mobile/…
-
V. Rubinetti almost 4 yearsI had a similar idea, where you just keep testing
@media screen and (resolution: 96dpi)
and increasingdpi
until you get a match. But testing it quickly on Chrome reveals that unfortunately, these CSSresolution
@media
queries also seem to scale up/down when the user zooms in/out, just likewindow.devicePixelRatio
does. So unfortunately they are also useless. -
V. Rubinetti almost 4 yearsThis tool doesn't work. Open it in Chrome, zoom the page in, refresh, and you get a new value.
-
DUzun over 3 yearsHere is a nicer version: repl.it/@duzun/findDPIjs#script.js
-
Endless over 3 years@DUzun i like the bit shifting part (left & right) instead of *2 and /2, looks like your dose a better job at finding #97, also more readability, local variables and no ternary operator. What's wrong with ternary operators? don't like them? :P
-
DUzun over 3 years@Endless Shifting gets ride of the fractional part, which is relevant for DPI. I like the ternary operator, only in simple expressions. Here I've replaced it for better readability and to avoid one call to
matchMedia()
. -
Yves Gurcan over 3 yearsCould you suggest an edit on the reply of the user instead of posting a new answer?
-
cdauth about 2 yearsThis doesn't make any sense, the user agent doesn't contain any information about what screen I have attached to my device.
-
Avi Tshuva about 2 yearsi'm afraid not... The assumption in the code is that "in" represent a real, physical inch in the screen. It is not. Sadly.