AS3 Bandwidth Test

Update (05/18/2011): You can fork the source code from github here

A few months back I was talking to a friend who is new to Flash and who was wondering what was the best way to determine a client’s connection speed.  I told him that he could use a bandwidth test to determine the client’s speed and based on the results he could decide what files to serve, we searched(lightly) google and noticed that there was no available AS3 code to determine a user’s bandwidth speed.  Sure we saw that if we used FMS we could use the native bandwidth detection to do our dirty work, but unfortunately not everyone has the resources to host some content on a FMS.

Therefore I decided to write him a little class to perform a basic bandwidth test.  It is not complicated at all, which is probably why nobody bothered posting the code online, to write code to test for speed but I have to admit that is not extremely accurate.  The accuracy of the test depends on the size of the file being downloaded for the test, the larger the file the more accurate the test is but the longer it takes to complete.  This is where one has to decide between accuracy and waiting time,  I find that a file of about 32KB gives a reasonably accurate idea of the speed a client has.  Why? well most, if not all, broadband users will be able to download this in less than a second and the results will most likely be above 100KBps, which should be fast enough for most instances that you want to test for.  Worst case scenario you could write some sort of loop that will keep testing the speed against increasingly larger files until you are satisfied with the results.

So how does it work?

Create an instance of the BandwidthTest class, whose constructor takes one parameter, the URL for the image or swf to test against.  Once you have your instance, you need to attach an EventListener to check when the test is complete and the last step is to call the start() function to start the test.

import com.yeungus.utils.BandwidthTest;

StartButton.addEventListener( MouseEvent.CLICK, startTest );
var test:BandwidthTest = new BandwidthTest( "http://yeungus.com/images/sample_image.jpg" );

function startTest( e:MouseEvent ):void
{
	test.addEventListener( Event.COMPLETE, downloadComplete );
	test.start( );
}

function downloadComplete( e:Event ):void
{
	_results.text = "results: " + Math.round( test.speed ) + "KBps";
}

The Actual Class

Basically we measure the time it takes to download something and we divide the file size by said time.  The formula looks like:

KBps = filesize(in KB) / ( endtime - starttime )(in s)

The rest of the class just takes care of basic things like converting time into seconds and bytes into kilobytes.  The other thing that I would like to point out is that you can use the same image to do your tests.  The class attaches a random number at the end of the target URL in order to avoid caching the image.

package com.yeungus.utils
{
	import flash.display.Loader;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.net.URLRequest;

	public class BandwidthTest extends EventDispatcher
	{
		public var URL:String;
		public var debug:Boolean = false;
		private var _speed:Number;
		private var _startTime:Number;
		private var _loader:Loader;

		public function BandwidthTest( testFileLocation:String ):void
		{
			URL = testFileLocation;
		}

		public function get speed( ):Number{ return _speed; }

		public function start( ):void
		{
			URL += "?" + ( Math.random( ) * 100000 );
			_startTime = ( new Date( ) ).getTime( );

			_loader = new Loader( );
			_loader.contentLoaderInfo.addEventListener( Event.COMPLETE, downloadComplete );
			_loader.contentLoaderInfo.addEventListener( IOErrorEvent.IO_ERROR, ioErrorHandler );
			_loader.load( new URLRequest( URL ) );
		}

		private function downloadComplete( e:Event ):void
		{
			var endTime:Number = ( new Date( ) ).getTime( );
			var totalTime:Number = ( endTime - _startTime ) / 1000;
			var totalKB:Number = e.currentTarget.bytesTotal / 1024;
			_speed = totalKB / totalTime;

			if( debug )
				trace( "total time: " + totalTime + " total KB: " + totalKB + " speed: " + speed + "KBps" );

			dispatchEvent( e );
		}

		private function ioErrorHandler( e:IOErrorEvent ):void
		{
			_speed = -1;
			trace( "URL not found" );
		}
	}
}

Demo