Return to Tutorials index

Introduction

This page lists some findings and notes related to Flash and ActionScript. The no installation-hassles and ubiquity of Flash has also contributed to the practical popularity of the flash platform. (Flash is now, where Java Applets were originally supposed to be).

Some good flash references:

Books: flash journalism, essential AS2
Components: flash exchange
Misc: Flash player internals

Introduction to timelines

Each timeline has 1 or more layers. Each layer has frames, which are like one frame of a movie reel. Like a movie, a series of frames can be played at speed - and presto ! - we get animation and movement.

Movies typically play at 24 frames/sec. Flash can play at any frame rate but is typically set to 12-18 fps.

Flash (by default) loops the frames, so once the player gets to the last frame, it starts again at the first one. Flash can be programmatically told not to play at all or to stop looping, etc.

A frame by default has no content. This is like an empty picture reel. This is called a regular frame.

Any frame that has (or can have) content is called a keyframe. (I would have called it content-frame myself but whatever, that's the terminology).

If the frame can have content (but has none to start out with, purposefully), then it's called a blank keyframe. If you draw on a blank keyframe, it becomes a non-empty keyframe.

 

This is relevant, since in Flash, to add content to any non-keyframe, it is not sufficient to just select that frame and start drawing. One must manually convert that frame to a keyframe first. Else, if one selects as regular frame and starts drawing/adding content to it, the content is added to the first prior keyframe behind that frame.

A new flash document starts with one blank keyframe as Frame #1. If that initial frame is deleted, then one must first create at least one new keyframe before content can be added to that document.

As it moves down the timeline, Flash will keep displaying the content of the last keyframe it encountered until it comes to the next keyframe, whereby it will show the contents of that next keyframe. Animation is done by slightly modifying the content betweeen keyframe to keyframe, much like the 24/second snap shots of a real movie.

Actionscript Code flow

Actionscript is Javascript with some more built-in objects (like video, webcam, movie etc.). Actionscript code can look exactly like traditional Javascript. However, much of the actionscript (AS) code one encounters is written with some AS-specific extensions, notably strong–typing. These extensions were intially supposed to be part of the Javascript 2.0 language (which may or may not actually happen, specification-wise). However, the AS interpreter has been open sourced and released under the Mozilla Tamarin project.

This doesn't mean that one must use strong-typing. I personally prefer (for smaller projects, at least), the simpler, traditional JS syntax and shy away from AS extensions (including strong typing).

For larger projects, strong typing is very useful. (remember: the compiler is always your best friend).

Traditional syntax:var foo = "hello";
AS-extended
(optional):
var foo:String = "hello";

Timeline Execution

Note: For these examples, the flasm open-source flash byte-code de-compiler is used. These examples are in Flash 8 (ActionScript 2) but the concepts are equally applicable to later flash versions as well.

Actionscript variables can be declared on any frame. They are only available to the frame in which they are declared and subsequent frames. The code is re-run every time the frame is revisted (in the normal looping movie, the first frame is revisted at the beginning of another loop).


1) One single variable on the first frame


var n = 1;

	

movie 'flash_1.swf' // flash 8, total frames: 1, frame rate: 12 fps,
550x400 px

  frame 0
    push 'n', 1
    varEquals
  end // of frame 0
end

	


2) One single variable on the first frame


var n = 1;
n++;
trace(n);

	

movie 'flash_1.swf' // flash 8, total frames: 1, frame rate: 12 fps,
550x400 px

  frame 0
    constants 'n'  
    push 'n', 1
    varEquals
    push 'n', 'n'
    getVariable
    increment
    setVariable
    push 'n'
    getVariable
    trace
  end // of frame 0
end

	
Output:

2
	

2 is output only once.

The flash player optimizes the timeline, such that, it does not loop over frames, if there is only 1 frame defined. This means that the code in that single frame will be called/execute just once, and not over and over again.

This is non-intuitive and poorly documented. One expects the flash player to keep looping, even over a single frame, at the movie fps setting.


3) One single variable on the first frame (more than 1 frame)

This is the same as the previous example, but with another (albiet empty) keyframe added.


var n = 1;
n++;
trace(n);

	

movie 'flash_1.swf' compressed // flash 8, total frames: 5, frame rate:
12 fps, 550x400 px

  frame 0
    constants 'n'  
    push 'n', 1
    varEquals
    push 'n', 'n'
    getVariable
    increment
    setVariable
    push 'n'
    getVariable
    trace
  end // of frame 0
end

	
Output:

2
2
2
...
	

The flash player now loops across all frames (since there is more than 1 frame) and '2' is printed forever

Note, interestingly, when the flash player loops back to the first frame, the variables are initialized again and the code behaves as if we were running the entire code (including initializers) for the first time. So we don't see 2, 3, 4,... but rather 2, 2, 2.... There is no way to initialize global state just once in a simple manner. (however, the example below shows how to do this in a non-simple manner).


4) Initializing variables just once

Since variables are reinitialized per loop, we can either (a) stop the player from looping (using the timeline control gotoAndStop) or (b) we can put in a check to see if the variable has been initialized already.

This latter example is shown below.


var n;

if (! n) {
    n = 1;
    }

n++;
(n);

	

movie 'flash_1.swf' compressed // flash 8, total frames: 5, frame rate: 12 fps, 550x400 px

  frame 0
    constants 'n'  
    push 'n'
    var
    push 'n'
    getVariable
    not
    not
    branchIfTrue label1
    push 'n', 1
    setVariable
   label1:
    push 'n', 'n'
    getVariable
    increment
    setVariable
    push 'n'
    getVariable
    trace
  end // of frame 0
end

	
Output:

2
3
4
5
...
	

The variable n is now initialized just once (if it's not already initialized). We see the expected sequence 2, 3, 4, 5...

Actionscript, like Javascript has the defined keyword. Variables are defined the first time flash runs through the loop although they are re-initialized every subsequent time through the loop. In the above example, we could also have said:


if (n == undefined) { //(same as saying: ! n)
    n = 1;
    }


5) Where are variables stored ?

Are variables stored on a frame specific scope ? Is there a global/movie specific scope and is that where variables are stored ? Let's find out...


var n = 1;
trace (n === _root.n);

	

movie 'flash_1.swf' compressed // flash 8, total frames: 5, frame rate: 12 fps, 550x400 px

  frame 0
    constants 'n', '_root'  
    push 'n', 1
    varEquals
    push 'n'
    getVariable
    push '_root'
    getVariable
    push 'n'
    getMember
    strictEquals
    trace
  end // of frame 0
end

	
Output:

true
true
...
	

All variables are stored in the per-movie clip _root global object.

In Actionscript3, they are stored in the MainTimeline object, which is the same as stage.getChild(0). Generally, in AS3, static class variables are the recommended way to store global variables, these can be accessed via the_classname.the_variable (since class names have global scope).

Scope Resolution

Like Javascript, Actionscript has a scope chain. At the top is _global, movie clip (for all movie clips), a per-movie clip global (_root) is somewhere in the middle and various sundry objects along the chain (that are not interesting and/or I don't feel like documenting).


6) From where can variables be accessed ?

On Frame 1:
trace (n);

On Frame 5:
var n = 1;

	

movie 'flash_1.swf' compressed // flash 8, total frames: 5, frame rate: 12 fps, 550x400 px

  frame 0
    push 'n'
    getVariable
    trace
  end // of frame 0

  frame 4
    push 'n', 1
    varEquals
  end // of frame 4
end

	
Output:

undefined
1
1
...
	

As expected, the variable is first defined and put in the _root object (via push) in frame #5 ( the bytecode is 0-indexed so frame #5 shows up as #4).

The first time through, in frame #1 the variable is not defined so undefined is printed.

Then flash, gets to frame #5 defines the variable and next time through the loop, frame #1 sees the variable set to the previous value (1 in this case).

Basically, each frame is setting/getting/creating variables somewhere in the scope chain (by default, the _root or MainTimeline via code in each frame. This is akin to the following Javascript.

<script>
//frame 1
trace(n);

//frame 2
var n = 1;

//frame 3
n++
//etc.
</script>
That is, each frame's code is sequentially executed, as if it was written in sequential fashion, in one large code block.

7) Multiple layers in the same movie

What does the bytecode for multiple layers in the same movie look like ?


Note: Layer 2 is above layer 1 in this example.

On Frame 1, layer 2:
var n = 100;

On Frame 1, layer 1:
trace (n);
	
movie 'flash_1.swf' compressed // flash 8, total frames: 5, frame rate: 12 fps, 550x400 px

  frame 0
    push 'n', 100
    varEquals
  end // of frame 0

  frame 0
    push 'n'
    getVariable
    trace
  end // of frame 0
end

	
Output:

100
100
...
	

Code in layers are converted in the layer order. On each frame, code on top most layers is executed first and code on bottom layers is executed later.


8) Multiple layers in the same movie

This example is similar to the example above, except the order of layers is reversed.


Note: Layer 2 is below layer 1 in this example.

On Frame 1, layer 1:
trace (n);

On Frame 1, layer 2:
var n = 100;
	
movie 'flash_1.swf' compressed // flash 8, total frames: 5, frame rate: 12 fps, 550x400 px

  frame 0
    push 'n'
    getVariable
    trace
  end // of frame 0

  frame 0
    push 'n', 100
    varEquals
  end // of frame 0
end

	
Output:

undefined
100
100
...
	

As expected, the code in frame #1, layer 1 is executed first. Since n is undefined the first time around, "undefined" is printed. Then frame #2, layer 2 code runs and initializes n. Thereafter, the next time around, this value of n is printed. (compare this to the previous example).


9) Function declarations

Shows the bytecode corresponding to a simple function declaration on the first frame.


var n = 1;
n++;

function foo() { 
  trace(n);
 }
 
foo();

	

movie 'flash_1.swf' compressed // flash 8, total frames: 5, frame rate:
12 fps, 550x400 px

  frame 0
    constants 'n', 'foo'  
    function foo ()
      push 'n'
      getVariable
      trace
    end // of function foo

    push 'n', 1
    varEquals
    push 'n', 'n'
    getVariable
    increment
    setVariable
    push 0.0, 'foo'
    callFunction
    pop
  end // of frame 0
end
	


10) Event handling

A simple event handler added to a button.


button_1.onPress = foo;
function foo() {
    trace("button_1 clicked");
	}
	
	
movie 'flash_1.swf' compressed // flash 8, total frames: 1, frame rate: 12 fps, 550x400 px

  frame 0
    function foo ()
      push 'button_1 clicked'
      trace
    end // of function foo

    push 'button_1'
    getVariable
    push 'onPress', 'foo'
    getVariable
    setMember
  end // of frame 0
end

	
Output:

button_1 clicked
	

When the button is clicked, button_1 clicked is output to the console.

Flash has a single-threaded interpreter. Code in frames in executed, frame by frame and (unless stopped), the interpreter loops back to the first frame and starts from the first frame again.

So how are events handled ? Events are asynchronous, in that they can happen anytime the user clicks on a button or types a keystroke or the flash VM received network data. If the flash interpreter is single threaded, and chugging along merrily, from frame to frame, then how does it:

  1. Know an event was produced ? (say a button was clicked on frame #1 when the interpreter is running code on frame #5)
  2. Handle that event ?

Flash documentation is sorely lacking in this regard. As of Sept. 20, 2008, there is not even a single (!) public adobe/macromedia document that talks about how flash event dispatching is implemented. I've downloaded the tamarin source code and also the gnash open source flash-player source code to investigate this issue. The gnash player code (in particular) is very nicely written.

From what I can gather, from reading the source and by this post (link opens in new window) on the gnash mailing list, this is how things work:

-- The operating system informs the flash player/VM about events such as user click, network actity, etc. These events are internally (by another thread, or the main thread when it's free, or by the operating system, or whatever) put in an event-buffer

-- The main execution thread, periodically looks at the event buffer and invokes functions that (we in actionscript have registered to) handle that event. This polling of the event buffer is done at each frame boundary (before the interpreter moves from the current frame to the next frame) and/or at some fixed interval (say every 10ms), which is an internal setting in the flash player implementation.


Random Actionscript 3 notes

Actionscript 3 implements closures differently than ActionScript 2.

Closures in ActionScript 3 are not compatible with Javascript.

var str = "bad dog";

function foo() {
	var hoopty = "hoopty";
	trace(this.str + "; " + hoopty);
}

var joe = new Object();
joe.str = "i am joe";
joe.foo =  foo;
joe.foo();

In both ActionScript 2 and Javascript (replace "trace" with "alert" to run this in a browser), this prints/alerts:

i am joe; hoopty

In ActionScript 3, this same example prints:

bad dog; hoopty

It's expected that all of AS2/AS3/Javascript save the local variable hoopty via a closure mechanism. That does happen and so far, well and good.

However, in AS3, note, the problem is that the "this" pointer is also getting saved as part of the closure in:
function foo() {...}.

This is incompatible with JS. (AS3 calls capturing the "this" pointer as part of closure, a bound method). However, in Javascript, closures are strictly over local function variables/parameters, in lexical scope. AS3's behavior is extremely non-intuitive and bad, because it completely breaks from what Javascript does. In Javascript, the "this" pointer is never part of any closure and "this" always refers to the object through which a method was invoked.