I ran into a problem today that had me scratching my head for a bit. Suppose you have a large flash project that loads a number of external SWFs. Suppose furthermore that several of those SWFs, as part of their default behavior, instantiate display objects from within their own libraries. Let’s get more specific.
Take this example. Map.swf has a symbol in its library (a Bitmap) exported as “MarkIcon.” If in the constructor I add the following code…
public function Map() {
addChild(new Bitmap(new MarkIcon(50, 50)));
}
…my SWF has no problem correctly displaying its image. Pardon the awful graphics. This is for demonstration purposes only.
What happens when I later try to load this simple SWF into another SWF?
We get a compilation error of course. This sounds like a big problem, doesn’t it? What could we possibly do to fix this? We instantiate via the child SWFs applicationDomain property. Huh?
public function Map() {
var miDef:Class = this.loaderInfo.applicationDomain.getDefinition("MarkIcon") as Class;
addChild(new Bitmap(new miDef(50, 50) ));
}
Now it works everywhere. This same technique can also be used to instantiate the class from within the parent SWF. How’s that for fancy?
Today, as happens once in a blue moon, I was presented with a stack of flash banner ads along with a request that they be squeezed under a 50k file size limit. If you’ve ever worked on banner ads you’re probably familiar with these sorts of limitations, as well as those placed on duration and the use of rich media. You may also be aware of how frustrating and difficult a task it can be to squeeze banners, with all the aesthetic demands placed on them, under these arbitrary size thresholds.
After several years of dealing with these issues, I’ve come up with an array of tips and guidelines for reaching the file sizes that ad networks demand. Some techniques are more effective than others. Many of them may compromise the aesthetic integrity of the files you’re working with so it’s best to use a mixture of these strategies to suit your needs.
I’ve prepared a sample banner in a standard size (300×250) to use as an example. You can download the initial source files by clicking here.
We have what seems to be a very simple banner animation. Several pieces of text fade or slide into view, and bubbles are dynamically placed on the stage and flown from right to left. A number of different fonts are used (all variations of HelveticaNeue) and the image in the background is crisp and clear. It may not seem like it’s very complicated, but this SWF is actually 1.6mb. How can that be the case, and how can we fix it? Let’s look at a couple things we can change.
1. Embedded Fonts
The most significant factor contributing to the file size of this SWF right now is the large number of embedded characters. If we browse through the assets being used in the SWF, we can come up with a list of the following fonts being used:
Helvetica Neue LT Std 95 Black
Helvetica Neue LT Std 67 Medium Condensed Oblique
Helvetica Neue LT Std 77 Bold Condensed
Helvetica Neue LT Std 87 Heavy Condensed
Helvetica Neue LT Std 97 Black Condensed
Helvetica Neue LT Std 107 Extra Black Condensed
Helvetica Neue LT Std 73 Bold Extended
This may seem like a large number of fonts, but it’s not uncommon to receive compositions that use at least this many. This in itself, though, is not necessarily a problem. Where things get messy is when we look at how the fonts are embedded.
In the case of our original file, we’re embedding every character in every font we’re using. If you look at the screenshot above, that’s almost 100,00 characters per font, or nearly 700,000 characters total. Most of the characters we’re embedding are entirely unnecessary. How can we resolve this issue? There are three ways.
Embed a smaller subset of the font. I generally use the following.
Embed only the characters being used within each text field. This is a fine option for fields that won’t be changing. You can accomplish this by first clicking “Don’t Embed” to clear any groups you’ve already included, and then clicking “Auto Fill.”
Make your text fields static. Generally this works well, although sometimes static text renders in unpredictable ways. This is also roughly equivalent to option 2.
After going through the source file and correcting every dynamic text field (using option 2), our 1.6mb SWF is down to a sweet and slim 36kb.
2. Image Compression
Another way we can influence file size is by toying with the JPEG compression settings for our SWF. By default, Flash embeds bitmaps at 80% of their original quality. Most of the developers I know rarely if ever tweak this setting, and in most cases it’s not necessary to, but if you’re willing to toy around the the figures you can squeeze a few more kb out of your SWF quite easily.
You can modify image compression in two ways.
Modify the global compression settings for your SWF. You can find this control in the publish settings panel under the “Flash” tab.
Modify the compression settings for individual images. When it comes to fine tuning your application, this is a better way to go. You can access this menu by selecting a bitmap in the library panel, right clicking, and selecting “Properties.”
This method is far from flawless, and the results are often hard to predict. In the case of the SWF we’re working with above, I’ve created a demo showing what the platypus photo in the background looks like at various compression levels. Click and drag the slider to adjust the quality of the image (displayed in the field next to the slider).
You probably can’t even see a difference before getting down below 70. From there to 40 the changes are subtle, and they become more noticeable with each step from 30 down. Obviously you have to know where to draw the line when you’re sacrificing image quality for filesize, though in the example above I would be willing to go perhaps as low as 20 if the need were great enough.
In our original SWF, setting the JPEG quality to 20 brought our filesize down from 36kb (after modifying our fonts) to a tiny 24kb.
3. External Libraries
Depending on what you’re trying to accomplish in your banner, you may require external libraries for animation or data connectivity. It’s easy to forget that including these libraries can add to the overall file size of your published SWF.
In our sample SWF we’re using the TweenLite library to animate bubbles across the screen and to remove them when they’ve cleared the left edge. TweenLite is a wonderful library, and it’s already quite small, but you may not be aware that there is a smaller version of TweenLite available called TweenNano. TweenNano is packaged with TweenLite, and unless you intend to do something highly complex it has all the same basic functionality.
The code in our original file for animating bubbles looks like this.
Switching the code in our application from TweenLite to TweenNano brought our file size down from 24kb to a miniscule 20kb.
4. Flash Player Version
The final fix I would recommend for our sample file requires an awareness of the environment you’re publishing to and the needs and capabilities of your project. While it may seem at first that it’s best to publish to the latest version of FlashPlayer, if your application isn’t taking advantage of newer features it may be in your best interests to hang back with an earlier version.
Our original application, after all the cuts we’ve already made, was clocking in at 20kb published for Flash Player 8. If we publish to Flash Player 7, we reduce the filesize even further down to an insane 16kb!
What do we lose for the bargain? In this case, the ability to specify anti-aliasing methods for our text fields. Because this animation is simple, and nothing dramatic happens with text, we can part with those capabilities quite comfortably.
Of course, there are trade offs here as well. You may think that it’s better to publish to Flash Player 9 than 10 if your project doesn’t require features like native 3D support, but perhaps you could achieve some performance benefit by replacing your Arrays with Vectors?
Which version of Flash Player you choose to publish to will depend on your unique needs, but it’s worthwhile to remember that the latest is not necessarily the greatest.
Conclusion
After applying the above techniques to our original swf with a filesize of 1.6mb, we were able to reduce it to only 16kb. That’s a 99% reduction, and it’s sure to make clients and advertising networks happy. The new, shrunken files, can be found by following this link.
Keep these tips in mind and let me know if you have any other tricks for putting the squeeze on bloated SWFs.
Big Spaceship labs just posted a great article on a unique problem they encountered while attempting to load content from external domains. They have a rundown of steps they took to solve the problem and the eventual solution they came to.
So, I thought I’d bring attention to a strange inconsistency I run into on a very regular basis while developing applications in Adobe Flash.
Much of the work that I do in flash has to do with displaying and managing video. To date, I have probably created at least 50 different playlist enabled video players, by which I mean video players with a list of either links, thumbnails, or both that allow you to play other videos in the same container without leaving the page. For example, something like this…
A Generic Video Player
…with a playlist that allows you to select other videos, like this…
Video Player with Playlist
This is all well and good. Because I try to build in a way that’s generalized for client accessibility and ease-of-use (and because I don’t want to be solely responsible for updating these things myself), the standard way to go about creating something like this would be to have an external XML document that contains all of the information required to build the playlist. The application is responsible for loading that document and extracting that information from it, which it uses to build a clickable, interactive playlist. This is all quite simple. The XML could look something like this:
<playlist>
<item>
<uid>1</uid>
<title>Video 1</title>
<video>PATH TO VIDEO</video>
<thumbnail>Image for video</thumbnail>
</item>
<item>
<uid>2</uid>
<title>Video 2</title>
<video>PATH TO VIDEO</video>
<thumbnail>Image for video</thumbnail>
</item>
<!-- and on, and on, and on -->
</playlist>
The folder structure for such an application could (and would) conceivably look like this:
FLA Folder
Within our document root, we have an FLA folder to hold our source files.
FLV Folder
We have an FLV folder for holding video assets.
SWF Folder
We have a SWF folder for holding all of our compiled application SWFs.
XML Folder
And finally we have an XML folder for holding any XML documents we’ll be loading in the application. The index file resides in the document root.
Here’s where things get screwy.
In my application, when I’m ready to load my external XML in order to build a playlist, my code will look like this:
var req:URLRequest = new URLRequest("xml/playlist.xml");
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, _playlistHandle, false, 0, true);
loader.load(req);
After I’ve loaded my playlist, suppose I want to play the first video. For the sake of brevity, we will suppose that I have an instance of a video player class that will allow me to simply pass the path to the FLV in to begin playing it, and that it’s already been instantiated and configured under the name “_player.”
function _playlistHandle(e:Event):void {
var loader:URLLoader = e.target as URLLoader;
var xml:XML = XML(loader.data);
_player.play(xml.item.video[0].toString());
}
What do you suppose the path to my video is going to be, within my xml doc? If you look above, you’ll see I loaded the xml from the document root, and it would be natural for you to assume that the path to the first video is “flv/video.flv.” In this instance, you would be incorrect.
The correct path to the video file would be “../flv/video.flv.” Why is this? The relative path to an asset, in flash, varies according to what type of asset it is you’re accessing. In the case of XML you load your file from the location in which the SWF file is running, which in our example is the document root. When you’re loading video, however, you have to load your file from the physical location of the swf, not from where it’s running. In our example, that means our video file must be loaded as if the application is running from within our “swf” subfolder.
Why is this the case? I honestly have no idea. I can only imagine that in some obtuse way it’s meant to protect video content from being exploited by external domains or malicious third parties, though I can’t see exactly how.
Hopefully if you find yourself developing such an application, and you can’t figure out why the paths to your videos won’t resolve correctly, you’ll remember reading about the issue here. If anyone has a good explanation for why this is the case, I’d also be very curious to be informed.
I just read a great article explaining the use and implementation of marking menus in AS3. This may be just the kind of interface I’m looking for for my long overdue portfolio relaunch. Check it out!