iPhone Web App Performance Tip – Use Inline Images

Inline images use the data: URL scheme to embed the image data in the actual page. This can increase the size of your HTML document. Combining inline images into your (cached) stylesheets is a way to reduce HTTP requests and avoid increasing the size of your pages. Inline images are not yet supported across all major browsers.

Performance Best Practices at Yahoo!

Reducing the number of connections is critical for client side performance. Due to the nature of mobile and mobile networks the problem is only amplified. A low hanging fruit has always been moving to css sprites for all your graphical resources. A lot of developers haven’t moved to inline images because not all browsers support it. However, web app developers on the iPhone have it much easier. We only have one browser to worry about, and luckily for us, it’s a good one.

It means that we can safely inline images and reduce the number of requests an iPhone user has to make across EDGE/3G.

Let’s take a look at the newly launched iphone.cnet.com:

There are 26 requests total for this page. If we take all the img tags and replace the src with the base64 encoded inline image we can reduce the number of requests to 13.

<img src="http://i.i.com.com/cnwk.1d/i/wdgt/ipr/back-button-tip.png" border="0" align="left" />
to...
<img src="data:image/png;base64,iVBORw0KGgottA ... SuQmCC" border="0" align="left" />

If we do this for all C|NET owned assets (css and js files) then we can get it down to 6 requests!

<link rel="stylesheet" type="text/css" href="http://i.i.com.com/cnwk.1d/css/rb/wdgt/iphone/iphone-reviews.css" />
to...
<link rel="stylesheet" type="text/css" href="data:text/css;base64,Ym9keSB7CglvdmVyZmsb2F0OiBs...YmxvY2s7Cn0%3D" />

You can view these on your iPhone (or your browser) and see the performance difference:

Any iPhone web app developer can do this right now in less than 30 mins. I used Grey Wyvern’s tool, but here’s some down and dirty php code I whipped together for local files that does the same thing:

< ?php
$options = getopt('f:');

if(empty($options['f']))
  die("\nUsage: php base64encode.php -f >filename<\n");

$ext_map = array(
  'png' => 'image/png',
  'gif' => 'image/gif',
  'jpg' => 'image/jpg',
  'css' => 'text/css',
  'js'  => 'text/javascript',
);
$file = $options['f'];
$ext = explode('.', $file);
$ext = $ext[count($ext) - 1];
if(empty($ext_map[$ext])) die("\nUnknown extension\n");

echo 'data:' . $ext_map[$ext] . ';base64,' 
     . urlencode(base64_encode(file_get_contents($file))) . "\n";

So get out there, optimize, and save your users some valuable time!

Update: There have been some questions of busting the iPhone cache with this technique. As was documented on the YUI Blog the iPhone does not cache items over 25kb pre-gzip. If you’re going to use this technique just be aware that external resources linked are less than 25kb.