Have you ever written code like this:
newBullet.x = hero.gun.x; newBullet.y = hero.gun.y; |
…and found that the bullet appears in a seemingly random place compared to the hero’s gun?
Or have you ever traced tank.turret.rotation and found that it always returns zero, even when it’s clearly pointing diagonally?
Maybe you’ve compared the width of two identical objects, one obviously double the size of the other, and found that Flash thinks they are the same.
Every Flash dev I know has run across one of these and sat scratching their head — myself included!
So what’s going on?
Well, it’s all to do with addChild()…
The Flash Bucket
Think of Flash as a bucket. When you look at a SWF, you’re looking into a container — a DisplayObjectContainer, to be precise.
To demonstrate, let’s suppose I’ve got three MovieClips (or Sprites, or Bitmaps, or whatever, it doesn’t matter), and I addChild() them to the document class:
public class DocumentClass extends MovieClip { public var bigM:BigM = new BigM(); public var bigJ:BigJ = new BigJ(); public var bigW:BigW = new BigW(); public var shapes:Shapes = new Shapes(); public function DocumentClass() { bigM.x = 25; bigJ.x = 160; bigW.x = 270; bigM.y = 140; bigJ.y = 140; bigW.y = 140; addChild( bigM ); addChild( bigJ ); addChild( bigW ); addChild( shapes ); } } |

(I think you can guess which symbol has which instance name.)
When I say Flash is like a bucket, I mean it looks like this:

When you addChild() a symbol, you drop it into the bucket, and it sits slightly above everything else that’s in there. Of course, unless the objects overlap, you don’t notice this.
Buckets of Buckets
Now let’s take a look at what happens with a more complicated display object. Suppose we make a new class like this:
public class Shapes extends MovieClip { public var greenTriangle:GreenTriangle = new GreenTriangle(); public var blueCircle:BlueCircle = new BlueCircle(); public var redSquare:RedSquare = new RedSquare(); public function Shapes() { greenTriangle.x = 25; blueCircle.x = 160; redSquare.x = 270; greenTriangle.y = 270; blueCircle.y = 270; redSquare.y = 270; addChild( greenTriangle ); addChild( blueCircle ); addChild( redSquare ); } } |
As a bucket, we can imagine that this symbol will look like this:

…but we won’t see any of this inside the SWF unless we addChild() it to the document class:
public var shapes:Shapes = new Shapes(); //...missed out some code here... shapes.x = 0; shapes.y = 0; //...missed out some more code... addChild( shapes ); |
Now the SWF will look like this:

…but what about in bucket view? Well, we don’t just drop each new shape into the document class bucket; rather, we drop each shape into the Shapes bucket, and then drop the whole Shapes bucket into the document class bucket:

OK, cool. Now, you can see that the M is just above the green triangle, so they should have the same x-values but different y-values, right? We can check with a trace():
addChild( shapes ); trace( bigM.x, shapes.greenTriangle.x, bigM.y, shapes.greenTriangle.y ); |
This returns 25 25 140 270 as we’d expect. So far, no problems.
What if we move the M to the right by 50 pixels?
addChild( shapes ); bigM.x += 50; trace( bigM.x, shapes.greenTriangle.x, bigM.y, shapes.greenTriangle.y ); |

The trace() now returns 75 25 140 270 — again, just as it looks. For the sake of completeness, let’s try moving the green triangle too:
bigM.y += 10; shapes.greenTriangle.y += 10; trace( bigM.x, shapes.greenTriangle.x, bigM.y, shapes.greenTriangle.y ); |

Now the trace() returns 75 75 140 270. Great. Let’s just set everything back how it was, now:
addChild( bigM ); addChild( bigJ ); addChild( bigW ); addChild( shapes ); //no changes to any x-values |
There’s one thing we haven’t tried, of course; moving the shapes object.
Stretchy Buckets
What happens if we move the entire shapes bucket 50 pixels to the right?
addChild( shapes ); shapes.x += 50; |
It looks like this:

This time, the green triangle is 50 pixels to the right of the M, so presumably its x-value will be 50 bigger:
addChild( shapes ); shapes.x += 50; trace( bigM.x, shapes.greenTriangle.x ); |
Hang on… the trace() returns 25 25. It says the two x-values are the same. What’s happened?
It’s easier to understand in bucket view:

It’s a little unclear, but the shapes bucket has shifted to the right, just as we told it to. This has caused the document class bucket to stretch wide enough to fit it — though note that the Flash player window still has the same view of the document class that it did before.
If you look at the shapes bucket, you can see that the green triangle is still as far away from the left-hand side of that bucket as it was before. And here’s where the confusion lies.
See, x and y are just values that say how far away an object is from the top-left corner of its parent — that is, the bucket to which the object is addChild()-ed.
Here, I’ll prove it. When you call addChild() on an object, its x- and y-values don’t change. Also, an object can only have one parent at any given time. This means we can change the green triangles parent from the shape class to the document class:
addChild( shapes ); shapes.x += 50; addChild( shapes.greenTriangle ); |
The green triangle is still a variable belonging to the Shapes class, which is why we have to write shapes.greenTriangle to access it, but now we’ve changed which bucket it’s in. The SWF looks like this:

…and in bucket view, it looks like this:

The triangle’s x-value is the same as before (we could trace it and prove it) but it now refers to its distance from the left edge of the document class, rather than from shapes. Also note that it’s dropped on top of everything else inside the document class bucket.
The same applies to rotation, width and height, but they’re a little harder to visualise.
Other Affected Properties
For this part, I’m going to go back to how things were at the start, with shapes in its normal place and the green triangle inside the shapes bucket.
Also, to make things simpler, I’ll draw a border around the edge of the Shapes class so that we can see it:
public function Shapes() { graphics.lineStyle( 6, 0x330066 ); graphics.drawRect( 0, 0, 400, 380 ); |
(I’ve used those values because they’re the dimensions of my Flash movie.)
By default, shapes.rotation is zero, so let’s change it and see what happens:
//etc shapes.rotation -= 10; |

Not a surprising result. You probably won’t be surprised either to learn that the rotation values of each shape is still zero. Only their parent’s rotation is different.
What about the x- and y-values of the triangle now?
trace( shapes.bigTriangle.x, shapes.bigTriangle.y ); |
This returns: 25 270, the same as before. It’s a little odd, because the distance from the triangle to the left side of the shapes bucket actually has changed this time, if we measure horizontally:

What’s happened is, when we rotated shapes, we also rotated the x-axis and y-axis used by every child of shapes!

We say that each DisplayObjectContainer has its own coordinate system (its own pair of x- and y-axes).
So instead of thinking as the triangle’s x-value as its distance from the left edge of its parent, we should think of it as the distance from the edge of its parent along the negative x-direction. The same goes for its y-value. (Remember, in Flash, y points “downwards”. Well, until you rotate it!)
This is kind of complicated when written out in words, so I like to sketch out a picture (like the one above) whenever I get confused.
How about width? Let’s set the rotation back to normal and try changing the width instead:
addChild( shapes ); shapes.width *= 2; |

Here, the circle is clearly twice as wide as it is high.
trace( shapes.blueCircle.width, shapes.blueCircle.height ); |
…and yet this trace returns 83 83. Once again, the shapes coordinate system has been changed; this time it’s been stretched.

If we were to now set shapes.bigTriangle.x += 1, we would actually see the triangle move two pixels to the right. So the x-value of the triangle can no longer be measured in pixels, since pixels are a property of the computer screen.
What do we call them, then? Well, they’re just “units”. Units-that-would-be-equal-to-pixels-if-everything-were-at-its-correct-size is a more accurate name, but a little unwieldy.
What To Do About All This
The simplest way to avoid getting in a big mess is to make sure every single object we’d ever want to refer to in code has the same parent: the document class. That way, if two objects have the same x- and y-values, we know they are in the same place, because they must be using the same coordinate system.
Of course, this is ridiculously inconvenient. Fortunately, we can get the benefits of a shared coordinate system without the drawbacks.
There is a function, localToGlobal(), that will take the x- and y-values of a point in one coordinate system, and return the x- and y-values of the same point in the document class’s coordinate system.
What? OK, here’s an example. Suppose I move the shapes over to the right:
addChild( shapes ); shapes.x += 135; //no longer altering the width or rotation |

Now, we know that the green triangle’s x-value is going to say the same as before — the same as the M — if we trace it. And yet, we can see that in the coordinate system of the document class, it should have the same x-value as the J. We can use localToGlobal to show this:
var triangleCoordinatesInsideShapes:Point = new Point( shapes.greenTriangle.x, shapes.greenTriangle.y ); var triangleCoordinatesInsideDocumentClass:Point = shapes.localToGlobal( triangleCoordinatesInsideShapes ); trace( bigJ.x, triangleCoordinatesInsideDocumentClass.x ); |
As you can see, localToGlobal is run on the shapes object, not the triangle; this is because we are converting from the shapes coordinate system.
That trace statement returns 160 160, just as we’d hope, showing that the green triangle and big J have the same x-coordinate relative to the document class.
What about the other way around? Use the globalToLocal() function. This takes a point in the coordinate system of the document class and returns the same point in any given object’s coordinate system, like this:
var bigJCoordinatesInsideDocumentClass:Point = new Point( bigJ.x, bigJ.y ); var bigJCoordinatesInsideShapes:Point = shapes.globalToLocal( bigJCoordinatesInsideDocumentClass); trace( bigJCoordinatesInsideShapes.x, shapes.greenTriangle.x ); |
That returns 25 25 — again, the coordinates match, but this time they’re given relative to shapes.
If you want to compare two objects that are in different containers, but neither of them is a child of the document class, you can combine the localToGlobal() and globalToLocal() functions.
For example, here I’ll add another instance of the shapes class, but I’ll set its alpha to 0.5 so that it looks transparent:
public var transparentShapes:Shapes = new Shapes(); //...missed out some code here... transparentShapes.x = 0; transparentShapes.y = 0; //...missed out some more code... addChild( transparentShapes ); transparentShapes.alpha = 0.5; transparentShapes.x += 135; |

(Note that I’ve also moved transparentShapes to the right by 135 units.)
Here, naturally, we’d find that shapes.bigTriangle.x and transparentShapes.bigTriangle.x were equal, because we haven’t moved either triangle within its parent. But we want to show that the transparent triangle has the same coordinates as the opaque circle. We need to chain a globalToLocal and a localToGlobal command:
var circleCoordinatesInsideShapes:Point = new Point( shapes.blueCircle.x, shapes.blueCircle.y ); var circleCoordinatesInsideDocumentClass:Point = shapes.localToGlobal( circleCoordinatesInsideShapes ); var circleCoordinatesInsideTransparentShapes:Point = transparentShapes.globalToLocal( circleCoordinatesInsideDocumentClass ); trace( circleCoordinatesInsideTransparentShapes.x, transparentShapes.greenTriangle.x ); |
This trace gives us 25 25. Success!
What About Rotation and Width?
Rotation and width are slightly more awkward to deal with.
Rotation is just a matter of addition and subtraction. If we wanted to see what angle the opaque triangle was at in the coordinate space of the document class, we’d have to add shapes.rotation and shapes.greenTriangle.rotation. If we wanted to work out its angle in the coordinate space of the transparent shapes, we’d then have to subtract transparentShapes.rotation.
For widths and heights we have the getBounds() method. It’s used like this:
var triangleBounds:Rectangle = shapes.greenTriangle.getBounds( this ); trace( triangleBounds.width ); |
That’ll give us the width of the triangle in the coordinate space of the document class (because we’re writing code inside the document class, that’s what this will refer to).
Note that the format is different to localToGlobal; you call the function as a method of the object whose width you want to find out, and pass the desired coordinate space as an argument — in localToGlobal its the other way around.
If for some reason you wanted to find the width of an object apart from any lines used to draw it, you could use the getRect() method instead. It’s called in the same way.
So that’s pretty much everything you’d need to know about coordinate systems in Flash. Any questions?

{ 22 comments… read them below or add one }
Understanding Flash’s Coordinate Systems http://bit.ly/3PxjTr
This comment was originally posted on Twitter
I think I know where the inspiration for this post came from
Nice job! I can’t say I completely understand it with the addChild and Shapes things, but this as3 doesn’t seem so bad. Perhaps I will have to switch soon. We’ll see.
Ryan´s last blog ..Freelance Flash Games Forums Now Live!
Nice write-up, Michael! Pure in depth genius. -Jeff
8bitjeff´s last blog ..8/6/2009 Mochi First Impressions Game Review Mash-Up: Thursday, Aug 06, 2009
@Ryan: Haha yep, it was your question the other day that made me think of this. So thanks
Good to see your timeline for learning AS3 has made another jump: “eventually -> sometime soon -> soon”
@Jeff: Thanks!
@Michael I enjoyed your post and thanks for sharing this. I liked the idea of looking at flash player as a bucket, it makes sense and clears my confusion on localToGlobal and globalToLocal
Thank you
Chetan Sachdev´s last blog ..Debugging TLF source code
Haha yeah. But it may have jumped back to sometime soon with news I heard from Cavalcade games in the fgl forums: http://www.flashgamelicense.com/view_thread.php?thread_id=10157&last_read_post_id=63128
I didn’t read all 3, but it sounded like it was about how as3 was taking a step towards being more powerful, but going away from being simple. Then, they just suggested features that flash should have. I think flash is taking suggestions for features, so that’s good at least.
One of them mentioned Haxe too, which I may have to check out. Their site claims they offer better performance and language features, but I’d have to see if it was worth learning over as3.
Ryan´s last blog ..Freelance Flash Games Forums Now Live!
@Chetan: Thank you! I’m glad to have helped
@Ryan: Cheers for that link, interesting reading. This debate fires up every now and again, and it usually hinges around the “AS2 is easier” argument. The thing is, “easy” is a relative term — if you’re used to AS2 then AS3’s event listeners and forced class-based structure seems really hard, but if you’re used to AS3 then AS2 seems messy and often frustrating.
If they seemed like completely different languages for different purposes and different types of users, like AS2 and C# (or, heck, let’s go crazy: AS2 and Haskell), I don’t think there’d be so much of a problem. Trouble is, they’re both used to make Flash apps, and that “3″ makes it seem like AS3 is an incremental improvement to AS2.
Plus, there’s this notion of “making the jump” to AS3, like it’s a one-way path and once you’ve tried it you can never go back. Nonsense!
I recommend trying it out, just to have a go. See it as a separate language, as though you were going to have a go at learning Java or C# just for fun. I happen to know of a beginner-level tutorial for doing just that
You might find you hate it, in which case no harm’s been done and you can just stick with AS2, or you might enjoy it enough to want to learn more about it (while still doing other work in AS2).
Either way, for the sake of a couple days playing around, you don’t have to guess at an opinion any more.
Good call. I’ll give it a try, within… a week we’ll say. Hopefully I don’t keep putting it off, but we’ll see.
Ryan´s last blog ..Concepts First
Nice article, and the illustrations were incredibly helpful! (65% of the population are visual learners, and 86% of statistics are made up on the spot) It’s definitely one I’ll need to bookmark in my list of “Articles that I won’t memorize…so I need to remember them somehow” Thanks for all your help. Your blog is an asset to the community.
CommentLuv? What’s that? Almost posted that… then I googled and found the answer to my own question. Suprisingly their website doesn’t really explain it well. Either that or you have to search for it (which I tried unsuccessfully) had to change my website URL to the direct link in order for it to work
Bigfoot´s last blog ..MJW Avoider Game ep11ch1
Odd, I already knew all this, I think it’s because of my experience in AS2. It didn’t fool me for more than 5 seconds when I first encountered it.
But very useful post to those who don’t understand!
@Ryan: haha OK.
@Bigfoot: Thanks! Yeah I find doodling out a little diagram like that really helps me. Odd that CommentLuv didn’t work first time.
@Arxanas: Cheers
You mean it didn’t fool you the first time you encountered it in AS2, or in AS3?
Is there a way to rearrange the order of these containers? For example, in my game, I’m firing bullets, but they appear on top of the gun and start moving, as opposed to under, so it seems as though it’s actually leaving the gun. I know AS2 had depth control, but does AS3 have any such thing?
Although the logical solution is probably to create a container for the bullets (and organize every type of object in the game) beneath the gun…
Sure — actually there are several ways. As I understand it, AS2 gave every object a “depth index”, right? AS3 does a similar thing, but with one big exception: there are no gaps. So, you can’t have an object with index 4 and then insert the next one at index 1000. Also, if you have objects at index 0, 1, and 2 (say), then insert another item at index 1, the objects currently at index 1 and 2 will move to indices 3 and 4.
The relevant functions are:
container.getChildIndex( child:DisplayObject ):int — gets the depth index of the child object within
container.container.setChildIndex( child:DisplayObject, index:int ) — sets the depth index of the child object within
container.container.addChildAt( child:DisplayObject, index:int ) — like
addChild()except inserts the new object at the specified index withincontainer.container.getChildAt( index:int ):DisplayObject — gets the object within
containerat the given index.container.swapChildren( child1:DisplayObject, child2:DisplayObject ) — swaps the indices of the two objects specified (must be within
container)container.swapChildrenAt( index1:int, index2:int ) — swaps the indices of the two objects that are at the given indices.
And of course you can combine these, like writing
container.addChildAt( newBullet, getChildIndex( gun ) - 1 )to add the new bullet underneath the gun.Personally I really like using the method you suggested, separating objects into different containers. This is particularly useful for making a HUD that doesn’t scroll and a background that scrolls at a different rate to the main sprites.
Ah, very useful. I remember someone at FrozenHaddock’s forum asked how to keep stuff on top (namely, a mute button). This would have been rather useful to whomever that was. Can indexes (indices, whatever) also go negative? Or is it all above zero?
And do these containers exist on certain indexes, which then have some sort of virtual “sub-indexes”?
The lowest object in a container has an index of 0, and none can be negative.
All the DisplayObjects within a DisplayObjectContainer have an index from 0 to ( number of display objects – 1 ). If one of those DisplayObjects happens to be a DisplayObjectContainer itself, and contain even more DisplayObjects, then each of those DisplayObjects has an index from 0 to ( number of display objects in the inner DisplayObjectContainer – 1 ). And so on.
It’s called a “composite pattern”; it’s self-similar, like a fractal. Hard to explain in text, haha!
Okay then, that’s what I needed to know. Thanks!
That’s definitely the most in depth explanation I’ve ever seen. If ever I need to explain this to someone, this is definitely where I’ll send them, great work.
Porter´s last blog: Do Sponsors Care About More Than CTR?
Thanks, Porter!
Awesome post, thanks for all the detail. I’ve been struggling with learning actionscript and its websites like yours that keep allowing me to make slow and steady progress. Of course, I usually find websites like yours after I have beaten my head bloody against the wall trying to figure out some of these quirks! Thanks again!
Free Games´s last blog: Square Jump
Thank you Free Games
Haha, yes the same thing happens with me, I often find posts just after I need them
If you’re looking for a more practical application of all this, check out my new tutorial on parallax scrolling.