HiDPI: preparing for Retina displays

This post serves as a summary of what I talked about at the WordPress Meetup Oslo on tuesday October 16th.

First, as an introduction, we need to understand what it HiDPI or retina display means. It is the concept that 1 pixel doesn’t display 1 unit on the operating system. A good way to understand the difference is to compare the iPhone 3GS (or earlier) and iPhone 4 (or later). The system draws the exact same user interface, but one of them shows it at 320 by 480 pixels, while the other shows it at 640 by 960 pixel. The difference is that everything looks sharper. This looks great for text!

A key thing to remember about this is that there are requirements to both the hardware – more specifically the display, and the software for this to work. Something we saw when many developers had to update their iPhone apps to take advantage of the screen.

Although this is nothing new, it is the only in recent years that this has become mainstream. In 2010 smartphones entered the market with such capabilities, such as the iPhone 4. In early 2012 we started to see the first tablets, and by mid 2012 the first laptop with a retina display became available.

Even though the iPad (3rd gen – 2012) should have started the revolution where developers started thinking about a retina desktop experience, a large part of the developer community didn’t consider this until the Retina MacBook was released.

Something to note, is that no matter what we do as developers, we can only improve the visual experience for users who use compatible software. As of now, only Safari and Chrome support HiDPI screens. Luckily Firefox has it now in a nightly build, and Opera has it in the current beta version.

Luckily, there are lots of things that we don’t need to think about. Fonts, CSS styles, vector graphics, basically everything except one thing.. static graphical content. What do I mean? Images, backgrounds, video, stuff like that. But why is that a problem?

Well, there are several issues. Ideally, the server would be able to recognise the browser or the pixel density of the screen on the device that is requesting the website and thus serve the correct images. Sadly though, it doesn’t. However, the browser does, but it currently doesn’t have any standards to handle this. For many, this is just an extension of the problems that come with responsive design, where we already are trying to serve the right images to the right devices.

The big question is then, how do other websites handle this? Unfortunately, the answer is that most simply don’t. Then what methods do we, who want to make our sites retina-ready, have at our disposal? Well, we can use a CSS method, a reactive or a proactive method with JavaScript, and last but not least, there is an alternative method.

CSS method

In CSS we have something called media queries, which allows us to target specific devices or screens. In this case, we can use it to select devices with a high “pixel ratio”, and set custom styles for those. This means that we can replace background images. An example of such code is shown below. There are also some CSS hacks that set ‘content’ with the url of an image, but since this is hacky, I will not add an example for it.

@media only screen and (min-device-pixel-ratio: 2){
    .logo{
        background-image: url(logo_highres.png);
        background-size: 100px 100px;
    }
}

Javascript methods

First we will take a look at the reactive JavaScript method. This method is based on the idea that after the page has loaded, if it is shown on a retina device, it will replace all the images it can with a retina version. There are some obvious problems with this, most prominent of which is that the user will have to download both images. A waste of broadband. Example code:

if(window.devicePixelRatio > 1) {
    var els = jQuery(".replace-2x").get();
    for(var i = 0; i < els.length; i++) {
        var src = els[i].src
        var j = src.lastIndexOf(".");
        src = src.substr(0,j) + "@2x" + src.substr(j);
        els[i].src = src;
    }
}

Secondly, there is the proactive JavaScript method. What this method does is, it replace every images with a script block, which checks for a retina screen while the browser parses the page. The good part is that the correct image will be inserted, and the user will only have to load the one. On the other side, it is argued that the “document.write” command will slow down the browsers parsing process somewhat.

<script type="text/javascript">
    ...check for retina
</script>
<!-- show regular image for those who don't have JavaScript enabled -->
<noscript>
    <img src="header.png" alt="Our company" height="116" width="640">
</noscript>

For this purpose I use a function i created where I pass several attributes, and the correct img tag will be written to the document as the page is loaded.

document.write(retina_post_image(
    'header.jpg',
    'header_2x.jpg',
    'Our company',
    640,
    116
));

Alternative solution – JPEG compression

An article that was published on the netvlies blog recently that showed a new and alternative solution to these problems. The idea that one could have a JPEG image, that has twice the size in pixel, and that still has the same file size while maintaining the same quality seems ridiculous, but it is possible.

If you take a retina image, and compress JPEG images a lot you will get a smaller file which you can serve to both retina and non-retina users. Surprisingly, the compression artifacts will be close to unnoticeable both on the retina display – due to the density of the pixels, and for the regular screens because the image is downscaled in the browser. There are, however, definitely some shortcomings. Such as the fact that this only works for JPEG images.

Future solutions

This part of the article talks about methods that currently cannot be used, and that are only an indication of a solution that could be implemented in the future. Seeing how the video and audio tags are currently, something like the example below could solve many of the responsive web design as well as retina issues. By allowing multiple sources for an image, and using media queries, one could specify the correct image, and have a simple img-tag as fallback. Additionally, the experimental srcset attribute for img tags, and the set-image() solution in CSS are solutions that might make it into future HTML and CSS specs.

<picture alt="Our logo" width="640" height="116">
    <source src="header_2x.jpg" media="(min-device-pixel-ratio: 2)">
    <source src="header.jpg">
    <img src="header.jpg" width="640" height="116" alt="Our logo">
</picture>

Update: It seems far more likely that this situation is going to be resolved by the addition of the srcset attribute to the image tag. This will be backwards compatible, and keep image tags fairly similar to the ones we use today. This is currently being tested in WebKit.

<img src="image.jpg" srcset="header.jpg 1x, header_2x.jpg 2x" alt="Our logo" width="640" height="116">

Summing things up

For WordPress users, the WordPress 3.5 update should incorporate better retina support. Thus far (beta 2) this is only making it’s appearance in the admin panel in the form of high-res icons. For developers, the CSS methods is the way to go when it comes to logos and background-images. To get retina images in the content, there are plugins like Hammy. I have also been working on a plugin for this myself, but it is unfortunately not ready for public consumption.

If you would like to contact me, I am on twitter: @thomasmb as well as App.net: @thomasmb

I’m a webdeveloper based in Oslo, Norway. I currently work with webdevelopment as lead developer and solutions architect at Metronet.

I would like to change the world, but they won’t give me the source…