Accurate Timing in ActionScript

7th November, 2007 – 9:26 pm

@Update: 23rd May 2008. Small refactoring of the code without changing the interface, the .zip archive now includes a working example.

There are many things that require timing when programming, for me the challenge is accurately timing how long a user has been viewing a video for. There are a couple of well documented approaches to creating a virtual stopwatch in ActionScript, most of which rely on using SetInterval() to increment a counter, however such methods can cause problems due to Flash Player’s unreliable timings.

My suggested solution is to instead poll the user’s local system’s clock, using the Date() Object, to get the current timestamp, by then using an interval, polling the system clock again and comparing the difference. From the testing I have conducted this yields accurate and reliable timing in Flash, perfect for tracking the progress of video playback.

Code and downloads after the break, comments welcome.

Download ActionScript Class

Download the TimerHelper Class with example code (.as and .fla) demonstrating how to use TimerHelper in your own code.

ActionScript 2 Source Code

/**
 * TimerHelper
 * John Reeves, john.reeves@mailc.net, http://www.jonnyreeves.co.uk/
 * March 2008
 *
 * Timer Class based on user’s system clock for accurate timing in ActionScript
 * When creating an instance of this class, use the `onTick` prototype to get
 * readings, a simple example would be:
 *
 *      class TimerExample
 *      {
 *              private var _timer:TimerHelper;
 *
 *              // Constructor, create instance of Timer.
 *              public function TimerExample() {
 *                      this._timer = new TimerHelper();
 *                      this._timer.onTick = mx.utils.Delegate.create(this, onTimerTick);
 *              }
 *
 *              // Timer callback, handled by the Delegate above.
 *              public function onTimerTick():Void {
 *                      trace(’Timer Callback: ‘ + this._timer.getElapsed() + ‘ seconds’);
 *              }
 *
*/

class co.uk.jonnyreeves.helpers.TimerHelper
{
        /**
         * Indicaqtes if the TimerHelper is Running.
        */

        private var _isRunning:Boolean;
       
        /**
         * Interval for Tracking the clock’s ticks
        */

        private var _tickInterval:Number;
       
        /**
         * Time in seconds elapsed whilist the timer has been running
        */

        private var _secondsElpased:Number;
       
        /**
         * Previous Reading from the Date Object.
        */

        private var _previousTime:Number;
       
        /**
         * Is the TimerHelper using the system clock, or falling back to using an
         * Interval.
        */

        private var _useSystemClock:Boolean;
       
       
        /**
         * Constructor.
         *
         *      @param Void
         *      @return Void
        */

        public function TimerHelper()
        {
                this._isRunning = false;
                this._tickInterval = 0;
                this._secondsElpased = 0;
               
                // Check that we can use the local system clock
                if (isNaN(new Date().getTime())) {
                        this._useSystemClock = false;
                        trace (‘Timer::construct() -> Could not initialise Local System Clock’);
                }
                else {
                        this._useSystemClock = true;
                }
        }

       
        /**
         * Starts the timer.
         *
         *      @param Void
         *      @return Void
        */

        public function start():Void
        {
                // Remove an existing interval
                if (this._isRunning) {
                        this.pause();
                }
               
                // Update last time
                if (this._useSystemClock) {
                        this._previousTime = new Date().getTime();
                }
               
                // Create the new interval.
                this._tickInterval = setInterval(this, ‘__advanceOneTick’, 1000);
                this._isRunning = true;
        }
       
        /**
         * Pauses the timer on its current value.
         *
         *      @param Void
         *      @return Void
        */

        public function pause():Void
        {
                clearInterval(this._tickInterval);
        }
       
       
        /**
         * Resets the Timer back to 0.
         *
         *      @param Void
         *      @return Void
        */

        public function reset():Void
        {
                this.pause();
                this._secondsElpased = 0;
                this._isRunning = false;
        }
       
       
        /**
         * Advance the timer one "tick" either by polling the System Clock, or
         * incrementing.
         *
         *      @param Void
         *      @return Void
        */

        private function __advanceOneTick():Void
        {
                if (this._useSystemClock)
                {
                        var date:Date = new Date();
                        this._secondsElpased += (date.getTime()this._previousTime) / 1000
                        this._previousTime = date.getTime();
                }
                else {
                        // Fall back to incremening, this method is less acurate.
                        this._secondsElpased += 1;
                }
                               
                // Call the callback Method (depricated)
                this.onTick();
        }
       
       
        /**
         * Accessor method for getting the number of seconds elapsed
         *
         *      @param Void
         *      @return Void
        */

        public function getElapsed():Number
        {
                return this._secondsElpased
        }
       
       
        /**
         * Call back method letting you hook into the timer.
         *
         *      @param Void
         *      @return Void
         *      @depricated
        */

        public function onTick():Void
        {
        }
}
 

  1. 3 Responses to “Accurate Timing in ActionScript”

  2. Thank you for this class.

    I’m not so cool in actionscript, and i’m interested, why there is
    “ trace (‘Clock at: ‘ + stopWatch.getElapsed()) ”, and not
    “ trace (‘Clock at: ‘ + this.getElapsed()) ”?

    By il.zoff on Feb 21, 2008

  3. Hi il.zoff; glad the class was useful to you.

    The “this” keyword refers to logic which is happening inside a class; not an instance of class. As we have created the instance “stopWatch”, we need to call its method directly.

    Hope that helps.

    By Jonny on Feb 21, 2008

  4. Dear Sir,

    i have tried to download the flash files. When i tried to open the .fla file i got an error message that it was not in the appropriate format. I should be using actionscript 2 and i cannot understand why it could not open. The other file opened and run well showing the changing seconds.

    thanks

    By Sarah Pule' on Jan 15, 2009

Post a Comment