
The Art of Flash 5 Preloading
by Colin Moock
05/29/2001
In Flash, a preloader is a code module that pauses a movie until some
required body of data has finished downloading, guaranteeing the proper
playback of the movie. For example, a preloader can ensure that animation or
sound has sufficiently buffered before playing, or that a series of variables
has loaded before being manipulated and displayed. For each of these types of
data, a different technique is used to build the corresponding preloader. Over
the course of this article we'll look at those techniques, starting with the
simplest: a preloader for a single movie. Note that the code examples in this
article all use Flash 5 ActionScript syntax.
Download the Samples
The preloader examples described in this article are available for
download here. Note that the music player
example has been extended to provide a draggable playhead (not covered in this
article).
Single-Movie Preloaders
A single Flash .swf file is a self-contained unit. Its
contents--graphics, sound, movie clips, buttons, and scripts--are dispersed
across the frames of its main timeline. By monitoring the download progress of
the main timeline's frames, we can prevent a movie from playing before
adequate content is available. When enough frames have loaded, we play the
movie. Follow these steps to create a basic, single-movie preloader:
Create a new movie.
Add 100 frames to the default Layer 1 layer (you can actually
use any number greater than 15, but we'll stick to 100 for this
example).
At frame 15 of the Layer 1 layer, add a blank keyframe
(Insert-->:Blank Keyframe)
Between frames 15 and 100 of the Layer 1 layer, add keyframes
with plenty of content (sound and bitmaps are nice and bulky).
On the main timeline, create a new layer called
scripts.
On the main timeline, create a new layer called
labels.
At frame 4 of the labels layer, add a blank keyframe.
Use Modify-->Frame to label the new keyframe
loading.
At frame 15 of the labels layer, add a blank keyframe.
Label the new keyframe beginMovie.
At frame 5 of the scripts layer, add a blank keyframe, and
attach the following code to it:
// Specify how many frames to load before playing.
var loadAmount = _totalframes;
// If the required number of frames have finished loading...
if (_framesloaded == loadAmount) {
// ...start the movie
gotoAndPlay("beginMovie");
} else {
// ...otherwise, go back and check load progress again.
gotoAndPlay("loading");
}
That's it, your preloader is done. Now you need to make sure it works. To
simulate movie download at various modem speeds we use a testing tool called
the Bandwidth Profiler, available in Test Movie mode. Here's how to turn the
Bandwidth Profiler on:
- While editing your movie, select Control-->Test Movie.
- In Test Movie mode, select View-->Bandwidth Profiler.
- Under the Debug menu, select the desired download rate.
- To simulate the download of a movie at that rate, select
View-->Show Streaming.
Keep an eye on the green bar at the top of the Bandwidth Profiler. It shows
the simulated download progress of the movie. Notice that playback pauses at
frame 4 while your movie loads. If the delay is too long at the target speed,
you should consider splitting your movie into pieces. Add multiple
preloaders to your main movie timeline or separate your content into
individual .swf files which you load only as necessary (we'll cover
preloading multiple .swf files later). Note that the Bandwidth
Profiler can only be used to test single-movie preloaders; it doesn't show the
progress of loaded variables or XML, or movies loaded via
loadMovie().
To see several inventive examples of preloaders, visit Colin's
recently launched companion
site for ActionScript: The Definitive Guide. The book's trailer
movie uses eye candy (rotating 3-D orbs) as a preloader, while the Flash
site itself transforms loading bars into graphical elements in a navigation
menu. (Of course, Flash is required.)
Now that you've seen your preloader in action with the Bandwidth Profiler,
let's consider how it works by examining the code you attached to frame 5.
In its first statement, we create a variable, loadAmount, that
indicates how many frames we're going to preload.
var loadAmount = _totalframes;
In our example, we set loadAmount to _totalframes, a
built-in ActionScript property that stores the number of frames in a movie
clip or main timeline. By using _totalframes, we keep our code
generic--the number of frames we want to preload is the number of frames in the
timeline, as determined by ActionScript.
However, we should remember that Flash streams content (it can play a
movie while it continues to load). It is often wise to preload only part of a
movie, allowing the rest to load while the user is occupied watching the
show. To preload only a portion of a movie, we set loadAmount to
some number less than _totalframes. Use the Bandwidth Profiler to
determine the appropriate portion to preload at your site's target modem
speed.
Having decided how many frames to preload (in our case, all of them), we
next check whether that amount has finished downloading or not. We compare the
built-in property _framesloaded (which tells us how many frames have
loaded) with loadAmount (which indicates how many frames must be
loaded before the movie plays):
if (_framesloaded == loadAmount) {
If the two values are equal, the required frames have loaded, so we can
start playing the movie:
gotoAndPlay("beginMovie");
But if the two values are not equal, the required frames have not loaded, so
we send the playhead back to the loading frame (frame 4 in our
example):
gotoAndPlay("loading");
There, the movie resumes play, and re-enters frame 5, where we check how
many frames have loaded again. The playhead, therefore, is stuck in a timeline
loop until the required number of frames have loaded, at which point it safely
proceeds to the beginMovie frame. Simple, but effective.
Some notes about the technique:
Our preloader starts at frame 5 rather than frame 1 because scripts on
the first frame of a movie are very occasionally skipped by the Flash player.
Leaving some room before our preloader also makes adding new content to the
start of the movie easier.
To perform our preload check less frequently we can add new frames
between frames 4 and 5. This might be handy if we're playing a preload
animation or sound that should terminate only at a specific point.
We don't use the old-school ifFrameLoaded statement in our
preloader. That's been deprecated and is less flexible than our code.
Low Tech: Preloading with Layer Load Order
Having just built a preloader that starts at frame 5, you might be wondering
what would happen if there were content on frames 1 through 4. If a frame is
not fully loaded when Flash attempts to render it, its contents are displayed
one layer at a time as they load. Layers load from either the bottom up or
the top down, depending on the load order set for the movie under
File-->Publish Settings-->Flash-->Load Order.
We can use this to our advantage to build a "pre-preloader". By simply
separating, say, a logo, onto individual layers, we can create a basic
animation that indicates initial load progress to the user. In Figure 1, the
letters in the word "moock" are each placed on their own layer, resulting in
the animation depicted in Figure 2.

Figure 1: Separating content onto layers.

Figure 2: The progressive display of content on
layers.
A simple animation based on layer load order tells the user that the movie
has started downloading, but it doesn't indicate how long they have left to
wait. We'll learn how to do that next.
Displaying Load Progress
In our first attempt at a preloader, we successfully delayed the playback of
a movie until a specified number of frames had downloaded. We'll now see how
to show some signs of life to the user while our movie loads (otherwise the
Flash Player may look conspicuously stalled). We'll add the following features
to our single-movie preloader (shown in Figure 3):
- a text field showing the file size of the movie, in kilobytes
- a text field showing the number of downloaded kilobytes
- a text field showing movie download progress as a percentage
- a preload bar that graphically depicts the movie's download status

Figure 3: On-screen display of load progress.
We've got some Flash production work to do, and then we need to update our
preload script. We'll start by creating layers on the main timeline to hold
the progress-display content:
On the main timeline, create two new layers, one named bar and
the other text fields.
At frame 4 of the bar and text fields layers, add a
blank keyframe.
At frame 15 of the bar and text fields fields layers,
add a blank keyframe.
Now we'll create the preload bar which is composed of two movie clips:
Create a movie clip symbol named loadBarHousing.
In loadBarHousing, create an 8-pixel high, 140-pixel wide
rectangularly shaped outline. (Make sure the rectangle has no fill.)
Select the rectangle in loadBarHousing, then choose
Window-->Panels-->Info.
Position the rectangle's left edge on the clip's registration point by
setting its X-coordinate to 0 and its Y-coordinate to -4.
Create a movie clip symbol named loadBar.
In loadBar, create an 8-pixel high, 1-pixel wide solid
rectangle. (Make sure the rectangle has no outline).
Select the rectangle in loadBar, then choose
Window-->Panels-->Info.
Position the rectangle's left edge on the clip's registration point by
setting its X-coordinate to 0 and its Y-coordinate to -4.
The preload bar clips are done; now place them on the Stage:
At frame 4 of the bar layer, place an instance of
loadBarHousing.
Name the instance loadBarHousing
(Modify-->Instance).
At frame 4 of the bar layer, place an instance of
loadBar.
Name the instance loadBar.
Select the loadBarHousing and loadBar
instances.
On the Align panel (Window-->Panels-->Align) select the Align Left
Edge button (top-left button on the panel) and the Align Bottom Edge button
(top-right button on the panel).
Finally, create the text fields to hold the kilobyte and percentage
information for the preloader:
Select the Text tool.
At frame 4 of the text fields layer, drag out a text box big
enough to hold a three-digit number.
Choose Text-->Options.
In the Text Options panel, change Static Text to Dynamic Text.
In the Text Options panel, select the Include Entire Font Outline option
under Embed Fonts (the [...] button). (Note that in a production situation
you should only embed the characters you actually use in your text fields--in
our case, numbers and the percent sign).
In the Variable text field, type bytesLoadedOutput.
Repeat steps 1 through 6 to create two more text fields, named
bytesTotalOutput and percentOutput.
Add regular static text next to each text field describing its content
as follows: Loaded: , Total: , Percent: .
Now the fun part--update the code on frame 5 so it reads as follows
(we're only changing the contents of the else clause, but the code
is shown in its entirety):
// Specify how many frames to load before playing.
var loadAmount = _totalframes;
// If the required number of frames have finished loading...
if (_framesloaded == loadAmount) {
// ...start the movie
gotoAndPlay("beginMovie");
} else {
// ...otherwise, display the load status
// then go back and check load progress again.
// First, determine the loaded and total kilobytes.
loaded = Math.round(getBytesLoaded() / 1024);
total = Math.round(getBytesTotal() / 1024);
percent = Math.round((loaded/total) * 100);
// Display the loaded kb, total kb, and percentage of
// kb loaded in text fields.
bytesLoadedOutput = loaded;
bytesTotalOutput = total;
percentOutput = percent + "%";
// Set the width of the loadBar to a percentage of
// the loadBarHousing.
loadBar._width = loadBarHousing._width * (percent / 100);
// Now go back and check load progress.
gotoAndPlay("loading");
}
Use the Bandwidth Profiler to test your preloader. The more content you have
on your timeline, the more you'll see the preload bar working.
Let's examine the changes to our code on frame 5. As before, we'll only
begin the movie if the number of frames loaded matches our required
loadAmount:
if (_framesloaded == loadAmount)
But this time, if the required number of frames hasn't loaded yet, we'll
update our on-screen text fields and preload bar before sending the playhead
back to the loading frame. First we calculate how many kilobytes
have loaded. The getBytesLoaded() movie clip method tells us how
many bytes have loaded; we divide its return value by 1024 to convert to
kilobytes. To keep our output readable, we round off the result using
Math.round():
loaded = Math.round(getBytesLoaded() / 1024);
Next we calculate how many kilobytes are in the entire movie. We use the
getBytesTotal() method to determine the movie's size in bytes.
Dividing by 1024 and rounding once again gives us kilobytes:
total = Math.round(getBytesTotal() / 1024);
To compute what percent of the movie had downloaded, we divide
loaded by total and multiply the result by 100. Using
Math.round(), we round the percent to the nearest integer for
display:
percent = Math.round((loaded/total) * 100);
To display our calculated values on screen, we assign loaded,
total, and percent to corresponding text fields:
bytesLoadedOutput = loaded;
bytesTotalOutput = total;
percentOutput = percent + "%"; // Add a % sign for display purposes.
Finally, we set the width of our preload bar. We know that loadBar
should be as wide as loadBarHousing when our movie is completely
loaded. While our movie is still loading, loadBar's width should be
some percentage of loadBarHousing's width. We've already calculated
the percent of the movie that has loaded, so now we simply set
loadBar's _width property to the appropriate percent of
loadBarHousing's _width property, in accordance with our
percent variable:
loadBar._width = loadBarHousing._width * (percent / 100);
With our display updated, we send the movie back to the loading
frame, where it will play and execute our preload check again:
gotoAndPlay("loading");
Reader Exercise: Ideally, while our movie loads we should distract the user
with eye candy, a prelude to a story-line, a game, or some other toy. See if
you can add these things by creating an entertaining movie clip placed on
frame 4. Be careful to make your distraction small enough to load quickly.
If it's too big, you'll have to preload your preloader!
Multiple-Movie Preloaders
Using loadMovie() we can load external .swf files into
movie clips (called targets) or document levels. Externally loaded
.swf files can be preloaded exactly like single Flash movies. For
example, consider a Flash site with a main menu that links to three
sections: Products, Contact, and About Us. Each section resides in a
separate .swf file: products.swf, contact.swf, and
aboutus.swf. The site's homepage, menu.swf, provides
buttons that load each section into level 1, as follows:
on (release) {
loadMovie("products.swf", "_level1");
}
To preload, say, the Products section, we place a single-movie preloader
directly on the main timeline of products.swf. When
products.swf is loaded onto level 1, its preloader will manage the
download.
There are times, however, when this approach is inefficient. For example,
suppose we're building a music player that loads multiple soundtrack
.swf files into a single target instance named host. Rather
than create a separate preloader for every .swf file, we create a
single, generic preloader that manages the download of any file loaded into
host.
Let's see how this works. We start with a series of buttons that load our
soundtrack movies into the host clip. The code on our first button
looks like this:
on (release) {
host.loadMovie("soundtrack1.swf");
}
When soundtrack1.swf loads into host, we want
host to automatically preload the file. We need to add a little
intelligence to the host clip. When a movie is loading into the
host clip it should call a custom preload() function to
handle the download. We'll use the following enterFrame movie clip
event handler to monitor the file in host and to call
_root.preload() when necessary:
onClipEvent (enterFrame) {
// If the host clip contains an external .swf file...
if (this._url != _root._url) {
// ...call our preload() function, which displays the
// loading .swf file's download progress.
_root.preload(this);
}
}
As each frame passes, the code in host's enterFrame
handler is executed. If host's filename (this._url) does not
match the filename of the main music player (_root._url), then an
external movie must be in host, so we should run our
preload() function. The preload() function will display load
progress and optionally determine when to play the movie in host.
Before we look at how preload() works, it bears mentioning that the
enterFrame handler just shown is not the optimal handler to use in
this situation. Ideally, we'd call preload() from a data event,
which only executes when data loads into a movie clip. Unfortunately I've
found that the data event doesn't always fire on fast connections (DSL,
Cable, T1). This is an unverified bug, but it was problematic enough to
warrant moving the preload() call to an enterFrame handler.
I've reported my findings to Macromedia and will post any resolutions on
my Web site.
Now, back to the preload() function. Skim through it to get a sense
of how it works, then we'll dissect it.
function preload (theClip) {
if (!theClip.doneLoading) {
// If we have all the frames, make a
// note that download is complete.
if (theClip._framesloaded > 0
&& theClip._framesloaded == theClip._totalframes) {
theClip.doneLoading = true;
// Optionally start the clip once it's done loading...
// theClip.play();
} else {
// Optionally pause the clip until it's loaded...
// theClip.stop();
}
// Display loading byte counts in text fields.
bytesLoadedOutput = theClip.getBytesLoaded();
bytesTotalOutput = theClip.getBytesTotal();
// Strip out the file name of the .swf loading into the
// clip and display it in a text field.
var lastSlash = theClip._url.lastIndexOf("/");
clipURLOutput = theClip._url.substring(lastSlash + 1,
theClip._url.length);
// Set the width of the loading bar.
var percentLoaded = (theClip.getBytesLoaded()
/ theClip.getBytesTotal());
preloadBar._width = preloadBarBG._width * percentLoaded;
}
}
[Editor's note: Lines 20 and 22 were extended to two lines each to
accommodate our Web site's formatting. Throughout this article, whenever a line of code has been broken up into two lines, the second line is indented.]
The preload() function takes one parameter, theClip, which
is a reference to the clip to preload (allowing for the potential of more
than one host clip):
function preload (theClip) {
Recall that our preload() function is called once per frame for as
long as an external .swf file resides in host. It's a waste
of time to execute any preloading code if that file is fully loaded, so
preload()'s first job is to check whether or not theClip
has finished loading:
if (!theClip.doneLoading) {
The variable doneLoading will be set in theClip when
loading completes; if it doesn't exist, then we need to run our preloading
code. There's not much new in the preloading code itself. We start by
checking whether the number of frames loaded so far equals the movie's total
number of frames, but we also make sure that there's at least one frame
loaded.
if (theClip._framesloaded > 0
&& theClip._framesloaded == theClip._totalframes) {
The _framesloaded > 0 safeguard is necessary when working with
movies loaded into target clips because unloading a clip's contents sets its
_totalframes to 0. Hence, on a very slow connection the comparison
_framesloaded == _totalframes can return true even when no
frames have yet loaded (because _totalframes and
_framesloaded would both be zero).
When we find that all the frames in theClip have loaded, we set a
flag indicating that we no longer need to run our preloading code.
theClip.doneLoading = true;
At this point we could also play the movie (knowing that it has safely
loaded) by simply executing:
theClip.play();
However, in our music player example, we'll let the movie start playing
immediately as soon as it loads, much like an MP3 player, so there's no need
to invoke theClip.play().
If all the frames of theClip have not yet loaded, then we might
stop it from playing prematurely as follows:
} else {
theClip.stop();
}
But again, in our example there's no need to stop the movie; we let it play
while the music streams to the Flash player. In any event, we'll always want
to update the progress display text fields and preload bar. This time we'll
display raw bytes rather than converting to kilobytes as we did in earlier
examples:
// Display loading byte counts in text fields.
bytesLoadedOutput = theClip.getBytesLoaded();
bytesTotalOutput = theClip.getBytesTotal();
// Strip out the file name of the .swf loading into the
// clip and display it in a text field.
var lastSlash = theClip._url.lastIndexOf("/");
clipURLOutput = theClip._url.substring(lastSlash + 1,
theClip._url.length);
// Set the width of the loading bar.
var percentLoaded = (theClip.getBytesLoaded()
/ theClip.getBytesTotal());
preloadBar._width = preloadBarBG._width * percentLoaded;
Notice that we display the filename of the loading movie by extracting
everything after the last slash in theClip._url. That's just one of
the endless ways to customize a preloader. You might also fade a clip in as a
movie loads by setting its _alpha property, or increase the volume
of a sound, or calculate how long a file is taking to transfer and estimate
the time remaining. In the source file for our music player example, we
indicate not only how much of the movie loaded, but also how much of it has
played.
Preloading Attached Sounds
Now that we've built a preloader for movies loaded into a target clip, we
can easily use it to load attached sounds. An attached sound is a sound added
dynamically to a movie at runtime. To attach a sound we first export it from
the Library (under Options-->Linkage), then we use an instance of the
built-in Sound class to create, attach, and play the sound.
For example, the following code adds a sound with the Symbol Linkage
Identifier loudBang to the Sound object bang. The
code then starts and stops the bang sound.
// Create a new Sound object scoped to the _root timeline.
bang = new Sound(_root);
// Retrieve the loudBang sound from
// the library and attach it to bang.
bang.attachSound("loudBang");
// Start the bang sound
bang.start();
// Stop just the bang sound
bang.stop("loudBang");
Any sound exported from a movie's library is downloaded in that movie's
first frame, causing a load delay before the movie starts. In order to avoid
the delay, we can place exported sounds in a separate .swf file that
we load into a target clip when needed (exactly like we loaded movies in our
music player example). For example, suppose we've stored our exported sounds
in linkedSounds.swf. We use our multiple-movie preloader to load
linkedSounds.swf into our host movie clip. When the file has
completely loaded we can safely attach and play its exported sounds as
follows:
// Create a new sound with a target of host.
host.bang = new Sound(host);
host.bang.attachSound("loudBang");
host.bang.start();
Notice that the sound being attached must be available in the Library of the
Sound object's specified target (in our case, host).
It's a common mistake to omit the target argument when constructing
Sound objects in loaded movies. If not supplied, target
defaults to _level0, where the sound cannot be found. For example,
the following attempt to play a sound in host would fail because the
sound loudBang is not available in the library of
_level0:
// Create a new sound with an implied target of _level0.
host.bang = new Sound();
// This operation fails..."loudBang" is not in _level10's Library.
host.bang.attachSound("loudBang");
// The sound won't start because it wasn't successfully attached.
// We forgot to specify a target when we constructed host.bang.
host.bang.start();
Preloading XML and Loaded Variables
Loading XML and variables is thoroughly covered in the pages of
ActionScript: The
Definitive Guide, so I won't repeat myself here. The following executive
summary should help you find the relevant information in either that book or
Macromedia's documentation.
To preload an XML document, we use the onLoad() callback handler of
the XML class. The onLoad() handler automatically executes
when Flash finishes loading and parsing an XML document requested via
XML.load() or XML.sendAndLoad(). In a typical
implementation, we invoke XML.load(), then send the movie to a frame
with a loading message, then wait for the onXML() handler to tell us
it's safe to resume playback (e.g., process the data and display it).
To intercept the raw data loaded by an XML object, we can
alternatively use the undocumented onData() handler. It is
unfortunately not possible in Flash 5 to determine the load progress of an
XML document being retrieved. For more information see the entries for
XML.send(), XML.sendAndLoad(), XML.onData(), and
XML.onLoad() in the Language Reference of
ActionScript: The
Definitive Guide. (The relevant sample chapter is
posted on my Web site). See also Macromedia's Flash XML primer for more information.
For loaded variables, we also use an event handler to handle preloading. We
start by invoking the loadVariables() function, which imports
variables from a server side script (or text file) into a movie clip's
timeline. While we're waiting for the variables, we display a loading frame.
When the variables arrive, the clip's data event is triggered, and
we carry on with the movie. A common alternative (which is Flash 4 friendly)
to the data handler is to run repeated checks for the value of our
last loaded variable, and proceed only when that variable becomes defined. In
either case, we unfortunately can't determine the load progress of our
variables; we can only detect when they have fully loaded.
For a full step-by-step tutorial on loading variables, see Chapter 17 of
ActionScript: The
Definitive Guide. Happy preloading!
Colin Moock has been researching, designing, and developing for
the Web since 1995. Colin served as Webmaster for SoftQuad Inc. (makers
of HoTMetaL PRO) until 1997. He is now a Web evangelist for ICE
Integrated Communications & Entertainment, where he divides his time
between writing about the Web, speaking at conferences, and creating
interactive content for companies like Sony, Levi's, Nortel, Air Canada,
and Hewlett-Packard. Colin's award-winning Flash work and his renowned
support site for Flash developers have made him a well-known personality
in the Flash developer community. Macromedia has officially recognized
his Flash expertise both on their Web site and by appointing him a
member of their Flash Advisory Board. Colin is a contributing author of
The Flash 4 Bible (1999, IDG Books) and The Flash 5 Bible
(2001, IDG Books). He is the author of O'Reilly's ActionScript: The
Definitive Guide. For more information on Colin Moock, visit his
Web site.

|