Simply Amazing: Caching in the Browser

As of December 2011, this topic has been archived. As a result, it is no longer actively maintained. For more information, see Archived Content. For information, recommendations, and guidance regarding the current version of Internet Explorer, see Internet Explorer Developer Center.

Robert Hess
Microsoft Corporation

June 12, 2000

I love to doodle.

Instead of doodling by drawing little images on paper, I doodle using Notepad—building up little Web page fragments and experimenting with layout tricks and such. I end up with a "Test" subdirectory on my hard drive full of individual nuggets and tests.

Some of my favorites are inspired by seeing something in "real life" and wondering how it could be rendered as a Web page. It might be a magazine ad, a newspaper layout, a business card—or sometimes it's just concepts and activities that I look at through my Web designer glasses. I thought that some of you might be interested in one of my more recent doodles, and how it has evolved to actually illustrate an important concept for Web site development.

It all started in a little maze of twisty passages. Or was it a twisty maze of little passages? Anyway, it all started at I was curiously cruising through the Internet when I ran into a site about a guy in England, Adrian Fisher, who designs mazes for a living. As I browsed through the photographs on Adrian's site, I was taken by the pure "physicalness" of what he creates. His mazes incorporate hedges, bricks, waterfalls, and mirrors; yet a maze is in concept very much a mental puzzle. I started imagining what it would be like to construct a maze as a Web page, or as a set of Web pages, in a way that would seem natural and be highly playable. Then a particular maze on his site caught my attention.


Figure 1. The Verden Arrow Maze, Verden, Germany

A flat-surface maze—no barriers, no obstructions, just simple rules to follow. The object of this maze is to stand on the black arrow and, moving only in the direction that the black arrow points, move to another square, follow the direction of that arrow, and continue until you find yourself at the center checkered square.

This, I thought, would make a good maze for a Web page. The next step would be to implement it.

The maze needs to be clickable, so that each click carries the player farther through the maze. Preferably, at each page view, the only things a player can click on are valid moves. One way to do this would be to make a page for each possible square a player could land on, thus fully controlling the play of the maze through static Web pages. But that would require me to write up 25 pages. I thought not. Next, I considered scripting—dynamically generating the page based on the current move. I decided to set up a "path" array that identified which squares a player could move to from a particular square; during the generation of the page, those squares are enabled only if they are part of the path.

The arrows are set in tiles made up of individual images. An arrow can point in eight possible directions, and I decided to use two tile colors for the checkerboard effect of Adrian's maze, plus the starting tile, plus the ending tile. That added up to 18 images. I used GIF images, and set the arrow color to be transparent, so I could use a table cell background color to change the color of the arrow from "white" to "gold," providing a visual clue as to which tiles a player can click on.


Figure 2. The white and gold arrows

I defined the layout of the maze as another array, denoting which image to display in that location. If nothing else, this allowed me to change the maze to another pattern without too much trouble. The array looked something like:

   var tile = new Array (
   "B_S.gif",   "R_S.gif",   "B_SE.gif",   "R_SE.gif",  "B_SW.gif",
   "R_N.gif",   "B_NE.gif",  "R_SE.gif",   "B_S.gif",   "R_SW.gif",
   "B_NE.gif",  "R_W.gif",   "GOAL.gif",   "R_E.gif",   "B_SW.gif",
   "R_NE.gif",  "B_SW.gif",  "R_NW.gif",   "B_SE.gif",  "R_N.gif",
   "B_E.gif",   "R_N.gif",   "START.gif",  "R_N.gif",   "B_W.gif");

Since the ".gif" could be assumed for each image, and since the "B_" (for BLUE) and "R_" (for RED) could easily be calculated to create the checkerboard pattern during the construction of the maze, I could have shortened each of these to just the compass direction, but I figured that using the full image name worked fine for now, and would perhaps be more flexible later on down the road.

For dealing with the actual playability of the maze, I could use Dynamic HTML (DHTML) to handle the onclick events of the tiles. That way, I could keep the entire process on the client—shifting around the colors and clickability of the page without refreshing. Unfortunately, such a solution would work only for Internet Explorer, not in Netscape Navigator. I could create a different model based on Navigator's method of using Layers to provide dynamic content, but then I'd have problems getting that to work properly on Internet Explorer. So it looked as though using links to force a page refresh that would progress the movement forward was the best cross-browser way to go.

Next, I needed to decide between server-side and client-side scripting. Since my maze doesn't need server-hosted information (such as database access, account authorization, or other forms of external information), I used client-side scripting. That way, the maze can be implemented on any Web server or even from the file system as an .html file a player can double-click on.

Then I needed to figure out exactly how each move would be communicated back to the page itself. Obviously, I would want to encode this into the URL associated with the tile being clicked. While I could have used client-side cookies to store the information, that seemed overly burdensome. It also would have required the user to have cookies enabled, and this is not always the case. Since I had an array that described the "path" that could be taken from any tile, and since my script code was dynamically generating the individual tile locations, I figured it would be easiest to encode the "path" into the HREF, then parse the path back out again as the page was being reconstructed on the return trip. Yes, there are many other solutions, but this seemed like a fun and simple way to do it.

In server-side scripting, parsing out parameters from a URL is pretty easy; unfortunately, it gets harder on the client side. While I could have written up a quick little routine for extracting specific parameters encoded in a URL, I decided to keep it as simple as possible by assuming that the "path=" element would be placed at the end of the URL. Then I grabbed it with:

   var test = new String (;
   var target = new String (test.substring(test.lastIndexOf("path=")+5));

The path itself was coded so that it was very simple to check for a single number within it. I did this by encoding the path to look like ":5:11:15:"—where each number represented a different tile that could be "clickable" at a given stage. That way, as I ran through a "for loop" for each tile of the maze, I could quickly create the array.

   // Build up an array that identifies which tiles are "clickable"
   var tileClickable = new Array(25);
   var i, j;
   for (i=0; i<mazeWidth; i++) {
      for (j=0; j<mazeHeight; j++) {
         if (target.indexOf(":"+((i*mazeWidth)+j)+":") >= 0 ) {
            tileClickable[(i*mazeWidth)+j] = true;
         } else {
            tileClickable[(i*mazeWidth)+j] = false;

This made it easy to walk through the array when I was constructing the table, and to emit the proper code for each table cell individually.

I now had the definition of the maze data, the script code for generating a table based on that data, and the HTML page itself to hold all of this information—and perhaps additional formatting and content that described how to play the maze. The key result was that, as the page was dynamically generated, it emitted table cells as either non-clickable:

   <td bgcolor=white>
   <img src="imagename.gif" border=0 width="55" height="55">

or as clickable:

   <td bgcolor=gold><a href="mazename.html&path=:#:#:#:">
   <img src="imagename.gif" border=0 width="55" height="55">

This allows the user to progress through the maze according to the definition.

I could have stopped here, with a perfectly useable little amusement, but as I was browsing farther through Adrian's site, I found additional maze images that were amazingly similar to the arrow maze that I used for this exercise. The Queens Maze and Rooks Maze were variations on the theme I had just used for my online example.

For the fun of it, I built up pages for each of these mazes. It became clear that a lot of code was the same for each page. I modified the basic script code, making it separate from the maze being constructed, and making it possible to extract it to a separate .js file. Each page can then pull in its respective code using the SRC attribute in the <SCRIPT> element.

   <script language="javascript" src="MazeEngine.js"></script>

If I fine-tuned this code, each of the individual mazes would automatically pull in the requested .js file. However, another benefit of this approach is that it enables the browser's caching capability, so that the browser needs to pull this code down only once. On each subsequent page request, the browser used the cached script code, instead of pulling it all back down through the page being loaded.

This is when I realized that because I was dynamically encoding the URL—even though there was only a single page being used for this entire maze—I was indeed pulling the entire page through the pipe on each move. Granted, the page was extremely small, but I saw here the opportunity to do some more doodling.

I had already pulled out the "engine" code from the page, leaving just the data definition and the HTML within the page. I then pulled the data definition out to a separate .js file, and read it in by using:

   <script language="javascript" src="ArrowMaze1.js"></script>

Now the data definition was also stored in the browser's cache, so it was brought down only once per maze. This way, I built up multiple mazes by creating only the definition script for each new maze. I contained the maze in a minimal HTML file:

   <script language="javascript" src="ArrowMaze1.js"></script>
   <script language="javascript" src="MazeEngine.js"></script>
   <body bgcolor=white>
   <script language="javascript">

Thus, by way of a long and winding road (or was that a winding, long road?), I ended up with a fairly simple Web page that illustrates within it the important concept of cache optimization. No, this isn't rocket science, it probably won't solve world hunger, and it has limited opportunity to promote global peace. But just because bandwidth is getting greater and greater every day doesn't mean that we shouldn't consider factors such as this in the solutions we design. Writing effective Web pages means incorporating a range of features, capabilities, and considerations. I hope that the very simple aspect I illustrated here will give you a sudden flash of insight that you'll be able to use in your own more complex and integrated solutions.

By relying on cached images, script code, and data elements, it's possible to trim pages so that they are not only faster to download, but also faster for programmers to modify and update. The possibilities for use on your sites are endless. Look through the script functions scattered throughout your pages, and think about possibly combining multiple small code fragments into a single, medium-size repository of functions that can be included on a bunch of pages. Also, look at how you might be able to define your pages based on data definitions from an external file. This, too, not only can increase the rendering speed of your pages, but also can help you manage your content.

Playable Mazes

Each of the following mazes is based on designs by Adrian Fisher, a professional maze designer in England. Further information about these and other mazes that he has designed can be found on his Web site. To try out some of these mazes, just click on one of the images below. The maze will appear in a new window. In all of these mazes, you start out from the black square at the bottom center. By following the rules that apply to that particular maze, you can progress through the maze, trying to reach the center, checkered square. Good luck!

Arrow Maze 1

Arrow Maze 1

Arrow Maze 1

Arrow Maze 2

Arrow Maze 1

Rook Maze

Arrow Maze 1

Queen Maze

Robert Hess hosts the MSDN Show.