HTML5 is all the rage and a lot of Flash developers are either curious about it or have actually made the switch. But how does its performance stack up against Flash? That is a very complicated question, so we’ll begin today with just a simple test of the speed at which a lot of bitmaps can be drawn to the screen. Who will win? Read on to find out.

Today’s test comes in two parts: an JavaScript/HTML5 app using the canvas tag and an AS3 app using BitmapData. Both of them draw 10,000 16×16 images to a 640×480 screen at random positions.

Launch the HTML5 version
Launch the Flash version

Here is the source code for the Flash version:

package
{
	import flash.geom.Point;
	import flash.events.MouseEvent;
	import flash.text.TextFormat;
	import flash.geom.Rectangle;
	import flash.utils.getTimer;
	import flash.text.TextFieldAutoSize;
	import flash.display.StageScaleMode;
	import flash.display.StageAlign;
	import flash.text.TextField;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
 
	public class BitmapTest extends Sprite
	{
		[Embed(source="opaque.jpg")]
		private static const OPAQUE:Class;
 
		[Embed(source="alpha.png")]
		private static const ALPHA:Class;
 
		private var opaqueBMD:BitmapData = (new OPAQUE() as Bitmap).bitmapData;
		private var alphaBMD:BitmapData = (new ALPHA() as Bitmap).bitmapData;
		private var screenBMD:BitmapData;
		private var screenRect:Rectangle;
		private var sourceRect:Rectangle;
		private var destPoint:Point;
		private var time:TextField;
 
		public function BitmapTest()
		{
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
 
			screenRect = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
			screenBMD = new BitmapData(640, 480);
			addChild(new Bitmap(screenBMD));
 
			sourceRect = new Rectangle(0, 0, opaqueBMD.width, opaqueBMD.height);
			destPoint = new Point(0, 0);
 
			makeButtons("Opaque", "Alpha");
 
			time = new TextField();
			time.text = "Time: 9999";
			time.autoSize = TextFieldAutoSize.LEFT;
			time.x = stage.stageWidth - time.width;
			time.y = stage.stageHeight - time.height;
			addChild(time);
		}
 
		private function makeButtons(...labels): Number
		{
			const PAD:Number = 5;
			var curX:Number = PAD;
			var curY:Number = stage.stageHeight - PAD;
			for each (var label:String in labels)
			{
				if (label == null)
				{
					curX = PAD;
					curY -= button.height + PAD;
					continue;
				}
 
				var tf:TextField = new TextField();
				tf.mouseEnabled = false;
				tf.selectable = false;
				tf.defaultTextFormat = new TextFormat("_sans");
				tf.autoSize = TextFieldAutoSize.LEFT;
				tf.text = label;
				tf.name = "lbl";
 
				var button:Sprite = new Sprite();
				button.buttonMode = true;
				button.graphics.beginFill(0xF5F5F5);
				button.graphics.drawRect(0, 0, tf.width+PAD, tf.height+PAD);
				button.graphics.endFill();
				button.graphics.lineStyle(1);
				button.graphics.drawRect(0, 0, tf.width+PAD, tf.height+PAD);
				button.addChild(tf);
				button.addEventListener(MouseEvent.CLICK, onButton);
				if (curX + button.width > stage.stageWidth - PAD)
				{
					curX = PAD;
					curY -= button.height + PAD;
				}
				button.x = curX;
				button.y = curY - button.height;
				addChild(button);
 
				curX += button.width + PAD;
			}
 
			return curY - button.height;
		}
 
		private function onButton(ev:MouseEvent): void
		{
			var mode:String = ev.target.getChildByName("lbl").text;
			switch (mode)
			{
				case "Opaque":
					test(opaqueBMD);
					break;
				case "Alpha":
					test(alphaBMD);
					break;
			}
		}
 
		private function test(bmd:BitmapData): void
		{
			var REPS:int = 10000;
			var maxX:int = screenRect.width - bmd.width;
			var maxY:int = screenRect.height - bmd.height;
			var beforeTime:int = getTimer();
			screenBMD.fillRect(screenRect, 0xffffffff);
			screenBMD.lock();
			for (var i:int = 0; i < REPS; ++i)
			{
				destPoint.x = Math.random() * maxX;
				destPoint.y = Math.random() * maxY;
				screenBMD.copyPixels(bmd, sourceRect, destPoint);
			}
			screenBMD.unlock();
			var afterTime:int = getTimer();
			time.text = "Time: " + (afterTime-beforeTime); 
		}
	}
}

And here are the images it embeds:

Opaque image
Alpha image

You can get the source code of the HTML5 version by simply opening up the HTML file. Its embedded images are Base64-encoded into the img tags.

Here are the devices tested:

Name CPU GPU OS
MacBook Pro Retina 2.3 GHz Intel Core i7 Intel HD Graphics 4000 OS X 10.8.3
Windows Desktop 2.66 GHz Intel Xeon X5550 Nvidia GeForce GTX 660 Windows 7 SP 1
iPad 2 1 GHz ARM Cortex-A9 PowerVR SGX543MP2 iOS 6.1.3
LG Optimus G 1.5 GHz Qualcomm Krait Qualcomm Adreno 320 Android 4.1
HTC Evo V 4G 1.2 GHz Qualcomm Snapdragon Qualcomm Adreno 220 Android 4.0

And here are the results:

Device Opaque Alpha
HTC Evo V 4G – Android 4 Browser 278 330
HTC Evo V 4G – Google Chrome 27 1093 1102
Apple iPad 2 – Safari 225 225
LG Optimus G – Android Browser 465 503
LG Optimus G – Google Chrome 27 358 349
Motorola Xoom – Android 4 Browser 231 232
Motorola Xoom – Google Chrome 27 289 276
MacBook Pro Retina – Google Chrome 27 35 36
MacBook Pro Retina – Firefox 21 70 73
MacBook Pro Retina – Safari 6 17 16
MacBook Pro Retina – Flash Player 11.7 33 35
Windows Desktop – Google Chrome 27 51 52
Windows Desktop – Firefox 21 141 139
Windows Desktop – Internet Explorer 10 188 180
Windows Desktop – Flash Player 11.7 11 12

Performance Graph (all)

Performance Graph (Windows, Mac)

Performance Graph (mobile)

From this data we can draw some interesting conclusions:

  • Regardless of device, choice of browser will make a huge performance difference with HTML5. On Mac OS X, Safari is about 4x faster than Firefox. On Windows 7, Google Chrome is 4x faster than Internet Explorer. On the HTC Evo V 4G, the Android Browser is 3x faster than Google Chrome.
  • On Windows and Mac, Google Chrome seems to be about twice as fast as Firefox.
  • Safari is about twice as fast as Flash on Mac OS X. Google Chrome is a little slower and Firefox is far slower.
  • Flash beats everything on Windows by a long shot. It’s 5x faster than Google Chrome, 13x faster than Firefox, and 17x faster than Internet Explorer
  • Mobile processors still have a long way to go to match their desktop/laptop counterparts. A modern phone (HTC Evo V 4G) was beaten by a modern desktop computer (Windows Desktop) by a factor of 100x. How much worse would the performance be on a typical 2-3 year old phone? If you go with HTML5 you’ll need to plan for performance to vary by at least two orders of magnitude.

There’s definitely no clear “HTML5 is faster” or “Flash is faster” conclusion here. As ever with HTML and JavaScript, your results will vary heavily by browser. If you go with HTML5 it’s imperative that you test on many browsers! However, Flash Player outperforms HTML5 on all browsers except Safari on Mac OS X in this test. So it’s safe to say that Flash Player is usually faster and certainly faster for the vast majority of users given that Safari only has about 5% of the desktop marketshare.

As a disclaimer with mobile, there are many devices out there with wildly different performance characteristics. Even iOS has five base models of iPad, six of the iPhone, and five of the iPod Touch. There are hundreds to thousands of Android devices. It’s therefore really hard to get a complete picture of mobile performance since very few people have access to so many devices. I certainly don’t. However, I’m guessing many people reading this article have a couple of minutes to point their mobile browsers at the above tests. Want to contribute your own test results? Post a comment with your device name, opaque and alpha times, and if you have them or can look them up, CPU and GPU specs.