Is there a good practice for serving images to mobile and HIDPI/retina devices yet?

I’m planing to create a blog and I’m thinking about how to serve images in the best possible quality without affecting loading times for mobile devices too much.

My content column is aprox. 768 display points wide and I’m posting screenshots using a retina Macbook so the images will be at least 1500px wide. Other Retina/HIDPI users should see the version best for them.

Read More

Is there a recommended best practice for this case? I’m talking only about content images (<img />); no CSS backgrounds. A true WordPress solution that serves images based on the uploaded hires images would be great.

I have used adaptive images in the past but it does not distinguish between HIDPI and normal clients. Other solutions serve for retina but not for different screen sizes.

Related posts

Leave a Reply

2 comments

  1. No, there is no good method for doing this. This isn’t a fault of WordPress, but of the state of the web in general. HiDPI on the web is currently a big hack, basically, and the web has not yet adapted to it properly.

    Basically, there’s no good way to specify multiple resolution images in the HTML in a way that the browser can then use to determine the proper image to show. CSS works, but only if you’re using images as backgrounds. If you’re dealing with IMGs, then you have to rely on Javascript to do the job, and that has various downsides depending on your methodology.

    In the future, when browsers have started to adopt methods for specifying multiple resolution images, then this won’t be as much of a problem.

    Edit: This deserves a bit more explanation.

    The basic problem with using JS to do this is that it relies on a technique of image-replacement. The JS code, through whatever logic it has, finds the IMGs on the page, then replaces them with higher resolution versions from some other file. No matter what method is used to do this, it has two major downsides:

    • First, the initial image is loaded in low resolution, then loaded again in high resolution when the image is replaced. This means extra bandwidth usage, and considering most HiDPI devices are currently mobile ones, this doesn’t make much sense to waste that sort of bandwidth on those devices.

    • Second, the image is generally first displayed in low resolution and then replaced with the high resolution image. This means that there’s a second or two of showing a fuzzy image on HiDPI screens, before it “pops” into focus as the replacement occurs. This is a really crappy effect to have.

    Other methods exist by which you can simply serve the proper image in the first place, but they have downsides as well.

    • Server-side browser detection by User-Agent is a pretty crappy way to do things in the first place, and best avoided simply because there’s so many different agents out there in the wild that maintaining a list of them is virtually impossible. However, if you want to go through the pain of it, this method can work.

    • Alternatively, it’s possible to have Javascript in the browser detect the device-pixel-ratio of the browser and set that in a Cookie to return to the server. The server can then use this cookie to serve the proper resolution image. This works, but is unreliable because many mobile user agents fail to properly set and return the cookie, and even on those that do, the high-resolution images would only be shown on the second visit to the page. First impressions are low-res, which isn’t good in general.

    Like you can see: It’s just hacks all the way down. Until the browser is capable of being told for a specific IMG that multiple versions exist, and their parameters, and then being allowed to choose for itself, then these are the only ways to do it. But, if things like the proposed HTML5 “srcset” or the PICTURE tag are implemented, then we’ll have better choices.

  2. After thinking a while about this, I have to offer two ways to approach (parts of) this problem (partly). Still: Read the answer from @Otto carefully. It has a lot of in depth details for a reason.

    Javascript detection

    I wrote an answer about »How to add default images« for attachments. You can alter the filter callback to always add a simple png/gif (that has just the background color) instead of the normal image or thumbnail. This would help you serving an extremely small image (maybe below 1kB – smushIt for the rescue) that the user agent/browser would cache at the first load and then simply insert for each other call to an image.

    Then, simply the display pixel density with…

    var dpr = 1;
    if ( window.devicePixelRatio !== undefined )
        dpr = window.devicePixelRatio;
    
    if ( dpr > 1 ) {
        // High pixel density pixel displays
    }
    

    …exchange the images when the DOM has fully loaded.

    CSS routing

    Adding stylesheets for different pixel density displays isn’t that hard:

    // For default/low density pixel displays only: 
    wp_enqueue_style(
         'low_density_styles'
        ,trailingslashit( get_stylesheet_directory_uri() ).'low_density.css'
        ,array()
        ,filemtime( locate_template( 'low_density.css' ) )
        ,'screen'
    );
    
    // For high density pixel displays only: 
    $media  = 'only screen and (min--moz-device-pixel-ratio: 2), ';
    $media .= 'only screen and (-o-min-device-pixel-ratio: 2/1), ';
    $media .= 'only screen and (-webkit-min-device-pixel-ratio: 2), ';
    $media .= 'only screen and (min-device-pixel-ratio: 2)';
    wp_enqueue_style(
         'high_density_styles'
        ,trailingslashit( get_stylesheet_directory_uri() ).'high_density.css'
        ,array()
        ,filemtime( locate_template( 'high_density.css' ) )
        ,$media
    );
    

    Now you can simply switch between high/low density images – for your default images (header, logo, etc.). This doesn’t work for post type attachments (again: refer to @Otto answer).