Super Easy Responsive Images with Viewport Cookies

Here is the idea behind the Viewport Cookies:

  1. first, detect the width of the device viewport using javascript and store its value in a viewport cookie from within the <head>;
  2. use that cookie in the backend (with Nginx image_filter or a PHP script) to resize, cache and serve images of appropriate size for that particular viewport.

The great thing about this method is that it doesn’t require any modifications to the image tags and the javascript is kept to the bare minimum.

Adaptive Images with Viewport Cookies

Part 1: Store the Screen Width in a Cookie Using Javascript

Javascript is the only thing that can detect parameters such as browser window dimensions and device screen size, so we have to use javascript until (and if) browsers start setting this data in HTTP request headers.

Cookies, however, are the only dynamic thing that can be passed together with the request to the backend without changing the request URL. So we use them both to first choose an optimal reduced image width and then store it into a cookie called viewport which the browser will send along all the subsequent requests, including for the images.

Keep in mind that cookies are set per domain name and in case you’re of using a Content Delivery Nework for serving static assets you will have to set the viewport cookie from the CDN.

<script type="text/javascript">
	if (screen.width < 1000) 
		viewport = 800; 
	if (screen.width < 800) 
		viewport = 600; 
	if (screen.width < 450) 
		viewport = 400; 

	if (typeof viewport != 'undefined') 
		document.cookie = 'viewport=' + viewport;
</script>

You can use whatever logic and levels of adaptiveness you want, but keep in mind that not setting a viewport cookie for larger screens will save you cache hits.

Here is a simple filter for WordPress that will put it into your theme’s <head>:

add_action( 'wp_head', 'add_viewport_cookie_js' );

function add_viewport_cookie_js() {
?>
	<script type="text/javascript">
		if (screen.width < 1000) viewport = 800; if (screen.width < 800) viewport = 600; if (screen.width < 450) viewport = 400; if (viewport) document.cookie = 'viewport=' + viewport;
	</script>	
<?php
}

Part 2: Use the Viewport Cookie Value to Serve Resized Images

Once the cookie is set, it will be passed along all the subsequent requests, including for images, so we just read it’s value in the backend and resize the image accordingly. Here is an example of how to do it in Nginx:

location ~ \.(jpg|jpeg|gif|png)$ {
	expires max;

	# Resize only if a valid cookie value is present
	if ( $cookie_viewport ~ ^800|600|400$ ) {
		return 418;
	}

	error_page 418 = @viewport_image;
}

location @viewport_image {
	internal;

	expires max;
	add_header X-Viewport $cookie_viewport;

	image_filter   resize $cookie_viewport -;
	image_filter_jpeg_quality 90;

	error_page     415   = /empty;
}

location = /empty {
	internal;
	empty_gif;
}

In the first location block that captures all images we simply add a new check for a valid value of the viewport cookie. If a valid cookie is present, we send the request to an internal virtual location called @viewport_images which uses the built-in image_filter function of Nginx to resize the image accordingly. Notice that we are specifying only the width of the image while the height is set to -, which means that it will be scaled proportionally.

We also add a custom HTTP header called X-Viewport to make it easier to verify the viewport size that was used in the backend (via the Network tab in Developers Tools, for example).

Conclusion

Viewport Cookies is an extremely simple method for serving images of reduced dimension and file size to devices and browser that can display only a certain amount of pixels. Although the screen size of a device doesn’t necessarily mean that it has a slow network connection (like when using iPad on a WiFi network vs GPRS), it is still beneficial to browser that have screens smaller than SVGA (800 x 600).

8 Comments

  1. Ryan Hellyer says:

    Seriously cool.

    I imagine the occasional person would have a poor experience if they had their browser window really small when they loaded your site, then expanded it to regular width afterwards. You could possibly pass the device type along as well (I assume you can access that via JS), but I’m not sure it’s necessary for the rare occasion that problem cropped up.

  2. Ryan Hellyer says:

    Oh, awesome! Well it seems you thought of everything :)

  3. Matt Wilcox says:

    Nice :) It’s like http://adaptive-images.com but without the image generation features. And likely a lot faster than AI as it doesn’t need PHP.

  4. Lee says:

    Even screen width isn’t fixed though. Think tablet or phone swapped from portrait to landscape ….

  5. Ryan Hellyer says:

    Screen width is always based on the landscape mode on a mobile device. The browser width is used for the portait mode.

  6. I just looked up HTTP status code 418 as I wasn’t sure what it was. It’s now my favourite status code.

Leave a Reply