114

With the new Apple MacBook Pro with retina display, if you provide a "standard" image on your website, it'll be a little fuzzy. So you have to provide a retina image.

Is there a way to automatically switch to @2x images, like iOS (with Objective-C) does? What I've found is: CSS for high-resolution images on mobile and retina displays, but I wish I could find an automatic process for all my images, without CSS or JavaScript.

Is it possible?

UPDATE
I would emphasize this interesting article suggested by @Paul D. Waite and an interesting discussion about it linked by Sebastian.

Community
  • 1
  • 1
jacoz
  • 3,508
  • 5
  • 26
  • 42
  • Not bad! I don't wanna use js but that solution seem to be good – jacoz Dec 06 '12 at 13:26
  • I've done a lot of research on this recently, and as yet there are no solutions that I have come across that can be implemented without either specifying an oversize image for everyone, using JavaScript or using CSS media queries. I'd like to see a solution that uses http content negotiation, but that would require browser support. – michaelward82 Dec 06 '12 at 13:35
  • 3
    You can do it server side with PHP: http://retina-images.complexcompulsions.com/ – ReLeaf Dec 06 '12 at 13:35
  • 2
    @michaelward82: for photographic images, [Daan Jobsis suggests that you can serve retina-size images to everyone without your file sizes being any larger than non-retina images](http://blog.netvlies.nl/design-interactie/retina-revolution/), by increasing the amount of JPG compression applied to the image. The fact that the image is either displayed scaled-down or on a retina display often means that the compression artifacts aren't visible. – Paul D. Waite Dec 06 '12 at 14:43
  • What's wrong with using CSS media queries for this? It sounds like they are built *specifically for features like this*. – Joachim Sauer Dec 06 '12 at 15:31
  • 1
    Actually it's not _wrong_, but I was wondering if there was some _trick_ to use. In iOS it's automatic... that's why I'm asking it! :) – jacoz Dec 06 '12 at 15:33
  • 2
    Note that the author of the "interesting article suggested" has made some big mistakes which are described here: http://silev.org/test/Retina-resize.html - so the article has to be taken with a big grain of salt. – Sebastian Dec 06 '12 at 16:15
  • You could also use https://resize.ai that automates the whole process. – jimmy Aug 16 '17 at 08:57

10 Answers10

171

There is a new attribute for the img tag that allows you to add a retina src attribute, namely srcset. No javascript or CSS needed, no double loading of images.

<img src="low-res.jpg" srcset="high-res.jpg 2x">

Browser Support: http://caniuse.com/#search=srcset

Other Resources:

yole
  • 92,896
  • 20
  • 260
  • 197
bogeylicious
  • 5,071
  • 3
  • 25
  • 17
  • 10
    This isn't just webkit anymore, Edge & Firefox also supports it. http://caniuse.com/#search=srcset - so currently ~64% of users globally. Then take into account that very few hi-DPI users will be on the unsupported browsers (IE & old Android), and lastly that it's fail-safe - users without support simply see a normal DPI image. Definitely think it's ready for usage. – andrewb Apr 18 '16 at 03:04
  • 1
    Also, no double loading is a huge boon. This means you never waste anyone's bandwidth. – andrewb Apr 18 '16 at 03:05
  • IE once again falling short. But despite this, I agree with @andrewb . To build upon his comment, i am providing the x2 inside the `src` so IE / Opera will always request the higher DPI version. – Ricky Boyce Apr 26 '16 at 01:24
  • 1
    This should be the accepted answer. It is by far the easiest solution for this thread. – Julien Le Coupanec Jun 12 '18 at 13:56
14

There are different solutions, each with its own pros and cons. Which one is best for you depends on various factors, such as how your website is designed, what kind of technology your typical visitors are using etc. Note that retina displays are not limited to the Macbook Pro Retina and the coming iMacs, but also include mobile devices, which may have their own needs.

The problem is also closely related to images in responsive designs in general. In fact, it is probably best to utilize generic responsive design techniques, instead of designing for specific devices. After all, technology will keep changing all the time in the future, too.

Some of the solutions/discussions I noted:

  • Vectors wherever possible including CSS techniques (gradients, rounded corners etc.), SVG and icon fonts.
  • Serving high resolution ("retina") images, but compress them more (JPEG quality), as suggested by Yoav Weiss, or let the mobile networks compress them when really needed (i.e. when mobile), as suggested by Paul Boag.
  • Adaptive Images, a (mostly) server side solution. It is based on a cookie storing the screen resolution, a web server configured to serve images from a PHP script, and named script to read the cookie and serve the appropriate image.
  • A bunch of possibilities well described and discussed on Smashing Magazine.
  • Serving just slightly higher resolutions to smooth retina portrayal a little, as suggested in a video by Paul Boag.
  • The @1.5x technique on A List Apart is basically the same idea.
  • In the near future, the <picture> tag may become a solution supported by a W3C working group and even Apple.
  • A JavaScript technique proposed by Jake Archebald.
  • An extensive discussion of different techniques on Smashing Magazine and the problem in general.

As the other answers show, there are even more techniques - but probably no best practice, yet.

One thing I wonder is how to test and debug some of these techniques, without having the respective device(s) available...

bhell
  • 1,004
  • 7
  • 9
12

Since no one's mentioned the obvious yet, I'll float it out there: where possible, just use SVG. They appear at beautiful retina resolutions with no effort whatsoever.

Support for it is good with IE8 being the main dinosaur to worry about. Gzipped file sizes are often better than bitmapped (png/jpg) formats and the images are more flexible; you can reuse them at different resolutions and restyle them if necessary, which saves both development time and download bandwidth.

svachalek
  • 1,166
  • 8
  • 18
  • I like your hint! The only issue with `svg` is with older browsers. – jacoz Dec 15 '12 at 09:00
  • 16
    And cases where you have photographs – Baumr Mar 18 '13 at 22:10
  • Indeed, they're great provided you have a Vector version of the image you want to use, but I don't believe you can save normal raster images as SVG. – Chuck Le Butt Jul 30 '13 at 15:49
  • 1
    There's no "good" way to convert in that direction, thus the "where possible". But with the exception of photography sites etc. you're generally going to be creating your art assets so I'd recommend having them done as vectors, which can easily be converted to raster if you like, at any resolution. – svachalek Jul 30 '13 at 20:06
  • SVG doesn't work for screen shots (e.g. when documenting UI features). – Greg Brown Aug 21 '17 at 18:35
9

Here is the less mixin I use to achieve this for background images. retina.js doesn't work for background images if you are using dotLess, since it requires its own mixin which itself uses script evaluation which isn't supported in dotLess.

The trick with all of this is to get IE8 support. It can't easily do background-size so the base case (non mobile media query) has to be a simple, non-scaled icon. The media query then handles the case of retina and is free to use the background-size class since retina will never be used on IE8.

.retina-background-image( @path, @filename,@extension, @size )
{
     .background-size( cover );
     background-image: url( "@{path}@{filename}@{extension}" );
         @media only screen and ( -webkit-min-device-pixel-ratio: 2 ),
                only screen and ( -moz-min-device-pixel-ratio: 2 ),
                only screen and ( -o-min-device-pixel-ratio: 2/1 ),
                only screen and ( min-device-pixel-ratio: 2 )
         {
             background-image:url( "@{path}@{filename}@x2@{extension}" );
             background-size:@size @size;
         }
}

Usage sample:

.retina-background-image( "../references/Images/", "start_grey-97_12", ".png", 12px );

Ths requires you to have two files:

  • start_grey-97_12.png
  • start_grey-97_12@2x.png

Where the 2x file is double resolution for retina.

kaiser
  • 21,817
  • 17
  • 90
  • 110
muzzamo
  • 1,721
  • 2
  • 14
  • 18
8

Just provide retina images to everyone, and squeeze the image to half its native size inside the image element. Like let's say your image is 400px wide and tall - just specify the image width as 200px to make it look sharp like this:

<img src="img.jpg" width="200px" height="200px" />

If your image is photographic, you can probably increase the JPG compression on it without making it look worse, because the JPG compression artifacts probably won't be visible when the image is displayed at 2x: see http://blog.netvlies.nl/design-interactie/retina-revolution/

jacoz
  • 3,508
  • 5
  • 26
  • 42
webdev
  • 339
  • 1
  • 9
  • 1
    Daan Jobsis suggests that for photographic images, this doesn't even have to result in larger file sizes: see http://blog.netvlies.nl/design-interactie/retina-revolution/ – Paul D. Waite Dec 06 '12 at 14:44
  • Ideally you should specify a height though, so that the browser can lay the page out before the image has downloaded. – Paul D. Waite Dec 06 '12 at 14:47
  • 9
    I don't think it's a great idea providing a larger and heavier image file if not necessary... – jacoz Dec 06 '12 at 14:56
  • @jan267: for photographic images, you may be able to provide a retina version that isn't "heavier" (i.e. its file size is not larger) than the regular version, by increasing the JPG compression. Because it's displayed at 2x, the JPG compression artifacts may not be visible. – Paul D. Waite Dec 06 '12 at 15:00
  • @webdev: I think when jan267 says "if not necessary", he's talking about providing 2x images to devices that don't have retina displays. – Paul D. Waite Dec 06 '12 at 15:02
  • 1
    @PaulD.Waite interesting for first thing and exactly for last! :) – jacoz Dec 06 '12 at 15:04
  • you can check for the device and conditionally display the images for each device. – webdev Dec 06 '12 at 15:08
  • 2
    @PaulD.Waite Note that the author of the linked article has made some big mistakes which are discussed here: http://silev.org/test/Retina-resize.html - so the article has to be taken with a big grain of salt. Especially the fact that the "unscaled image on the right" is in fact scaled and thus cannot really be compared against the one whose resolution is doubled exactly (tell your browser to show the right images in a new window and you will see what I and the author of that other article mean) – Sebastian Dec 06 '12 at 16:18
  • @Sebastian: excellent, cheers for that, I hadn't seen it. Looks like you can still expect to produce retina images with a file size not *that* far away from the 1x size (and certainly not double it), so it's still an approach worth considering. – Paul D. Waite Dec 06 '12 at 16:28
  • Great, nobody cares, that there's no length unit in the `width`/`height` attributes in HTML. http://www.w3.org/TR/html-markup/img.html#img.attrs.width – Volker E. Oct 04 '14 at 15:03
  • I prefer my images to be crisp in regards to displaying logos and illustrations. – Ricky Boyce Apr 25 '16 at 01:48
  • The silev/netvlies links seem to have died. There's a follow-up article at https://www.netvlies.nl/blogs/retina-revolutie-follow though, which possibly (or not?) addresses the mistakes raised at silev. – mwfearnley Oct 11 '21 at 12:01
1

if its background images a simple way to do this is:

    #image { background: url(image.png); }

@media only screen and (-webkit-min-device-pixel-ratio: 2),
       only screen and (-moz-min-device-pixel-ratio: 2),
       only screen and (-o-min-device-pixel-ratio: 2/1),
       only screen and (min-device-pixel-ratio: 2) {
           #image { background: url(image@2x.png); background-size: 50%; }
}

another simple way is to use this method:

Just replace:

<img src="image.jpg" alt="" width="200" height="100" />

with

<img src="image@2x.jpg" alt="" width="200" height="100" />
SonnyP
  • 13
  • 3
1

I've found this interesting way for providing multiple resolution images.
It actually uses CSS, something I wanted to avoid, and works in Safari and Chrome only.
I'm talking about image-set.

Here's an example, provided by Apple (here):

header {
    background: -webkit-image-set( url(images/header.jpg)    1x,
                                   url(images/header_2x.jpg) 2x);
    height: 150px; /* height in CSS pixels */
    width: 800px; /* width in CSS pixels */
}

I wanna share also these two links:

jacoz
  • 3,508
  • 5
  • 26
  • 42
1

With JSF you could create a custom Facelets tag to save the fuzz of having to add srcset to each image.

In your taglib.xml you could have something like:

<tag>
  <tag-name>img</tag-name>
  <source>tags/img.xhtml</source>
  <attribute>
    <name>src2x</name>
    <required>true</required>
    <type>java.lang.String</type>
  </attribute>
</tag>

And your tag could look something like:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:fn="http://xmlns.jcp.org/jsp/jstl/functions"
                xmlns:ui="http://xmlns.jcp.org/jsf/facelets">

  <img src="#{fn:replace(src2x, '@2x', '')}"
       srcset="#{src2x} 2x"/>

</ui:composition>

Which could be used like:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:myTag="http://your.com/namespace-of-taglib">
  <myTag:src2x="image@2x.jpg"/>
</html>

And will render:

<img src="image.jpg"
     srcset="image@2x.jpg 2x"/>
Jasper de Vries
  • 19,370
  • 6
  • 64
  • 102
0

This problem is especially tricky with responsive sites where and image can be of varying width dependant on browser size. Also when dealing with a CMS where multiple editors are potentially uploading 1000s of images it to me seemed unrealistic for me to ask people to upload specially compressed images.

So I wrote a script that takes this into account, it fires at the bottom of the page and on resize finish. Each time taking into account pixel density and the size the image is occupying.

http://caracaldigital.com/retina-handling-code/

Keegan 82
  • 394
  • 2
  • 11
0

If you are not frustrated by fear of using java-script then here is a good article http://www.highrobotics.com/articles/web/ready-for-retina.aspx. It has very simple solution.

And the example in JSFiddle is worth a thousand words.

Using:

<img onload="getImgSrc(this,'image_x1.png')" width="100" height="100" />

JS:

/* RETINA READY IMG SRC */
function getImgSrc(img, src) {
    var srcResult = src;
    // if high-res screen then change _x1 on _x2
    if (window.devicePixelRatio > 1 && 
        src.indexOf("_x1.")>=0) {
          srcResult = src.replace("_x1.", "_x2.");
    }
    img.onload = null; //protect from second rasing
    img.src = srcResult;    
}

$(document).ready(function(){
  // fire onload trigger on IMG tags that have empty SRC attribute
  var images = $('img:not([src=""])');
    images.each(function(i) {
        $(this).trigger('onload');            
    });
});
Artur A
  • 7,115
  • 57
  • 60