The right way to do a preloader in AS3

I wanted to write this post for a very long time but Lee Brimelow beat me to it. But that is alright because he confirmed that the way I was doing my preloaders was a good one.

In AS2 we were used to seeing preloaders in the 2 first frames of a flash movie, but that technique didn’t work well when you used Classes and Library object exported for ActionScript. Even in AS2, I started making my preloader external. An external preloader is a swf that sole purpose is to load another swf.

I suggest you watch Lee’s video because it explains very well why you should use an external preloader and how to do it.

I think his method can be perfected to be even more memory and CPU efficient.

Let’s take a look at the code he uses:

var l:Loader = new Loader();
l.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loop);
l.contentLoaderInfo.addEventListener(Event.COMPLETE, done);
l.load(new URLRequest("content.swf"));
 
function loop(e:ProgressEvent):void
{
  var perc:Number = e.bytesLoaded / e.bytesTotal;
  percent.text = Math.ceil(perc*100).toString();
}
 
function done(e:Event):void
{
  removeChild(percent); // was removeChildAt(0) but it make more sense this way
  percent = null;
  addChild(l);
}

That’s pretty simple and straight forward. Lee makes sure to remove assets he used for is preloader to free memory, which is good,  but if you really wanted to push things a bit farther here is what you would do:

var myMovie:Sprite;
 
var l:Loader = new Loader();
l.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loop, false, 0, true);
l.contentLoaderInfo.addEventListener(Event.COMPLETE, done, false, 0, true);
l.load(new URLRequest("content.swf"));
 
function loop(e:ProgressEvent):void
{
  var perc:Number = e.bytesLoaded / e.bytesTotal;
  percent.text = Math.ceil(perc*100).toString();
}
 
function done(e:Event):void
{
  removeChild(percent); // was removeChildAt(0) but it make more sense this way
  percent = null;
 
  myMovie = Sprite(l.content);
 
  addChild(myMovie);
 
  l.contentLoaderInfo.removeEventListener( ProgressEvent.PROGRESS, loop);
  l.contentLoaderInfo.removeEventListener(Event.COMPLETE, done);
  l = null;
}

First difference you might find is that I set the weakReference of the listeners to true. It doesn’t serve any purpose in this case but I think it is a good practice and a good habit to always put weakReference to true for you listeners.

The second thing you might see is that I don’t add the Loader to the stage. I don’t want to do that because I want to get rid of the Loader. It did it’s job, now I don’t want it using memory for nothing. So instead I created a Sprite called myMovie and I give it the content of the Loader. For this to work, the content of the Loader as to be a Sprite, but you can always change the type of myMovie to adapt to what you are loading. Once I gave myMovie the content I add it to the stage.

Now that the Loader handed off its package I can get rid of it. The first step to do so is to remove the listeners. Listeners take up CPU resources and in this case they will never be used again so I might as well remove them. Once this is done I set the Loader to null to ged rid of it. I could have just done that and not remove the listeners before because I used weakReferences and the GarbageCollector would have destroyed them, but why wait until then when you can do it now.

Well that was it, it is really just a little cleaner but I think that if someone was looking to push this further that’s how he would do this.

Do you know any better way of preloading?

, , , , , ,

  1. #1 by Theo - September 19th, 2008 at 03:20

    One interesting aspect of using a separate SWF for preloading is that it completely removes what little possibility there was that Google would index your Flash site, at least until they start indexing dynamically loaded content.

    I’m not saying you shouldn’t use this preloading/bootstrapping technique, on the contrary, I think it’s great. What I am saying is that Google’s new Flash indexer is completely useless, because it doesn’t take in consideration the way Flash developers actually create Flash content. This is an example of that.

  2. #2 by funkyboy - September 19th, 2008 at 04:40

    To help garbage collection even more, I’d move up removeListener() to line 16.

  3. #3 by doug - September 19th, 2008 at 06:06

    Nice idea but shouldn’t you change this:

    l.removeEventListener( ProgressEvent.PROGRESS, loop);
    l.removeEventListener(Event.COMPLETE, done);

    to

    l.contentLoaderInfo.removeEventListener( ProgressEvent.PROGRESS, loop);
    l.contentLoaderInfo.removeEventListener(Event.COMPLETE, done);

  4. #4 by doug - September 19th, 2008 at 06:08

    Here’s my example:

    package com.deceptiveresolution.kohive.swf.preloading
    {
    import flash.text.TextField;
    import flash.events.ProgressEvent;
    import flash.events.Event;
    import flash.display.Loader;
    import flash.net.URLRequest;
    import flash.display.MovieClip;
    import flash.display.Sprite;

    public class PreloadingContainer extends MovieClip
    {

    private var __container:Sprite;
    private var __preloader:Loader;
    public var percent_txt:TextField;
    private static const __APP_URL:String = “TextChat.swf”;

    public function PreloadingContainer()
    {
    trace(“\nPreloadingContainer()”);
    __preloader = new Loader();
    __preloader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, _onPreloadProgress, false, 0, true);
    __preloader.contentLoaderInfo.addEventListener(Event.INIT, _onPreloadInit, false, 0, true);
    __preloader.contentLoaderInfo.addEventListener(Event.COMPLETE, _onPreloadComplete, false, 0, true);
    __preloader.load(new URLRequest(__APP_URL));
    }

    private function _onPreloadProgress(e:ProgressEvent):void
    {
    trace(“\nPreloadingContainer._onPreloadProgress()”);
    var percentage:Number = e.bytesLoaded / e.bytesTotal;
    percent_txt.text = String(Math.ceil(percentage * 100));
    }

    private function _onPreloadInit(e:Event):void
    {
    trace(“\nPreloadingContainer._onPreloadInit()”);
    percent_txt.text = “100″;
    }

    private function _onPreloadComplete(e:Event):void
    {
    trace(“\nPreloadingContainer._onPreloadComplete()”);
    removeChild(percent_txt);
    percent_txt = null;
    __container = Sprite(__preloader.content);
    addChild(__container);
    __preloader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, _onPreloadProgress);
    __preloader.contentLoaderInfo.removeEventListener(Event.INIT, _onPreloadInit);
    __preloader.contentLoaderInfo.removeEventListener(Event.COMPLETE, _onPreloadComplete);
    __preloader = null;
    }

    }

    }

  5. #5 by doug - September 19th, 2008 at 06:10

    @Theo

    Another good point but we could just have a load of static text in our preloading swf describing our site with perhaps links to more detailed external assets… just an idea for now.

  6. #6 by zedia.net - September 19th, 2008 at 09:21

    @Theo
    You are totally right, but as you say I don’t think it is of any use to try to make your SWF searchable for Google. Progressive enhancement is still the way to go.

  7. #7 by zedia.net - September 19th, 2008 at 09:27

    @Doug
    Thx for pointing out my error on the remove listener. It is fixed now.

  8. #8 by barry - September 23rd, 2008 at 03:53

    HI,

    I’ve read a lot of articles that suggest you should set use weak reference to true. Is there any instance when you might want it to be false?

  9. #9 by zedia.net - September 23rd, 2008 at 13:06

    @barry
    The only case where you would want weak reference set to false on a listener is when the only thing that keeps the object, that the listener is on, from being Garbage Collected is the listener itself. And I don’t see any application of this so I always set the weak reference to true.

  10. #10 by Tunde - October 8th, 2008 at 22:23

    please go to 8landphoto.com, click on work and you should see my problem is there a step by step way to fix this?

  11. #11 by panorama360 - October 23rd, 2008 at 13:25

    very usefull, it saved me a lot of time! thanx

  12. #12 by Garfty - October 29th, 2008 at 08:22

    It’s nice to see this, and it’s the way i’ve been doing it after reading various articles etc etc…

    My only question is how would you do a nice dynamic loader, one that fades in, does the load, then fades out to reveal the content. There is a big spaceship version which is nice, however I feel this is quite complex still.

    Cheers for the post, cleared a couple of niggly things up for me

    ^_^

  13. #13 by Sharpy - November 10th, 2008 at 22:32

    This works well BUT … content.swf starts playing BEFORE event.COMPLETE fires! So by the time your progress hits 100%, your main file content is many frames into the animation.

    Any ideas how to stop/pause the content.swf until its fully loaded?

  14. #14 by zedia.net - November 12th, 2008 at 15:38

    @sharpy
    Put a stop on the first frame of the movie that is being loaded and in the onComplete function of your preloader start the content. Using the code I have written up there it should look something like this:

    myMovie = MovieClip(l.content);
    addChild(myMovie);
    myMovie.play();

    In my exemple the loaded content was a Sprite but you would have to change it to MovieClip to make your thing work.

  15. #15 by John Smith - November 22nd, 2008 at 08:28

    I took this concept and added to it. I am building a thumb panel, so I wanted all of my thumbnail urls to be held externally in an XML file. Basically what this does is loads a series of .jpgs of .swfs and increments the x coordinate of the dynamic text field everytime i changes. Here is my code. I am just learning how to garbage collect, so I would appreciate if you would look at my code and critique it.

    var xml:XML;
    var xmlPath:String = “./Gallery1/gallery.xml”;
    var imagelist:XMLList;
    var numberOfImages:int;
    var thumbUrl:String;
    var thumbx:Number=0;
    var i:Number=0;
    var ihaschanged:Boolean = false;
    var thmbLoader:Loader = new Loader();

    var loader = new URLLoader();
    loader.load(new URLRequest(xmlPath));
    loader.addEventListener(Event.COMPLETE, xmlLoaded);

    //create a dynamic preloader text field
    var preloaderText = new TextField();
    preloaderText.name = ‘textfield’;
    preloaderText.selectable = false;
    preloaderText.x = 27;
    preloaderText.y = 20;
    preloaderText.width = 30;
    var txtFormat:TextFormat = new TextFormat();
    txtFormat.color = 0×000000;
    txtFormat.size = 12;
    preloaderText.setTextFormat(txtFormat);
    addChild(preloaderText);

    function xmlLoaded(e:Event):void {
    xml = new XML(e.target.data);
    imagelist = xml.gallery.image;
    numberOfImages = imagelist.length();
    createThumbnail();
    }

    function createThumbnail():void{
    if(i

  16. #16 by John Smith - November 22nd, 2008 at 08:30

    looks like the bottom half got cutt off so here is the rest of it…

    function createThumbnail():void{
    if(i

  17. #17 by Fernando - December 16th, 2008 at 03:17

    I used this method before, but the problem is that it doesn’t work in ie 6 or 7 and google chrome too, I don’t know why. I search in google but I can’t find nothing about this bug. I’m using the flash player v.10, any one have an idea?

  18. #18 by Beebs - February 18th, 2009 at 19:12

    I am using this preloader as external document class in the movie (let’s say website.swf) to load external movie (let’s say main.swf). The main.swf is all constructed with external document classes.

    I always got Error #1009: Cannot access a property or method of a null object reference.

    Any suggestion?

  19. #19 by Beebs - February 20th, 2009 at 07:11

    Hi It’s me again.

    I manage to deal with my post#18 above.

    However, the further issue arrive : I load more than 2 external movie into var myMovie.

    Although I keep the “l = null” , the movie are sit on top each other instead of replacing them.

    Can you shed me alight how to unload the old one and load the new one?

    Thanks a lot

  20. #20 by zedia.net - February 20th, 2009 at 10:34

    You have to remove your previous content from the display list before you set it to null. The addChild method create a link to the object so as long as you didn’t remove that link, that object cannot be destroyed(garbage collected).

  21. #21 by Beebs - February 28th, 2009 at 05:22

    Thanks for your reply.

    I have further wish list. Since I love your approach of this preloader, how possible to have them in a document class?

    The reason I have this wish list is that I will use the preloader in many places ( like child movies) – so it will be handy if I can call it in a function – something like this.

    Thanks Zedia

  22. #22 by Bill - March 30th, 2009 at 09:09

    I find the little pop-out thing(“annoying” reply – quote) blocks the text when trying to read what people posted .

  23. #23 by Deon - August 30th, 2009 at 23:57

    I did everything exactly as written in the original post, and it works fine offline, but once it is on my server the text starts at 100, and with a trace of “Math.ceil(perc*100).toString()” I see it looping the percent count over and over. This seems to be the case with every preloader I try to use, any suggestions?

  24. #24 by Deon - September 6th, 2009 at 15:10

    I wanted to leave an answer in case someone stumbled in here with my problem.

    “Gzip compression is on in the server; turn it off it will be ok. Do so by using a .htaccess file and in it write this without qoutes ” Options +FollowSymLinks
    RewriteEngine on
    RewriteRule \.swf$ – [E=no-gzip:1] “

  25. #25 by mark mun - October 14th, 2009 at 21:34

    What do you do if your loaded swf also has external assets that it needs to load that needs to be displayed in the loader swf progress bar?

  26. #26 by DeltaFrog - February 19th, 2010 at 22:23

    If your loading the swf into the other swf does the browser know too look for newly updated swf’s with this method? In as2 I always had to add a random id number too loaded swf’s. Any thoughts on that?

  27. #27 by DeltaFrog - February 20th, 2010 at 01:18

    I got a “Cannot access a property or method of a null object reference” error when I used this method to load my working swf. You do have to worry about how you code your file. For me it was not plug and play… ;(

  28. #28 by DeltaFrog - February 20th, 2010 at 03:02

    Ok sry for my rants up there, long day. ;D Everything seems to be working now.

    Any suggestions on getting these two line to run with a delay?

    myMovie=Sprite(l.content);
    addChild(myMovie);

    I tried Set interval but it didn’t work for me. Thanks in advance.

  29. #29 by Karl - April 21st, 2010 at 11:09

    Hey Guys,

    Any way we could get the content to load into a container movieclip (called ‘container’ on main timeline) as at the moment the content.swf is loading over everything in the main.swf..

    Here is my code

    //Loading Default Section
    var Xpos:Number = 0;
    var Ypos:Number = 0;
    var swf:MovieClip;
    var myMovie:Sprite;
    var loader:Loader = new Loader();

    loader.x = Xpos
    loader.y = Ypos
    addChild(loader);
    ////////////////////////////////////

    //Preloader
    loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loop, false, 0, true);
    loader.contentLoaderInfo.addEventListener(Event.COMPLETE, done, false, 0, true);
    loader.load(new URLRequest(“swfs/section1.swf”));

    function loop(e:ProgressEvent):void {

    var perc:Number = e.bytesLoaded / e.bytesTotal;
    percent.text = Math.ceil(perc*100).toString();

    }

    function done(e:Event):void {

    removeChild(percent);
    percent = null;
    myMovie = Sprite(loader.content)

    addChild(myMovie);

    loader.contentLoaderInfo.removeEventListener( ProgressEvent.PROGRESS, loop);
    loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, done);
    loader = null;

    }
    ////////////////////////////////////

    // Button Function
    function btnClick(event:MouseEvent):void {

    removeChild(loader);
    var newSWFRequest:URLRequest = new URLRequest(“swfs/” + event.target.name + “.swf”);
    loader.load(newSWFRequest);
    loader.x = Xpos
    loader.y = Ypos
    addChild(loader);

    }
    ////////////////////////////////////

    // Button Listeners
    section1.addEventListener(MouseEvent.CLICK, btnClick);
    section2.addEventListener(MouseEvent.CLICK, btnClick);
    section3.addEventListener(MouseEvent.CLICK, btnClick);
    section4.addEventListener(MouseEvent.CLICK, btnClick);

    ////////////////////////////////////

    any help would be appreciated.

    Ta

  30. #30 by husky - April 30th, 2010 at 13:11

    I dont like AS3.. I personally think is powerful and whatever.. but its too complicated and.. complicatd things.. never last long.. cause newbies skip it cause its hard and they look for something easier.. so I say programming flash as3 will die… actually is dying.

  31. #31 by zedia.net - April 30th, 2010 at 13:58

    Haha, that is pretty funny, AS3 as been around for quite some time now, at least 3-4 years, and Flash hasn’t died yet. I hope you know that HTML5 will be just as hard; Javascript and ActionScript are quite similar. Anyway everyone is entitled to their beliefs.

  32. #32 by Dan - June 1st, 2010 at 12:02

    @doug
    Could you write here clean way how to hand over flashvars to new loaded swf…
    Thank you in advance

  33. #33 by john - June 3rd, 2010 at 13:34

    great!!!
    i use this tutorial and it works nice
    except when i put some code on the first
    frame of the content.fla which gives me
    this error.

    TypeError: Error #1009: Cannot access a property or method of a null object reference.
    at content_fla::MainTimeline/frame1()

    why is this happening? any solution?
    thank you!

  34. #34 by Bernie - June 29th, 2010 at 17:10

    For a solution to the #1009 Error see:

    http://www.sitepoint.com/forums//showthread.php?t=642623

  35. #35 by bishop412 - September 16th, 2010 at 13:38

    ive been having a problem trying to get preloaders to work. ive even downloaded Lee Brimelow’s .fla file and it worked locally but on the server its just stuck on zero and wont load anything anymore. ive read pages and pages of forums and i cant seem to find the solution. jeez…i didnt realize preloaders can be a such a pain in the wrong parts of the body

  36. #36 by S - November 25th, 2010 at 19:01

    @Garfty
    If you want to fade in and out parts of your movie as and when you preload etc. Use the Caurina Tweener class, you can pretty much capture anything at any given time and control when you execute your functions because it has an onComplete function where you can put your functions in and then execute your functons after fading in and out your alpha on your backgrounds or whatever, it gives you a lot of control

  37. #37 by mT - December 7th, 2010 at 13:30

    this is becoming extremely frustrating, followed Lees tut…and referenced Tuts elsewhere as well, and i simply cannot get this file to load….

    whenever i publish, all i get is a white screen, the profiler shows that the .swf is loading into the preloader, but nothing is showing (not even the pre-loader)

    once the loaded swf is done loading, nothing shows up, even after being added to the display list…

    im at my wits end and about to put my foot through both my monitors

  38. #38 by bennettd123 - December 23rd, 2010 at 03:09

    @Beebs How did you solve your problem?? Can you please share? I’ve been stuck on this for so long. I also followed #34, Bernie’s link, but it didn’t work for me. Even though it seemed my situation was exactly the same…

  39. #39 by Jun - March 16th, 2011 at 10:28

    Loading the external swf file using the code above I got an error. The external swf file doesn’t load at the first frame, It jumps into several frames (115 frames). My external swf file has several scenes: intro, scene 1 and so fort.

  40. #40 by saurabh agrawal - June 24th, 2011 at 09:12

    I want to load multiple external swf in as3 but it should load one after another and should be added one after another but loaded at once.

  41. #41 by tony - July 18th, 2011 at 14:28

    what a nightmare, i use the files exactly as there were presented, but of course the movie loaded start playing early (60% thru), and when you stop that movie then tell it to play in the preloader the code stops working, perfectly frustrating, just enough to pull your hair out:
    does var myMovie:Sprite; need to become
    var myMovie:MovieClip; ????? i tried it, and mf’s started playing at 20 percent

  42. #42 by shayne - September 20th, 2011 at 03:58

    Great tutorial even though iam complelty dumb when it comes to as3..ive managed to export the swc file from flash..then ive made a flash catalyst project then exported that…then imported my flash catalyst project into flash builder..then inported the swc and also created the new preloader class…everything works well even the preloader loads my catalyst swf..the only problem i have is the preloader repeats itself 3 times before loading the swf..ive tried everything to fix it but cant work out why….you can see the loader doing it here http://avatars.imvu.com/VisNova

  43. #43 by Goofyfoot2001 - October 11th, 2011 at 22:26

    Not a single one of the external preloaders found online work. PERIOD. This one doesn’t prevent the external content from starting as soon as it starts loading. If you stop the external content then you will be hiding the preloader graphics. I’m going to get this right and publish something that people can actually use. I’m new to as3 but not programming and this is ridiculous that this page comes up first on a search for external preloaders.

  44. #44 by Goofyfoot2001 - October 11th, 2011 at 22:28

    I’m not dissing your solution just that it’s the whole AS3 preloader subject is sadly lacking even after years of people hacking away at it. Maybe it’s just Google ranking the wrong pages or something and the solution is out there but too far back in the search results.

  45. #45 by Muzikayise - January 11th, 2012 at 15:04

    thank you, i was looking for a nice elegant solution to pre-load swfs and this is perfect.

    also thanks to Lee Brimelow

  46. #46 by Z Rodis - February 26th, 2012 at 21:03

    I found a solution to the #1009 Error.

    For me it was caused by code in my document class’s constructor.

    Take out all the code in your constructor and put it in a new function. Add an event listener into the constructor that listens for itsself to be added to the stage. You can use that to run all the code that makes your game/site work.

    For example:

    public function DocumentClass {
    this.addEventListener(Event.ADDED_TO_STAGE, runGame);}

    public function runGame(e:Event) {
    addChild(exampleThing)}

  47. #47 by Mikey - July 2nd, 2012 at 04:26

    Can someone please explain to me this:

    I have a swf file and it is working perfectly locally and on the server. This application has a few buttons and some combo drop down boxes. The drop down boxes are filled with data, of course :) .

    However, when I use this loader, and when all is loaded and everything seems to work, I realize that the combo drop down boxes respond to the mouse in, and mouse out events. But I can not click these drop down boxes so that it expands (drops down) and show the rows of text to select.

    This is so strange. All other functions are working perfect using the loader, but these combo boxes will NOT work?!?!

    Here is how I add data to them:

    sel_track_01.addItem( { label: “Select one answer…”, data: “0″} );

    On the other hand, since these drop down boxes are working without the loader, on and off-line, I know it is a problem within the loader.

    Can someone try to answer this, please? :)

    Thanks!

    /Mikey

  48. #48 by Mikey - July 2nd, 2012 at 08:29

    @Mikey

    Never mind. I fixed it.

    I just added the combo box to the library of the loader, if anyone is wondering :)

(will not be published)
Subscribe to comments feed