In A Blink

Stick-figure like images over an image.

Goal Story

Not for the first time, Pam is negotiating away a stack of requirements from the project goals. Having successfully convinced the client "Security Features" was never really worthwhile, she drags a "Magic Chainsaw" sprite from the tool pallete to the "Security Features" requirement, shakes the mouse a couple of times, and the chainsaw is animated for a second as the requirement fades away.

Problem

How can you ensure visual content is flexible?

Forces

Rich visual displays are often the most effective way to present information.

Information frequently changes as the user interacts and new data is received from the server, hence the visual display needs to frequently update.

It's expensive to continuously download new visual content.

Solution

Augment the display with "sprites": small, flexible, icon-like blocks of content. Typically, the sprites are images, but they may also be div elements containing text or images or a combination of both. The name refers to the sprites used in traditional graphics programming, particularly gaming. They are the little graphics representing players, enemies, and physical objects, capable of moving around a scene and animating themselves to reflect current activity. The entire scene does not need to be recomputer each time, only the sprites. DOM elements are not (yet) capable of more advanced features such as arbitrary rotation, but many aspects of sprites can actually be translated to a web context.

Google Maps is a case in point. Search for "museum new york" and you'll see a set of thumbtack Sprites appear on the map, one for each museum. Then click on a thumbtack, and a new speech balloon sprite will show with the precise address. There are several ways to achieve this effect:

Prepare the entire image in advance. But what if you had typed "Restaurant" instead? Or "742 Evergreen Terrace"? What if you'd zoomed in a bit? Google would have to pre-store a new image for each possible search term, an impossibility even for Google.

Prepare the entire image on demand. The search might result in a new image being placed on the page, with a URL like http://maps.google.com/image/museum+new+york, which will trigger the browser to load up the new image. When the server detects this request, it could generate the image on demand. This solution solves the infinite storage problem, but is grossly inefficient. Google's servers will be working overtime to pump out new images, and the user will suffer from a slow response time.

Download the basic map image, along with semantic content indicating the map's coordinates and those of the museums. The browser will then render the whole image, slapping on a thumbtack sprite for each museum.

The sprite solution is the most scaleable in terms of storage, server processing, and user response times. It makes the map a backdrop for whatever arbitrary data needs to be shown. The sprites are completely flexible. Google could use them, for example, to render a personal map showing places of interest to each individual user. All that needs to be downloaded is some semantic content, and the browser handles the rendering.

Sprites are often implemented as div elements, or simply images. They usually appear "in front of" the rest of the document and are sometimes partially transparent. Following are the relevant CSS properties:

zIndex determines how elements are stacked, and sprites usually have a high value in order to appear in front of other elements.

left, right, top, and bottom are often used to position a sprite. position can be set as relative or absolute to influence how the browser interprets these settings.

backgroundImage is used to select a background image when the sprite is a div. As long as there is no foreground content, it is the background image that is shown. The image need not be rectangular, because a transparent GIF file can be used to show any arbitrary shape.

You can also animate the sprite by rapidly changing its appearance. Unfortunately, Javascript cannot easily control animated GIFs, so that's not a viable option. However, animation is easy enough with some Event Scheduling. A simple technique is to set up a recurring "changeImage()" action, in which the element's background-image is continuously run through a cycle of different images. Ideally, you should preload the images first to avoid a slow cycle the first time round. Several tricks are available for preloading, most commonly involving code like "var sprite[i] = new Image(); sprite[i].src = "sprite.gif". An alternative animation technique is outlined in the Code Examples below.

Decisions

What file format will the sprite image be?

Sprites are usually images, but what file format to use --- JPEG, GIF, or PNG? There are several considerations:

Portability

Image quality

File size

Transparency

JPEG may be the most common format on the web, but is ruled out for most sprites. It doesn't support transparent pixels, so the sprite must be rectangular, and it is not actually very effective at storing the kinds of images typically used for sprites: cartoon-like line drawings. So JPEG is only a consideration in the rare case you have a fairly large, rectangular, photo-like sprite.

This leaves GIF and PNG as the most common choices. An intellectual property cloud used to hang over GIF, but note that the patent in question is now expired, and is no longer a consideration. PNG has some technical advantages over GIF, particularly variable transparency, but unfortunately that's not been correctly implemented in IE until IE7. So, to support older IE editions, you'll need to manipulate opacity if you want to achieve transparency, regardless of the format. So, as long as you're supporting IE, the distinction between PNG and GIF becomes fairly arbitrary.

Will the sprite be animated?

Ongoing animation can be distracting, but a quick animation is an effective way to convey what's happening. Consider how the "one-second" visual effects might be used:

One-Second Spotlight: You might Materialise a sprite when it first appears, or you might Fade In the sprite when its subject to some action.

One-Second Mutation: You might Form a sprite when it first appears, or you might rapidly Metamorphise it in a cycle, to provide the illusion of continuous change.

Real-World Examples

DHTML Lemmings

DHTML Lemmings by Tino Zijdel ("Crisp") is a game which uses Sprites in their traditional gaming sense. Lemmings are shown performing different actions, and the characters change shape as they move around the game space. The entire thing is orchestrated with DOM and CSS scripting.

Google Maps

As discussed in the Solution above, Google Maps overlays the basic map with thumbtacks to highlight certain locations. As an interesting variant, look at the use of thumbtacks by Housing Maps, the Maps-Craigslist mashup overviewed in Cross-Domain Proxy.

Quek

Quek is a fun use of sprites as chat avatars. The application is a "chat-and-surf" program, meaning that Quek users can chat about a website they're visiting at the same time. Traditionally, such applications have involved downloads or plugins, but Quek aims for a completely Ajaxian experience. It works by rendering the entire target website, so you're always actually on a Quek page, but the page looks like the same as the target site.

The chat is supported by avatars, Sprites representing the users. Logging into Quek, each user is assigned a unique Sprite consisting of an image, a text ID underneath the image, and an input box (visible only on the user's own avatar). Typing in the input box animates the avatar image, and clicking Enter submits the mssage for all to see. The user is free to drag the avatars around the page. So the appearance is several avatars moving around and talking to each other, with the web page as a backdrop.

Code Examples

DHTML Lemmings

DHTML Lemmings involves the most sophisticated use of Sprites I know of. The analysis here is only a rough summary of what's achieved. Note that the code uses some special handling for IE, which the author, Tino, explained to me is due to a well-known bug where IE bypasses the cache upon any backgorund style change. To keep it simple, the following discussion skims overt IE-specific handling.

In DHTML Lemmings, each lemming is represented by a Lemming class, initialised with a number to identify the lemming, and an initial position corresponding to the place where the lemming first appears on this particular level. Various other state information is also tracked.

Lemmings themselves are regular Javascript objects and not visible in the browser. To visually depict the lemming, a div element is created and made an attribute of the lemming, simply known as "l". Thus, we might say the Lemming "wraps" a Sprite as a means of separating semantic content from visual content. Each of these sprites resides in the "playground", an area representing the game space where the lemmings roam around. Both the playground and the sprites are rendered with absolute positioning. Once the sprite element is created, event handlers are also added to support the gameplay.

At any time, each lemming is performing a single action, such as walking or climbing, and the action is tracked by a property of Lemming, "ani". When the game begins, the lemming falls from a trapdoor, so ani is always initialised to "fall".

function Lemming(i,top,left) {
this.ani = 'fall';
}

Each type of action requires unique animation and movement. A periodic timer ticks every 60 milliseconds, and updates the game's state, including each lemmings' appearance and positioning. Here's how the animation works. Each action has a collection of 32x32-pixel icons associated with it. The icons are all retained in a single image, with the icons strung together horizontally. The lemming's backgroundImage style is always set to the entire image associated with its current action. Because the lemming image is set to 32 pixels, with overflow hidden, you will only ever see one of the icons. To select the appropriate icon, the backgroundPosition property is set. Thus, the backgroundImage remains fixed while the lemming is performing some action, but the backgroundPosition flows rapidly through each of the icons, decrementing by 32 pixels each time until it reaches zero, then back round again.

A single image depicting the entire floating sequence.To depict an activity, the sprite's backgroundImage is fixed to one image, while backgroundPosition continuously cycles through each icon in the sequence.

Whenever the action changes, the script sets the image and initalises the position. Then, for each "tick" of the timer, the icon is rotated by altering the position of the reference image.

Why is the appearance changed using backgroundPositon? Wouldn't it be simpler to just run through a sequence of separate image files, and alter the backgroundImage instead? Via email, Tino explained the reason is performance: The image is held in memory, whereas image-swapping would require images to be retrieved from the cache.

As for motion, each "tick" delegates to a Strategy function for the action taking place. The Strategy function then directly manipulates the Sprite's position. So to model a lemming falling vertically, the div's top increases with each tick:

function lemming_fall_ani(l) {
...
l.top += 4;
...
}

Likewise, a walking action involves a change to the left property.

function lemming_walk_ani(l) {
l.left += l.dx;
}

Alternatives

Tiling

Tiling is a technique in which a big image is built up from small tiles. In fact, Google Maps works this way - each map image is actually a grid of smaller image files. The benefit is that the browser will cache each tile in memory. So if you pan a little in one direction, only the new tiles need to be downloaded. Like Sprites, tiling is a technique you can use to change visual content without extracting everything from the server. And Google Maps shows the two techniques combine effectively.

Related Patterns

Popup is a close cousin of Sprite, as they are defined here, and some examples arguably fit both patterns. Both share the appearance of being "in front of" the rest of the document, typically implemented with the zIndex style. But they differ semantically. The Sprite pattern is intended to cover small objects, often draggable, animated, and icon-like. Often, Sprites are a standard fixture of the application, often have a sense of unique identity, and remain present throughout the application. Popup is more about a transient, informational, block of content which appears to show something or accept some input, then vanishes again. Popups tend to be larger, more text-based, and often dynamic in content.