Peter Keating

Developer from the New Forest in the South of England.

Reducing HTTP Requests for Images using CSS Sprites


When developing websites nowadays there is a lot more to think about than the appearance, content and user interface. Performance should be one of the primary concerns for developers with the rise of mobile browsing giving more varied contexts for users visiting a website. Considerations need to be made for those with a slow or fast Internet connections, small or big screen size & high resolutioned displays. When a user visits a web page their first impression will be the page load speed. Slow page load speeds will deter users from continuing their visit, and if it doesn't deter them then it will fuel a bad impression in their mind during their visit. One of the most effective ways to improve page loads speeds is to reduce the number of HTTP requests for resources such as CSS, JavaScript and primarily images.

Generally images will take up the majority of HTTP requests for a page load. Below is a screenshot of the requests made by this blog before writing this article. As you can see it makes 11 requests (bottom left of the image below) of which 4 are for images. My website has a simple design containing only a handful of images, this is generally not the case, websites usually have lots of images.

There are a few methods to reduce the number of HTTP requests to load images, the one I am going to focus on is the use of a sprite sheet. A sprite sheet is a single image that is made up of other images, reducing the number of HTTP requests to one. Instead of having every image on a web page in a sprite sheet I like to only have related images contained within a single sprite sheet, icons for example. Once you have sprite sheet containing images for a web page, CSS is used to dictate which image in the sprite sheet should be shown. Each image in the sprite sheet is represented by a CSS class that has the sprite sheet as a background image and a background position specifying the co-ordinates of a particular image from the sprite sheet.

I am going to use my website as an example. The social media icons in the top right corner are a perfect example of a situation where a collection of related images can be placed into a sprite sheet in order to reduce multiple requests to a single request. Below is the current markup to display the social media icons, as you can see currently I am just loading each social media icon individually with the img tag.

<nav class="social-navigation">  
    <a href="http://twitter.com/peterkeating" title="View my Twitter profile." target="_blank">
        <img src="http://peterkeating.co.uk/wp-content/themes/peterkeating.co.uk/images/icon-twitter.png" />
    </a>
    <a href="http://github.com/peterkeating" title="View my Github profile." target="_blank">
        <img src="http://peterkeating.co.uk/wp-content/themes/peterkeating.co.uk/images/icon-github.png" />
    </a>
    <a href="http://www.endomondo.com/profile/5042109" title="View my Endomondo profile." target="_blank">
        <img src="http://peterkeating.co.uk/wp-content/themes/peterkeating.co.uk/images/icon-endomondo.png" />
    </a>
</nav>

The first step to implementing a sprite sheet is to gather the related images into a single image file. Generation of the sprite sheet is fairly straight forward with many online tools just a Google search away, I recommend Stitches by Matthew Cobbs. Stiches provides an easy drag and drop user interface to upload images to be included in a sprite sheet and then click the generate button. Not only will Stitches generate a sprite sheet image, but also a stylesheet with the CSS classes representing each image in the sprite sheet. Stitches takes away all the hard work of calculating the x & y co-ordinates for each image in the sprite sheet.

Before uploading the current images that are being displayed, I paid some thought to those who visit my website on a device with higher DPI (generally iDevices at the moment). The general principle for ensuring great quality on higher DPI screens is to have images double the size as ones displayed on a regular screen. So with this in mind, instead of uploading the current 50x40 icons, I am going to upload 100x80 icons to Stitches, below is the generated sprite sheet.

Below is the stylesheet generated by Stitches containing classes for each image.

.sprite {  
    background: url(download.png) no-repeat;
}

.sprite-icon-endomondo-png {
    width: 100px;
    height: 100px;
    background-position: -111px -0px;
}

.sprite-icon-github-png {
    width: 100px;
    height: 100px;
    background-position: -0px -0px;
}

.sprite-icon-twitter-png {
    width: 100px;
    height: 82px;
    background-position: -0px -111px;
}

I am not a big fan of the class names, but these can easily be modified, the important thing in the code above is the background-position property that identifies the co-ordinates of each image in the sprite sheet. My next step was to create a copy of the sprite sheet that is exactly 50% of the size, this will be used for screens without high DPI. Below are the two sprite sheets side by side that will be used depending on the resolution of the visitors device, this can be identified using media queries.

For the low resolution sprite sheet all the positions in the CSS generated by Stitches will need to be recalculating as they won't align properly using the positions given to us by Stitches for the high resolution sprite sheet. By changing the positions to half the value they will align perfectly because the high resolution sprite sheet was re-sized to half the size. The CSS below has been re-factored slightly to improve the naming of the classes and has the recalculated positions and each icon is now 50px in width.

.social-media-icon {  
    background: url('../images/icon-sprite.png') no-repeat;
}

.icon-endomondo {
    width: 50px;
    height: 50px;
    background-position: -55px -0px;
}

.icon-github {
    width: 50px;
    height: 50px;
    background-position: -0px -0px;
}

.icon-twitter {
    width: 50px;
    height: 41px;
    background-position: -0px -55px;
}

A simple div with the class of social-media-icon and the specific icon class, for example icon-github, will show an icon on the web page. Below is the HTML that is now used for the social media links section of my blog. You may notice that I am now using the <ul> element, this was to improve the semantics and simplify the definition of the elements going horizontally.

<ul class="social-navigation">  
    <li>
        <a href="http://twitter.com/peterkeating" title="View my Twitter profile." target="_blank">
            <div class="social-media-icon icon-twitter"></div>
        </a>
    </li>

    <li>
        <a href="http://github.com/peterkeating" title="View my Github profile." target="_blank">
            <div class="social-media-icon icon-github"></div>
        </a>
    </li>

    <li>
        <a href="http://www.endomondo.com/profile/5042109" title="View my Endomondo profile." target="_blank">
            <div class="social-media-icon icon-endomondo"></div>
        </a>
    </li>
</ul>

So far so good, looks to be working on desktop and mobile, however when looking at my blog on a device with a retina display (iPhone or iPad) the images look blurred. This is the effect that occurs when images aren't appropriate for devices with retina displays. To solve this an image of higher resolution needs to be shown instead of the original image, luckily I had thought about this earlier and use the original sprite sheet that generated by Stitches before I re-sized it to half the size. By using media queries in CSS, we can define that the larger image should be used when visitors device has a higher DPI. Shown in the code sample below, the -webkit-min-device-pixel-ratio and min-resolution is used to target devices that contain a higher resolution. Generally the min-resolution value is going to be whatever the -web-min-device-pixel-ratio value is multiplied by 92. GirlieMac has done a great article that covers resolution in media queries going into detail about the feature and different units that are currently and will be available.

@media (-webkit-min-device-pixel-ratio: 2),  
       (min-resolution: 196dpi)
{
    .social-media-icon {
        background: url('../images/icon-sprite@x2.png') no-repeat;
        background-size: 111px 102px;
    }
}

In order for the CSS above to work it needed to be placed above the individual classes for the icons. As you can see the background image is being changed for icon-sprite@x2.png which is what I named the higher resolution sprite sheet, @x2 seems like a good convention that is followed by others in the developer community. The other change is the use of background-size in order to scale the background image down so it remains the same size as the original. It should be noted that background-size isn't supported in IE8 or earlier, but is supported by all the modern browsers. For my site I have decided not to rely on a fallback, my blog targets developers so I am going to make a bold assumption that the majority of visitors will be visiting with a modern browser. It is important to think about the target audience when making these types of decisions, but always do some research as there is most likely a solution for making HTML5 features available for legacy browsers like IE8 and earlier.

Viewing my blog on a device with a retina display no longer shows the social media icons with the blurry effect. Below are screenshots from my iPhone to show the differences between using a higher resolution sprite sheet against the half sized sprite sheet for default resolution devices. The screenshot on the left (or top if reading on mobile) shows the blurry effect (especially for the Github icon in the middle), comparing it to the screenshot on the right (or bottom if reading on mobile) where the icons are clearer and crisper.

The process of optimizing for retina displays may have been overkill for the social media icons as it really is attention to detail, however for larger and more significant images it should be a major consideration, especially if you find that majority of visitors to your website are using a device with a higher resolution. Consideration should be given to the impact on page load speed by loading higher resolution images with a larger file size. The default sprite sheet used for the social media icons weighs in at 6KB, the high resolution one is over double the size at 14KB. Because the file size is small the impact on page load speed isn't a concern, however for larger images this should be monitored. If the visitor is viewing on a mobile device like the iPhone with a retina display but has poor connectivity, then loading a high resolution image will have a huge impact on page load speed. This issue requires consideration during the design phase, a decision should be made as to how necessary it is that the image is displayed for smaller screens, and whether it is worth the risk of a slow page load speed.

The number of requests now for my blog has now reduced, and although the impact on page load speed would be minimal, if there was much more than three images in the sprite sheet then the difference would be more dramatic. All the code that was changed during the sprite sheet process can be viewed in a commit to my WordPress themes Github repository.

Back to Posts

-->