56

Right now I am using this function:

function is_retina_device() {
    return window.devicePixelRatio > 1;
}

But its simplicity scares me. Is there a more thorough check?

Martin James
  • 902
  • 1
  • 9
  • 25
  • 1
    Retina *specifically* or just HD support in general? IIRC Retina's pixel ratio is `2` but anything larger than `1` can be considered HD. – André Dion Oct 30 '13 at 17:32
  • 1
    @AndréDion So the reason why I am doing this in js is to determine which image to serve (regular or 2x the size). So I believe I need just retina and not hd. Because if this function returns true I serve an image 2 times the size. –  Oct 30 '13 at 17:58
  • if you want it for images you can use this http://retinajs.com/ – Juan Oct 30 '13 at 17:59
  • @TK123 I think the condition you have is safe to do what you describe. Even if the device's pixel ratio isn't exactly `2` but is still greater than `1` you would still want to serve up HD assets. You would serve up the higher resolution image and scale it back down—it's the same technique regardless of the device's exact pixel ratio. – André Dion Oct 30 '13 at 18:08
  • @AndréDion Now would that be for *any* >1? Why not about >1.4? – user2864740 Dec 06 '13 at 00:36
  • Fwiw, good reading here http://www.quirksmode.org/blog/archives/2012/06/devicepixelrati.html – user2864740 Dec 06 '13 at 00:43
  • See also https://stackoverflow.com/q/15234519/32453 for a discussion of doing it server-side vs. client-side and even an answer that describes how to do it "javascript - less" if you're looking for just loading higher res images, FWIW... – rogerdpack May 23 '17 at 20:10

4 Answers4

48

According to everything that I've read recently, browsers seem to be moving towards the resolution media query expression. This is instead of device-pixel-ratio that is being used in the currently accepted answer. The reason why device-pixel-ratio should only be used as a fallback is because it is not a standard media query.

According to w3.org:

Once upon a time, Webkit decided a media query for the screen resolution was needed. But rather than using the already-standardized resolution media query, they invented -webkit-device-pixel-ratio.

View Full Article

Resolution Media Query Documentation

Since resolution is standardized and therefore the future let's use that first in the detection for future proofing. Also because I'm not sure if you want to detect only high dppx devices or only retina(Apple only) devices, I've added one of each. Finally just as a note, the Apple detection is just user agent sniffing so can't be depended on. Note: for the isRetina function I'm using a dppx of 2 instead of 1.3 because all retina apple devices have a 2dppx.

Note it appears that MS Edge has some issues with non integer values

function isHighDensity(){
    return ((window.matchMedia && (window.matchMedia('only screen and (min-resolution: 124dpi), only screen and (min-resolution: 1.3dppx), only screen and (min-resolution: 48.8dpcm)').matches || window.matchMedia('only screen and (-webkit-min-device-pixel-ratio: 1.3), only screen and (-o-min-device-pixel-ratio: 2.6/2), only screen and (min--moz-device-pixel-ratio: 1.3), only screen and (min-device-pixel-ratio: 1.3)').matches)) || (window.devicePixelRatio && window.devicePixelRatio > 1.3));
}


function isRetina(){
    return ((window.matchMedia && (window.matchMedia('only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx), only screen and (min-resolution: 75.6dpcm)').matches || window.matchMedia('only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min--moz-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2)').matches)) || (window.devicePixelRatio && window.devicePixelRatio >= 2)) && /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
}
Adam Merrifield
  • 10,307
  • 4
  • 41
  • 58
  • 16
    Checked isRetina() function today on my MacBook with Retina - it doesn't worked. – ujeenator Jul 23 '15 at 17:43
  • 1
    What's the reason for using both CSS 'or' (`,`) and JS 'or' (`||`)? – binaryfunt Sep 22 '15 at 15:13
  • 1
    @BinaryFunt for the CSS `,`s we are checking different unit values (`dpi`, `dppx`, `dpcm`) for `min-resolution` because different browsers support different units. The javascript `||`s are so we can check for `min-resolution` and `min-device-pixel-ratio` (once again because different browsers support different media queries). Also, some browsers don't support `window.matchMedia` so the other `||` is to check `window.devicePixelRatio` as well. – Adam Merrifield Sep 22 '15 at 16:11
  • What is this part for? `/(iPad|iPhone|iPod)/g.test(navigator.userAgent)` checking for iPad or iPhone or iPod? is this really necessary? I mean those are not the only devices that support retina. A few new products are starting to have retina too. e.g. Mac – Jomar Sevillejo Oct 01 '15 at 06:22
  • 1
    @JomarSevillejo At the time of writing this, the only retina displays Apple had were the iPad, iPhone, and iPod. The only purpose of this line is to check the useragent of the browser, which should not be trusted anyway. It was more an example than anything. I would suggest using the `isHighDensity` function and changing the resolution media queries to your needs rather than using the `isRetina` function. – Adam Merrifield Oct 01 '15 at 15:19
  • @AdamMerrifield Nice mate! Thanks for clearing that one out :) – Jomar Sevillejo Oct 01 '15 at 23:39
  • Tried `isRetina()` on iMax with Retina 5k and did not work. – Meekohi Sep 28 '16 at 18:47
  • @Meekohi What browser are you using? Currently the `isRetina` function is just using user agent sniffing for `iPad|iPhone|iPod` which no desktop browser should match. maybe you want to use `isHighDensity` instead? – Adam Merrifield Sep 28 '16 at 20:20
  • Indeed, using Chrome. – Meekohi Oct 03 '16 at 20:43
  • Seems like MS Edge has some issues with non int values: `window.matchMedia('only screen and (min-resolution: 48.8dpcm)').matches === true` while `window.matchMedia('only screen and (min-resolution: 48dpcm)').matches === false` – Novitchi S Dec 19 '18 at 12:49
  • 1
    @NovitchiS interesting, [according to the spec](https://www.w3.org/TR/css3-mediaqueries/#values) "The value is a positive __ immediately followed by a unit identifier" (emphasis mine) so it looks like MS isn't following spec. i will add a note to the answer. – Adam Merrifield Jan 03 '19 at 16:33
35

If you want it for images you can use retinajs or this code is a common response to detect it:

function isRetinaDisplay() {
        if (window.matchMedia) {
            var mq = window.matchMedia("only screen and (min--moz-device-pixel-ratio: 1.3), only screen and (-o-min-device-pixel-ratio: 2.6/2), only screen and (-webkit-min-device-pixel-ratio: 1.3), only screen  and (min-device-pixel-ratio: 1.3), only screen and (min-resolution: 1.3dppx)");
            return (mq && mq.matches || (window.devicePixelRatio > 1)); 
        }
    }
Juan
  • 4,910
  • 3
  • 37
  • 46
  • 1
    Great thanks, to play it safe I added my previous check to this one `mq && mq.matches || window.devicePixelRatio > 1` –  Oct 30 '13 at 19:45
  • 2
    `-moz-min-device-pixel-ratio` is not correct. It should be `min--moz-device-pixel-ratio`. Please read the note here: [MDN](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Media_queries?redirectlocale=en-US&redirectslug=CSS%2FMedia_queries#-moz-device-pixel-ratio) @TK123 – Adam Merrifield Dec 03 '13 at 19:41
  • @Adam Merrifield Thank you, your comment came at the right time. A coworker on firefox in a linux machine found that the function was returning true even though he was not on a retina device. Updating to `min--moz-device-pixel-ratio` fixed that. I should note though that this entire function (even after the fix) returned true on a nexus tablet although retina css from a stylesheet wasn't executing on that machine which leads me to believe that it falsely returns true on certain devices. Maybe there's more, I only checked on a few. –  Dec 05 '13 at 21:37
  • @TK123 The function is working correctly. It measures the density of pixels of an output device. `Retina` is just apples way of marketing a higher density of pixels on their devices. If you want to know if a device is an **Apple** retina device, then you might also want to match user agent or use feature detection to tell if it's an idevice. That being said, the nexus 7(2012 model) has a 1.3dppx & the nexus 7(2013 model) has a 2dppx. So this is correct. Please view [this article](http://bjango.com/articles/min-device-pixel-ratio/) to see a short list of devices that have > 1dppx – Adam Merrifield Dec 05 '13 at 23:34
  • If you want approximately an equivalent but only in CSS, see https://gist.github.com/marcedwards/3446599 Also note that you might not even need retinajs if you specify higher res equivalents in your image tag itself, see https://stackoverflow.com/a/43823483/32453 – rogerdpack May 23 '17 at 20:14
21

Actually, the code you're using in your question is just completely right if you care only about modern browsers. (See: http://caniuse.com/#feat=devicepixelratio)

All modern browsers have it implemented, and older browsers would be just served your lower resolution images. I don't expect IE10- to show up on a retina / high-res device. Also, is using CSS checks in JavaScript not more weird than using a native window property?

Heck, devicePixelRatio browser support is even better than the resolution spec. (See: http://caniuse.com/#feat=css-media-resolution)

I'd say it's actually very safe to use, we use it in production websites with over 10 million visitors a month. Works as expected.

The only thing I'd change is the function name, as the need to load high res images doesn't technically mean the screen is retina. Actually, you don't even need a number check, as undefined > 1 results in false.

function is_high_resolution_screen() {
  return window.devicePixelRatio > 1;
}
Guido Bouman
  • 3,155
  • 4
  • 22
  • 33
  • 2
    `window.devicePixelRatio` is ok to find retina display, but if the goal is to detect **only** retina/high-dpi displays then `window.devicePixelRatio` is completely unreliable (try eg. with Chrome, set zoom level to something higher than 100% and you get a false positive no matter the display you use) – guari Jan 24 '16 at 15:37
  • 1
    Yeah, sure, but the browser would still profit from the higher res images because you would have more pixels to display them on. Which once again proves just checking the pixelratio instead of the type if screen if preferred in most of the cases. Also, if you look at TS's comment here: https://stackoverflow.com/questions/19689715/what-is-the-best-way-to-detect-retina-support-on-a-device-using-javascript/34739835?noredirect=1#comment29246172_19689715, you can see he's asking for the best way to know when to serve high-res images. This (his) solution does exactly that. – Guido Bouman Jan 24 '16 at 20:25
  • mm.. what if the image has a percentage size like usual in responsive layout or if it is a fullscreen background? Checking only `devicePixelRatio` isn't enough to ensure that an hd image is really needed. You can easily trigger the download of an heavy hd image when it is not necessary, at least you should check for `devicePixelRatio` and screen logical width/height (this too isn't perfect because the behavior of both isn't standard in all latest browsers, but it addresses more cases) – guari Jan 24 '16 at 23:53
  • 2
    Yeah, of course, it depends on the situation. If your images are sized based on a percentage it's only logical you'll need something more delicate than just `devicePixelRatio`. But that's not the original answer. In a project I'm working on, we have lazy images that resize depending on the browser size. We get images sized to the exact pixel we need from a CDN. To fully utilize high-res displays we multiply the width by the `devicePixelRatio`. This way we fill each device screen pixel with an image pixel. If the ratio is below 1 you can actually get a lower res image. (Zoomed out browsers.) – Guido Bouman Jan 25 '16 at 10:18
6

devicePixelRatio is not reliable at all. when you zoom in to 200%, the window.devicePixelRatio will return you 2, but you are not on a retina display.

Lee Le
  • 337
  • 1
  • 4
  • 10
  • 6
    But your screen is still able to render all the extra pixels available. That's why device pixel ratio is a great feature. – Guido Bouman Jan 30 '17 at 14:08
  • Agreeed with Lee Le. At zoom 200%, current Chrome returns double the original value. That is, when you zoom, devicePixelRatio also "zooms". It's not a reliable value for detecting zoom or retina support. – Ignacio Segura Feb 12 '20 at 12:02
  • Good point Lee Le. Especially when trying to make an HTML canvas look right on a retina display using this technique: https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio You can't actually use devicePixelRatio as advertised here on MDN since it scales wrongly when manually zoomed in by the user. There needs to be a separate device scale variable for user zoom and retina displays – keithphw Apr 25 '20 at 05:13