I thought I’d follow-up my original post about Doppelgänger, and take some time to detail its implementation from a rendering perspective. There’s a lot that can be done to ensure your AIR for iOS apps hit their target frame rate, but these optimizations aren’t always obvious. I’ll also mention a few common pitfalls that leave many Flash developers scratching their head.

I hope you find the following useful for your own projects.

Test on the lowest common denominator

Test frequently on a physical device and use the lowest common denominator. For example, if you’re targeting both iPad’s then get your hands on an iPad 1. Don’t exclusively test on the iPad 2 and assume everything will be fine and dandy for those using previous generation hardware. You’ll be asking for trouble if you do.

Profile your app using Instruments

I know Flash Professional and ADT allow you to develop iOS apps using a PC, but if you’re at all serious then you really should get yourself a Mac. Why? Well you’ll be able to install the Instruments application, which allows you to analyse the performance of your app as it runs on a device.

Profiling tools are essential. Profile and optimize as you go.

Instruments was invaluable during the development of Doppelgänger. It allowed me to examine the app’s memory consumption, CPU usage and frame rate, making it very easy to spot areas in my code that were causing CPU spikes very early in the development process. My personal goal was to keep CPU usage at around 20%. It was easy to spot any spikes above this and quickly address these by optimizing either my code or graphics.

Don’t ignore spikes in CPU usage. Endeavour to fix all bottlenecks as early as possible.

Focus on rendering bottlenecks from the outset

Graphics rendering is often the single biggest bottleneck when porting Flash apps to iOS. Be prepared to flatten your display lists, simplify complex vector shapes, reduce the number of layers, and use bitmaps where appropriate. Also, perform incremental tests of your graphics assets and use Instruments to measure performance.

The original FLA for Doppelgänger was quite bloated. There was an insane amount of vector detail in many of the assets and an unnecessarily deep display list. I converted much of what was there to bitmaps to maximize render performance. I also tested many of the graphics assets in isolation by simply publishing individual test apps and profiling with Instruments. Once happy with the performance I’d layer more animations together and test again. This allowed me to quickly find any graphical assets that were going to hurt performance.

GPU rendering

AIR for iOS provides two choices of rendering engine – CPU mode and GPU mode. The CPU renderer is the same software renderer used by the desktop Flash Player. However, on iOS devices we can instruct Flash to offload much of this work to the GPU, which when used correctly, can accelerate rendering. Doppelgänger took advantage of GPU rendering to help maximise its frame rate.

Bitmap caching is your friend

When a display object changes in any way, or intersects the bounding area of some other display object that has changed, then it will need to be redrawn. Unfortunately redrawing complex vector shapes, text, or clips containing deeply nested display lists can be expensive.

Thankfully help is at hand in the form of bitmap caching. Put simply, bitmap caching stores a bitmap representation of a display object and uses the bitmap version whenever the object needs to be re-drawn. In many situations, drawing the bitmap representation to the screen is typically much faster than redrawing the original. Bitmap caching is particularly useful for objects that have translation (movement along the x- or y-axis) or transformation (scaling or rotation) applied to them.

Doppelgänger makes heavy use of bitmap caching to minimise the render time of each frame.

Bitmap caching is your enemy

Hold on! Didn’t I just say that Bitmap Caching was good? Yes I did. But only if it’s used correctly.

Many developers seem to think that activating GPU rendering and bitmap caching everything, will somehow guarantee a blindingly fast frame rate. This couldn’t be further from the truth. Misuse of bitmap caching will cripple your application. Even if you fully understand the ins-and-outs of bitmap caching, it’s all too easy to make a simple mistake that can undo much of your previous optimizations.

To benefit from bitmap caching you need to ensure that the cached bitmap does not frequently become invalidated. If invalid, the bitmap will need to be recreated by rendering its source display object and copying a bitmap representation to an off-screen buffer. To prevent this, don’t cache movie clips that contain timeline animations or container clips that have children that move relative to it.

I must admit that I slipped up here. Some of my initial tests on Doppelgänger performed poorly on iPad 1. I eventually tracked it down to the shutters that open and close between levels. I’d accidentally cached the container, which held both halves of the shutter. Essentially, as they opened and closed, every frame of animation was invalidating the cached version and forcing a re-cache. With the shutters consuming almost twice the height of the screen when fully open, re-caching was killing performance. This was fixed by simply caching each of the shutters’ child clips rather than the container.

To see the shutters in action, take a look at the gameplay video of Doppelgänger.

Bitmaps Vs. Vectors

Use bitmaps or vectors based on their strengths. It’s easy to get into the mindset of using bitmaps for everything because they tend to render faster. However bitmaps can consume significantly more memory and can quickly exhaust GPU memory when GPU Rendering is being used.

Content with a large bounding box and/or many animation frames might be better as vectors.

Doppelgänger uses a combination of both vector and bitmap assets. The timer’s ticking clock, for example, is a vector timeline animation. With a bounding area of 173×161 pixels and running for 90 frames, a series of bitmaps would simply have been too expensive (almost 10Mb of uncompressed data). The same was also true of the tick and cross animations, which both cover a fairly large area of the screen come the end of their animation cycles. And of course the WeeMee’s themselves are constructed from vector shapes, helping to reduce the app’s footprint and ensuring the WeeMee’s maintain their image fidelity when scaled.

Swap render quality where appropriate

Consider using a lower render quality setting. Bitmaps are relatively unaffected by this and it really can be hard to spot any differences for vector content that moves.

Doppelgänger constantly switches between low and high rendering quality during the lifetime of each game. This gives a massive performance boost, especially on the iPad 1. The drop in render quality is noticeable on the timer’s clock but I felt it was worth doing in order to maximize the frame rate. Each of the WeeMees are rendered into pixel buffers using the highest render quality setting just before each level begins. The bitmap representations of each WeeMee is then used during the level, which by this point has switched to the lowest render setting.


It can very much feel like smoke and mirrors some times, but it’s the final result that counts. These challenges aren’t specific to Flash and ActionScript either. Targeting mobile requires a little more effort than developing for the desktop. However, with some careful planning and an understanding of the constraints you’re working within, you should be able to make the necessary compromises to ensure a fun and engaging experience.

  1. Thanks a lot for sharing your experiences. I’ll hopefully start one of my own projects for AIR on iOS soon and I’m following you as well as some other Flash guys out there to be best prepared. Keep up these high quality output! 😉

  2. Hi, thank you for this article.
    You say in “Profile your app using Instruments” that only mac provide a tool to analyse the performance of the app on the device, or you seem to say such a thing, but it is not correct. You have really good tools even on “pc”. You can watch on youtube for exemple one developped in AIR by thibault imbert, it’s work just fine on every device.

  3. Thanks for the info Alex! If you have any suggestions for PC-based tools to help profile on-device then please feel free to share.

    Christopher (Author)
  4. Hi Christopher,
    if viewing a french video doesn’t matter to you, you can go to http://www.bytearray.org/?p=3151, and advance the video to “thibault imbert – le futur du profiling”.
    For exemple, i think demonsterdebugger is capable to profile your application on the fly. There are also others tools that you can install on the device, but i’m sorry i never took the time to use them.
    (also i did not receive a notification for your response, i have to check manually if i want to know if there is an reply. Is there a way to be notified automatically as we give our mail ?)

  5. Do you have any suggestions as to how I can use to learn instruments with flash cs5.5. I have been looking on the web, but seem to only be able to find out to use instruments in connection with x-code. Wasn’t sure if you have a tutorial on this subject. I bought a copy of your book as well, but I didn’t see anything in there about it.


  6. Hi Josh, that’s a great suggestion and I’ll try and get a tutorial put together. In the meantime I’ll try and send some quick steps over to you over the weekend, and also probably post them here for anyone else who is interested.

    Christopher (Author)
  7. Hey Christopher, I would love to know more about using instruments to profile Air for iOS. Thanks!

  8. Hi Mike. I’ve emailed you some quick steps that should hopefully get you started with Instruments. Let me know how you get on. Thanks.

    Christopher (Author)
  9. Hi Chris,

    Great article, very helpful.

    I’m also trying to figure out how you’ve used Instruments. My device isn’t showing up under Targets, and there’s no way to import the IPA file. Any tips?


  10. Hi Lou and sorry about the late reply.

    I started writing an article about using Instruments quite some time ago and then never managed to find the time to finish it. I did however create a draft of that tutorial, which I’ll send along to you.

    As for your device not showing up in Instruments. I can’t remember ever having that problem but a friend says you can using XCode’s Organizer window to get things working. Here’s his quick steps:

    1.First, make sure you have the most recent version of XCode installed on your Mac.
    2. Then once it’s been launched, open the Organized window (Window | Organizer) and register your device as a development device from there. Instruments should give you some options to do this once you’ve connected your device to your Mac.

    Hope that gets it running for you, and hopefully you’ll find the draft tutorial useful.


    Christopher (Author)
  11. Chris, thank you so much, I really appreciate it. That tutorial was very helpful, and I was able to get it working.

    I understand now why using Instruments is so important: I’m seeing aspects of my app I never saw before. Time to start optimizing…

    Two values stuck out to me: It’s using about 50-60% CPU and about 200-300 virtual memory. Compared to other apps, this seem rather high. (FYI, I can keep CPU usage below 30% for the most part, but I’m using embedded video clips and playing those shoots it up to 60%).

    In your experience, what would be the ideal? And do you think this could be a reason for apple to reject an app?

  12. Hi Lou. Glad you found the draft tutorial useful.

    Yes, Apple can reject your app for using too much CPU, although they aren’t exactly explicit about actual measurements. Basically, they may reject an app that rapidly drains the device’s battery and/or generates excessive heat. So yeah, try to write apps that are as CPU friendly as possible.

    You can find out more information here: https://developer.apple.com/appstore/resources/approval/guidelines.html#damage-device

    Looking ahead, Adobe are working on a new tool named Monocle that will give even more detail for Flash devs wishing to profile their content. If you aren’t aware of Monocle then take a look at the video on this post: http://www.yeahbutisitflash.com/?p=3950

    Christopher (Author)
  13. Hi Chris

    Your tutorial is really interesting. I’m currently looking at running instruments for my app, but I can’t seem to get it working – I get the following message when I try to run the target: “Target failed to run : Remote exception encountered : ‘Failed to get task for pid xxxx”. Any thoughts?


  14. Hi Gareth, and sorry for the late reply.

    I’ve not experienced this error myself but you could try checking that your app is compiled using your Development certificate instead of your Distribution certificate, as you can’t test apps that are ready for distribution.

    It’s also worth checking that you are using the most recent version of XCode on your Mac. I do remember experiencing some problems with Instruments after letting my version of XCode fall behind.

    Hope that’s of some help.

    Christopher (Author)
  15. Thnx for sharing this information. Iam currently working on a ipad app for my company, and its hard to get some animations without lack.

  16. I have a problem developing iOS prototypes on Flash CC 2014. No matter which SDK I use whether it is 13 (iOS 4.3 and up) or version 16 (iOS 5.1.1 and up) I got 2.5% CPU as long as the monitor an the app is turned on with the empty app doing nothing beside showing a empty canvas.
    The app contains no as3 code. So it is completely empty.
    When I change the FPS from 24 to 60 the CPU usage will go up to about 4%.
    I compiled the IPA in the “App Store” mode. So it should be at least the fastest from this point I guess.

    any Ideas would be great to have an AIR app running on iOS with zero CPU.