New game demo, initial version
diff --git a/examples/game/lib/game_demo.dart b/examples/game/lib/game_demo.dart
index ff83916..def48f8 100644
--- a/examples/game/lib/game_demo.dart
+++ b/examples/game/lib/game_demo.dart
@@ -1,5 +1,6 @@
 library game;
 
+import 'dart:async';
 import 'dart:sky' as sky;
 import 'dart:math' as Math;
 import 'sprites.dart';
@@ -10,4 +11,4 @@
 import 'package:sky/animation/curves.dart';
 import 'package:vector_math/vector_math_64.dart';
 
-part 'game_demo_world.dart';
+part 'game_demo_node.dart';
diff --git a/examples/game/lib/game_demo_node.dart b/examples/game/lib/game_demo_node.dart
new file mode 100644
index 0000000..d6c420c
--- /dev/null
+++ b/examples/game/lib/game_demo_node.dart
@@ -0,0 +1,784 @@
+part of game;
+
+final double _gameSizeWidth = 320.0;
+double _gameSizeHeight = 320.0;
+
+final bool _drawDebug = false;
+
+class GameDemoNode extends NodeWithSize {
+
+  GameDemoNode(
+    this._images,
+    this._spritesGame,
+    this._spritesUI,
+    this._sounds,
+    this._gameOverCallback
+  ): super(new Size(320.0, 320.0)) {
+    // Add background
+    _background = new RepeatedImage(_images["assets/starfield.png"]);
+    addChild(_background);
+
+    // Create starfield
+    _starField = new StarField(_spritesGame, 200);
+    addChild(_starField);
+
+    // Add nebula
+    _nebula = new RepeatedImage(_images["assets/nebula.png"], sky.TransferMode.plus);
+    addChild(_nebula);
+
+    // Setup game screen, it will always be anchored to the bottom of the screen
+    _gameScreen = new Node();
+    addChild(_gameScreen);
+
+    // Setup the level and add it to the screen, the level is the node where
+    // all our game objects live. It is moved to scroll the game
+    _level = new Level();
+    _gameScreen.addChild(_level);
+
+    _objectFactory = new GameObjectFactory(_spritesGame, _sounds, _level);
+
+    _level.ship = new Ship(_objectFactory);
+    _level.addChild(_level.ship);
+
+    // Add the joystick
+    _joystick = new VirtualJoystick();
+    _gameScreen.addChild(_joystick);
+
+    // Add HUD
+    _hud = new Hud(_spritesUI);
+    addChild(_hud);
+
+
+    addObjects();
+  }
+
+  // Resources
+  ImageMap _images;
+  Map<String, SoundEffect> _sounds;
+  SpriteSheet _spritesGame;
+  SpriteSheet _spritesUI;
+
+  // Sounds
+  SoundEffectPlayer _effectPlayer = SoundEffectPlayer.sharedInstance();
+
+  // Callback
+  Function _gameOverCallback;
+
+  // Game screen nodes
+  Node _gameScreen;
+  VirtualJoystick _joystick;
+
+  GameObjectFactory _objectFactory;
+  Level _level;
+  StarField _starField;
+  RepeatedImage _background;
+  RepeatedImage _nebula;
+  Hud _hud;
+
+  // Game properties
+  double _scrollSpeed = 2.0;
+  double _scroll = 0.0;
+
+  int _framesToFire = 0;
+  int _framesBetweenShots = 20;
+
+  bool _gameOver = false;
+
+  void spriteBoxPerformedLayout() {
+    _gameSizeHeight = spriteBox.visibleArea.height;
+    _gameScreen.position = new Point(0.0, _gameSizeHeight);
+  }
+
+  void update(double dt) {
+    // Scroll the level
+    _scroll = _level.scroll(_scrollSpeed);
+    _starField.move(0.0, _scrollSpeed);
+
+    _background.move(_scrollSpeed * 0.1);
+    _nebula.move(_scrollSpeed);
+
+    // Add objects
+    addObjects();
+
+    // Move the ship
+    if (!_gameOver) {
+      _level.ship.applyThrust(_joystick.value, _scroll);
+    }
+
+    // Add shots
+    if (_framesToFire == 0 && _joystick.isDown && !_gameOver) {
+      fire();
+      _framesToFire = _framesBetweenShots;
+    }
+    if (_framesToFire > 0) _framesToFire--;
+
+    // Move game objects
+    for (Node node in _level.children) {
+      if (node is GameObject) {
+        node.move();
+      }
+    }
+
+    // Remove offscreen game objects
+    for (int i = _level.children.length - 1; i >= 0; i--) {
+      Node node = _level.children[i];
+      if (node is GameObject) {
+        node.removeIfOffscreen(_scroll);
+      }
+    }
+
+    if (_gameOver) return;
+
+    // Check for collisions between lasers and objects that can take damage
+    List<Laser> lasers = [];
+    for (Node node in _level.children) {
+      if (node is Laser) lasers.add(node);
+    }
+
+    List<GameObject> damageables = [];
+    for (Node node in _level.children) {
+      if (node is GameObject && node.canBeDamaged) damageables.add(node);
+    }
+
+    for (Laser laser in lasers) {
+      for (GameObject damageable in damageables) {
+        if (laser.collidingWith(damageable)) {
+          // Hit something that can take damage
+          _hud.score += damageable.addDamage(laser.impact);
+          laser.destroy();
+        }
+      }
+    }
+
+    // Check for collsions between ship and objects that can damage the ship
+    List<Node> nodes = new List.from(_level.children);
+    for (Node node in nodes) {
+      if (node is GameObject && node.canDamageShip) {
+        if (node.collidingWith(_level.ship)) {
+          // The ship was hit :(
+          killShip();
+          _level.ship.visible = false;
+        }
+      }
+    }
+  }
+
+  int _chunk = 0;
+  double _chunkSpacing = 640.0;
+
+  void addObjects() {
+
+    while (_scroll + _chunkSpacing >= _chunk * _chunkSpacing) {
+      addLevelChunk(
+        _chunk,
+        -_chunk * _chunkSpacing - _chunkSpacing);
+
+      _chunk += 1;
+    }
+  }
+
+  void addLevelChunk(int chunk, double yPos) {
+    if (chunk == 0) {
+      // Leave the first chunk empty
+      return;
+    } else if (chunk == 1) {
+      addLevelAsteroids(10, yPos, 0.0);
+    } else {
+      addLevelAsteroids(9 + chunk, yPos, 0.5);
+    }
+  }
+
+  void addLevelAsteroids(int numAsteroids, double yPos, double distribution) {
+    for (int i = 0; i < numAsteroids; i++) {
+      GameObjectType type = (randomDouble() < distribution) ? GameObjectType.asteroidBig : GameObjectType.asteroidSmall;
+      Point pos = new Point(randomSignedDouble() * 160.0,
+                            yPos + _chunkSpacing * randomDouble());
+      _objectFactory.addGameObject(type, pos);
+    }
+  }
+
+  void fire() {
+    Laser shot0 = new Laser(_objectFactory);
+    shot0.position = _level.ship.position + new Offset(17.0, -10.0);
+    _level.addChild(shot0);
+
+    Laser shot1 = new Laser(_objectFactory);
+    shot1.position = _level.ship.position + new Offset(-17.0, -10.0);
+    _level.addChild(shot1);
+
+    _effectPlayer.play(_sounds["laser"]);
+  }
+
+  void killShip() {
+    // Hide ship
+    _level.ship.visible = false;
+
+    _effectPlayer.play(_sounds["explosion"]);
+
+    // Add explosion
+    Explosion explo = new Explosion(_spritesGame);
+    explo.scale = 1.5;
+    explo.position = _level.ship.position;
+    _level.addChild(explo);
+
+    // Add flash
+    Flash flash = new Flash(size, 1.0);
+    addChild(flash);
+
+    // Set the state to game over
+    _gameOver = true;
+
+    // Return to main scene and report the score back in 2 seconds
+    new Timer(new Duration(seconds: 2), () { _gameOverCallback(_hud.score); });
+  }
+}
+
+class VirtualJoystick extends NodeWithSize {
+  VirtualJoystick() : super(new Size(160.0, 160.0)) {
+    userInteractionEnabled = true;
+    handleMultiplePointers = false;
+    position = new Point(160.0, -20.0);
+    pivot = new Point(0.5, 1.0);
+    _center = new Point(size.width / 2.0, size.height / 2.0);
+    _handlePos = _center;
+
+    _paintHandle = new Paint()
+      ..color=new Color(0xffffffff);
+    _paintControl = new Paint()
+      ..color=new Color(0xffffffff)
+      ..strokeWidth = 1.0
+      ..setStyle(sky.PaintingStyle.stroke);
+  }
+
+  Point value = Point.origin;
+
+  bool _isDown = false;
+  bool get isDown => _isDown;
+
+  Point _pointerDownAt;
+  Point _center;
+  Point _handlePos;
+
+  Paint _paintHandle;
+  Paint _paintControl;
+
+  bool handleEvent(SpriteBoxEvent event) {
+    if (event.type == "pointerdown") {
+      _pointerDownAt = event.boxPosition;
+      actions.stopAll();
+      _isDown = true;
+    }
+    else if (event.type == "pointerup" || event.type == "pointercancel") {
+      _pointerDownAt = null;
+      value = Point.origin;
+      ActionTween moveToCenter = new ActionTween((a) => _handlePos = a, _handlePos, _center, 0.4, elasticOut);
+      actions.run(moveToCenter);
+      _isDown = false;
+    } else if (event.type == "pointermove") {
+      Offset movedDist = event.boxPosition - _pointerDownAt;
+
+      value = new Point(
+        (movedDist.dx / 80.0).clamp(-1.0, 1.0),
+        (movedDist.dy / 80.0).clamp(-1.0, 1.0));
+
+        _handlePos = _center + new Offset(value.x * 40.0, value.y * 40.0);
+    }
+    return true;
+  }
+
+  void paint(PaintingCanvas canvas) {
+    applyTransformForPivot(canvas);
+    canvas.drawCircle(_handlePos, 25.0, _paintHandle);
+    canvas.drawCircle(_center, 40.0, _paintControl);
+  }
+}
+
+class Level extends Node {
+  Level() {
+    position = new Point(160.0, 0.0);
+  }
+
+  Ship ship;
+
+  double scroll(double scrollSpeed) {
+    position += new Offset(0.0, scrollSpeed);
+    return position.y;
+  }
+}
+
+abstract class GameObject extends Node {
+  double radius = 0.0;
+  double removeLimit = 1280.0;
+  bool canDamageShip = true;
+  bool canBeDamaged = true;
+  double maxDamage = 3.0;
+  double damage = 0.0;
+
+  Paint _paintDebug = new Paint()
+    ..color=new Color(0xffff0000)
+    ..strokeWidth = 1.0
+    ..setStyle(sky.PaintingStyle.stroke);
+
+  bool collidingWith(GameObject obj) {
+    return (GameMath.pointQuickDist(position, obj.position)
+      < radius + obj.radius);
+  }
+
+  void move() {
+  }
+
+  void removeIfOffscreen(double scroll) {
+    ;
+    if (-position.y > scroll + removeLimit ||
+        -position.y < scroll - 50.0) {
+      removeFromParent();
+    }
+  }
+
+  void destroy() {
+    if (parent != null) {
+      Explosion explo = createExplosion();
+      if (explo != null) {
+        explo.position = position;
+        parent.addChild(explo);
+      }
+
+      removeFromParent();
+    }
+  }
+
+  int addDamage(double d) {
+    if (!canBeDamaged) return 0;
+
+    damage += d;
+    if (damage >= maxDamage) {
+      destroy();
+      return (maxDamage * 10).ceil();
+    }
+    return 10;
+  }
+
+  Explosion createExplosion() {
+    return null;
+  }
+
+  void paint(PaintingCanvas canvas) {
+    if (_drawDebug) {
+      canvas.drawCircle(Point.origin, radius, _paintDebug);
+    }
+    super.paint(canvas);
+  }
+
+  void setupActions() {
+  }
+}
+
+class Ship extends GameObject {
+  Ship(GameObjectFactory f) {
+    // Add main ship sprite
+    _sprt = new Sprite(f.sheet["ship.png"]);
+    _sprt.scale = 0.3;
+    _sprt.rotation = -90.0;
+    addChild(_sprt);
+    radius = 20.0;
+
+    canBeDamaged = false;
+    canDamageShip = false;
+
+    // Set start position
+    position = new Point(0.0, 50.0);
+  }
+
+  Sprite _sprt;
+
+  void applyThrust(Point joystickValue, double scroll) {
+    Point oldPos = position;
+    Point target = new Point(joystickValue.x * 160.0, joystickValue.y * 220.0 - 250.0 - scroll);
+    double filterFactor = 0.2;
+
+    position = new Point(
+      GameMath.filter(oldPos.x, target.x, filterFactor),
+      GameMath.filter(oldPos.y, target.y, filterFactor));
+  }
+}
+
+class Laser extends GameObject {
+  double impact = 1.0;
+
+  Laser(GameObjectFactory f) {
+    // Add sprite
+    _sprt = new Sprite(f.sheet["laser.png"]);
+    _sprt.scale = 0.3;
+    _sprt.transferMode = sky.TransferMode.plus;
+    addChild(_sprt);
+    radius = 10.0;
+    removeLimit = 640.0;
+
+    canDamageShip = false;
+    canBeDamaged = false;
+  }
+
+  Sprite _sprt;
+
+  void move() {
+    position += new Offset(0.0, -10.0);
+  }
+}
+
+abstract class Obstacle extends GameObject {
+
+  Obstacle(this._f);
+
+  double explosionScale = 1.0;
+  GameObjectFactory _f;
+
+  Explosion createExplosion() {
+    SoundEffectPlayer.sharedInstance().play(_f.sounds["explosion"]);
+    Explosion explo = new Explosion(_f.sheet);
+    explo.scale = explosionScale;
+    return explo;
+  }
+}
+
+abstract class Asteroid extends Obstacle {
+  Asteroid(GameObjectFactory f) : super(f);
+
+  Sprite _sprt;
+
+  void setupActions() {
+    // Rotate obstacle
+    int direction = 1;
+    if (randomDouble() < 0.5) direction = -1;
+    ActionTween rotate = new ActionTween(
+      (a) => _sprt.rotation = a,
+      0.0, 360.0 * direction, 5.0 + 5.0 * randomDouble());
+    _sprt.actions.run(new ActionRepeatForever(rotate));
+  }
+
+  set damage(double d) {
+    super.damage = d;
+    int alpha = ((200.0 * d) ~/ maxDamage).clamp(0, 200);
+    _sprt.colorOverlay = new Color.fromARGB(alpha, 255, 3, 86);
+  }
+}
+
+class AsteroidBig extends Asteroid {
+  AsteroidBig(GameObjectFactory f) : super(f) {
+    _sprt = new Sprite(f.sheet["asteroid_big_${randomInt(3)}.png"]);
+    _sprt.scale = 0.3;
+    radius = 25.0;
+    maxDamage = 5.0;
+    addChild(_sprt);
+  }
+}
+
+class AsteroidSmall extends Asteroid {
+  AsteroidSmall(GameObjectFactory f) : super(f) {
+    _sprt = new Sprite(f.sheet["asteroid_small_${randomInt(3)}.png"]);
+    _sprt.scale = 0.3;
+    radius = 12.0;
+    maxDamage = 3.0;
+    addChild(_sprt);
+  }
+}
+
+enum GameObjectType {
+  asteroidBig,
+  asteroidSmall,
+}
+
+class GameObjectFactory {
+  GameObjectFactory(this.sheet, this.sounds, this.level);
+
+  SpriteSheet sheet;
+  Map<String,SoundEffect> sounds;
+  Level level;
+
+  void addGameObject(GameObjectType type, Point pos) {
+    GameObject obj;
+    if (type == GameObjectType.asteroidBig)
+      obj = new AsteroidBig(this);
+    else if (type == GameObjectType.asteroidSmall)
+      obj = new AsteroidSmall(this);
+
+    obj.position = pos;
+    obj.setupActions();
+
+    level.addChild(obj);
+  }
+}
+
+// class MovingObstacle extends Obstacle {
+//   MovingObstacle(SpriteSheet sheet, Map<String,SoundEffect> effects, ObstacleType type) : super (sheet, effects, type);
+//
+//   void setupAction() {
+//     actions.stopAll();
+//
+//     List<Offset> offsets = [
+//       new Offset(-160.0, 160.0),
+//       new Offset(-80.0, -160.0),
+//       new Offset(0.0, 160.0),
+//       new Offset(80.0, -160.0),
+//       new Offset(160.0, 160.0)];
+//
+//     List<Point> points = [];
+//     for (Offset offset in offsets) {
+//       points.add(position + offset);
+//     }
+//
+//     ActionSpline spline = new ActionSpline((a) => position = a, points, 4.0);
+//     actions.run(new ActionRepeatForever(spline));
+//   }
+// }
+
+class StarField extends NodeWithSize {
+  sky.Image _image;
+  SpriteSheet _spriteSheet;
+  int _numStars;
+  bool _autoScroll;
+
+  List<Point> _starPositions;
+  List<double> _starScales;
+  List<Rect> _rects;
+  List<Color> _colors;
+
+  final double _padding = 50.0;
+  Size _paddedSize = Size.zero;
+
+  Paint _paint = new Paint()
+    ..setFilterQuality(sky.FilterQuality.low)
+    ..isAntiAlias = false
+    ..setTransferMode(sky.TransferMode.plus);
+
+  StarField(this._spriteSheet, this._numStars, [this._autoScroll = false]) : super(Size.zero) {
+    _image = _spriteSheet.image;
+  }
+
+  void addStars() {
+    _starPositions = [];
+    _starScales = [];
+    _colors = [];
+    _rects = [];
+
+    size = spriteBox.visibleArea.size;
+    _paddedSize = new Size(size.width + _padding * 2.0,
+                           size.height + _padding * 2.0);
+
+    for (int i  = 0; i < _numStars; i++) {
+      _starPositions.add(new Point(randomDouble() * _paddedSize.width,
+                                   randomDouble() * _paddedSize.height));
+      _starScales.add(randomDouble() * 0.4);
+      _colors.add(new Color.fromARGB((255.0 * (randomDouble() * 0.5 + 0.5)).toInt(), 255, 255, 255));
+      _rects.add(_spriteSheet["star_${randomInt(2)}.png"].frame);
+    }
+  }
+
+  void spriteBoxPerformedLayout() {
+    addStars();
+  }
+
+  void paint(PaintingCanvas canvas) {
+    // Create a transform for each star
+    List<sky.RSTransform> transforms = [];
+    for (int i = 0; i < _numStars; i++) {
+      sky.RSTransform transform = new sky.RSTransform(
+        _starScales[i],
+        0.0,
+        _starPositions[i].x - _padding,
+        _starPositions[i].y - _padding);
+
+      transforms.add(transform);
+    }
+
+    // Draw the stars
+    canvas.drawAtlas(_image, transforms, _rects, _colors, sky.TransferMode.modulate, null, _paint);
+  }
+
+  void move(double dx, double dy) {
+    for (int i  = 0; i < _numStars; i++) {
+      double xPos = _starPositions[i].x;
+      double yPos = _starPositions[i].y;
+      double scale = _starScales[i];
+
+      xPos += dx * scale;
+      yPos += dy * scale;
+
+      if (xPos >= _paddedSize.width) xPos -= _paddedSize.width;
+      if (xPos < 0) xPos += _paddedSize.width;
+      if (yPos >= _paddedSize.height) yPos -= _paddedSize.height;
+      if (yPos < 0) yPos += _paddedSize.height;
+
+      _starPositions[i] = new Point(xPos, yPos);
+    }
+  }
+
+  void update(double dt) {
+    if (_autoScroll) {
+      move(0.0, dt * 100.0);
+    }
+  }
+}
+
+class RepeatedImage extends Node {
+  Sprite _sprt0;
+  Sprite _sprt1;
+
+  RepeatedImage(sky.Image image, [sky.TransferMode mode = null]) {
+    _sprt0 = new Sprite.fromImage(image);
+    _sprt0.size = new Size(1024.0, 1024.0);
+    _sprt0.pivot = Point.origin;
+    _sprt1 = new Sprite.fromImage(image);
+    _sprt1.size = new Size(1024.0, 1024.0);
+    _sprt1.pivot = Point.origin;
+    _sprt1.position = new Point(0.0, -1024.0);
+
+    if (mode != null) {
+      _sprt0.transferMode = mode;
+      _sprt1.transferMode = mode;
+    }
+
+    addChild(_sprt0);
+    addChild(_sprt1);
+  }
+
+  void move(double dy) {
+    double yPos = (position.y + dy) % 1024.0;
+    position = new Point(0.0, yPos);
+  }
+}
+
+class Explosion extends Node {
+  Explosion(SpriteSheet sheet) {
+    // Add particles
+    ParticleSystem particlesDebris = new ParticleSystem(
+      sheet["explosion_particle.png"],
+      rotateToMovement: true,
+      startRotation:90.0,
+      startRotationVar: 0.0,
+      endRotation: 90.0,
+      startSize: 0.3,
+      startSizeVar: 0.1,
+      endSize: 0.3,
+      endSizeVar: 0.1,
+      numParticlesToEmit: 25,
+      emissionRate:1000.0,
+      greenVar: 127,
+      redVar: 127
+    );
+    particlesDebris.zPosition = 1010.0;
+    addChild(particlesDebris);
+
+    ParticleSystem particlesFire = new ParticleSystem(
+      sheet["fire_particle.png"],
+      colorSequence: new ColorSequence([new Color(0xffffff33), new Color(0xffff3333), new Color(0x00ff3333)], [0.0, 0.5, 1.0]),
+      numParticlesToEmit: 25,
+      emissionRate: 1000.0,
+      startSize: 0.5,
+      startSizeVar: 0.1,
+      endSize: 0.5,
+      endSizeVar: 0.1,
+      posVar: new Point(10.0, 10.0),
+      speed: 10.0,
+      speedVar: 5.0
+    );
+    particlesFire.zPosition = 1011.0;
+    addChild(particlesFire);
+
+    // Add ring
+    Sprite sprtRing = new Sprite(sheet["explosion_ring.png"]);
+    sprtRing.transferMode = sky.TransferMode.plus;
+    addChild(sprtRing);
+
+    Action scale = new ActionTween( (a) => sprtRing.scale = a, 0.2, 1.0, 1.5);
+    Action scaleAndRemove = new ActionSequence([scale, new ActionRemoveNode(sprtRing)]);
+    Action fade = new ActionTween( (a) => sprtRing.opacity = a, 1.0, 0.0, 1.5);
+    actions.run(scaleAndRemove);
+    actions.run(fade);
+
+    // Add streaks
+    for (int i = 0; i < 5; i++) {
+      Sprite sprtFlare = new Sprite(sheet["explosion_flare.png"]);
+      sprtFlare.pivot = new Point(0.3, 1.0);
+      sprtFlare.scaleX = 0.3;
+      sprtFlare.transferMode = sky.TransferMode.plus;
+      sprtFlare.rotation = randomDouble() * 360.0;
+      addChild(sprtFlare);
+
+      double multiplier = randomDouble() * 0.3 + 1.0;
+
+      Action scale = new ActionTween( (a) => sprtFlare.scaleY = a, 0.3 * multiplier, 0.8, 1.5 * multiplier);
+      Action scaleAndRemove = new ActionSequence([scale, new ActionRemoveNode(sprtFlare)]);
+      Action fadeIn = new ActionTween( (a) => sprtFlare.opacity = a, 0.0, 1.0, 0.5 * multiplier);
+      Action fadeOut = new ActionTween( (a) => sprtFlare.opacity = a, 1.0, 0.0, 1.0 * multiplier);
+      Action fadeInOut = new ActionSequence([fadeIn, fadeOut]);
+      actions.run(scaleAndRemove);
+      actions.run(fadeInOut);
+    }
+  }
+}
+
+class Hud extends Node {
+  SpriteSheet sheet;
+  Sprite sprtBgScore;
+
+  bool _dirtyScore = true;
+  int _score = 0;
+
+  int get score => _score;
+
+  set score(int score) {
+    _score = score;
+    _dirtyScore = true;
+  }
+
+  Hud(this.sheet) {
+    position = new Point(310.0, 10.0);
+    scale = 0.6;
+
+    sprtBgScore = new Sprite(sheet["scoreboard.png"]);
+    sprtBgScore.pivot = new Point(1.0, 0.0);
+    sprtBgScore.scale = 0.6;
+    addChild(sprtBgScore);
+  }
+
+  void update(double dt) {
+    // Update score
+    if (_dirtyScore) {
+
+      sprtBgScore.removeAllChildren();
+
+      String scoreStr = _score.toString();
+      double xPos = -50.0;
+      for (int i = scoreStr.length - 1; i >= 0; i--) {
+        String numStr = scoreStr.substring(i, i + 1);
+        Sprite numSprt = new Sprite(sheet["number_$numStr.png"]);
+        numSprt.position = new Point(xPos, 49.0);
+        sprtBgScore.addChild(numSprt);
+        xPos -= 37.0;
+      }
+      _dirtyScore = false;
+    }
+  }
+}
+
+class Flash extends NodeWithSize {
+  Flash(Size size, this.duration) : super(size) {
+    ActionTween fade = new ActionTween((a) => _opacity = a, 1.0, 0.0, duration);
+    ActionSequence seq = new ActionSequence([fade, new ActionRemoveNode(this)]);
+    actions.run(seq);
+  }
+
+  double duration;
+  double _opacity = 1.0;
+  Paint _cachedPaint = new Paint();
+
+  void paint(PaintingCanvas canvas) {
+    // Update the color
+    _cachedPaint.color = new Color.fromARGB((255.0 * _opacity).toInt(),
+                                            255, 255, 255);
+    // Fill the area
+    applyTransformForPivot(canvas);
+    canvas.drawRect(new Rect.fromLTRB(0.0, 0.0, size.width, size.height),
+      _cachedPaint);
+  }
+}
diff --git a/examples/game/lib/game_demo_world.dart b/examples/game/lib/game_demo_world.dart
deleted file mode 100644
index 6d0e6e5..0000000
--- a/examples/game/lib/game_demo_world.dart
+++ /dev/null
@@ -1,752 +0,0 @@
-part of game;
-
-const double _steeringThreshold = 0.0;
-const double _steeringMax = 150.0;
-
-// Random generator
-Math.Random _rand = new Math.Random();
-
-const double _gameSizeWidth = 1024.0;
-const double _gameSizeHeight = 1024.0;
-
-const double _shipRadius = 30.0;
-const double _lrgAsteroidRadius = 40.0;
-const double _medAsteroidRadius = 20.0;
-const double _smlAsteroidRadius = 10.0;
-const double _maxAsteroidSpeed = 1.0;
-
-const int _lifeTimeLaser = 50;
-
-const int _numStarsInStarField = 150;
-
-const int _numFramesShieldActive = 60 * 5;
-const int _numFramesShieldFlickers = 60;
-
-class GameDemoWorld extends NodeWithSize {
-  // Images
-  sky.Image _imgNebula;
-
-  SpriteSheet _spriteSheet;
-  SpriteSheet _spriteSheetUI;
-
-  Map<String,SoundEffect> _sounds;
-  SoundEffectPlayer _soundPool = SoundEffectPlayer.sharedInstance();
-
-  Navigator _navigator;
-
-  // Inputs
-  double _joystickX = 0.0;
-  double _joystickY = 0.0;
-
-  Node _gameLayer;
-
-  Ship _ship;
-  Sprite _shield;
-  List<Asteroid> _asteroids = [];
-  List<Laser> _lasers = [];
-  StarField _starField;
-  Nebula _nebula;
-
-  // Game state
-  int _numFrames = 0;
-  bool _isGameOver = false;
-  int _gameOverFrame;
-  int _currentLevel = 0;
-
-  // Heads up display
-  Hud _hud;
-
-  Function _gameOverCallback;
-
-  GameDemoWorld(App app, this._navigator, ImageMap images, this._spriteSheet, this._spriteSheetUI, this._sounds, this._gameOverCallback) : super(new Size(_gameSizeWidth, _gameSizeHeight)) {
-    // Fetch images
-    _imgNebula = images["assets/nebula.png"];
-
-    _gameLayer = new Node();
-    this.addChild(_gameLayer);
-    // Add ship
-    addShip();
-
-    // Add background
-    Sprite sprtBackground = new Sprite.fromImage(images["assets/starfield.png"]);
-    sprtBackground.position = new Point(512.0, 512.0);
-    sprtBackground.zPosition = -3.0;
-    addChild(sprtBackground);
-
-    // Add starfield
-    _starField = new StarField(_spriteSheet, _numStarsInStarField);
-    _starField.zPosition = -2.0;
-    addChild(_starField);
-
-    // Add nebula
-    addNebula();
-
-    userInteractionEnabled = true;
-    handleMultiplePointers = true;
-
-    _hud = new Hud(_spriteSheetUI);
-    _hud.zPosition = 1000.0;
-    addChild(_hud);
-
-    // Setup level
-    setupLevel(0);
-  }
-
-  void setupLevel(int level) {
-    int numLargeAsteroids = 5 + level * 2;
-    int numMediumAsteroids = 5 + level * 2;
-
-    // Add some asteroids to the game world
-    for (int i = 0; i < numLargeAsteroids; i++) {
-      addAsteroid(AsteroidSize.large);
-    }
-    for (int i = 0; i < numMediumAsteroids; i++) {
-      addAsteroid(AsteroidSize.medium);
-    }
-
-    _numFrames = 0;
-    _shield.visible = true;
-  }
-
-  // Methods for adding game objects
-
-  void addAsteroid(AsteroidSize size, [Point pos]) {
-    Asteroid asteroid = new Asteroid(_spriteSheet, size);
-    asteroid.zPosition = 1.0;
-    if (pos != null) asteroid.position = pos;
-    _gameLayer.addChild(asteroid);
-    _asteroids.add(asteroid);
-
-    // Animate asteroid into the scene
-    Action action = new ActionTween((a) => asteroid.scale = a, 0.0, 1.0, 1.0, bounceOut);
-    _gameLayer.actions.run(action);
-  }
-
-  void addShip() {
-    Ship ship = new Ship(_spriteSheet["ship.png"]);
-    ship.zPosition = 10.0;
-    _gameLayer.addChild(ship);
-    _ship = ship;
-
-    _shield = new Sprite(_spriteSheet["shield.png"]);
-    _shield.zPosition = 11.0;
-    _shield.scale = 0.5;
-    _shield.transferMode = sky.TransferMode.plus;
-    _gameLayer.addChild(_shield);
-
-    Action rotate = new ActionRepeatForever(new ActionTween((a) => _shield.rotation = a, 0.0, 360.0, 1.0));
-    actions.run(rotate);
-  }
-
-  void addLaser() {
-    Laser laser = new Laser(_spriteSheet["laser.png"], _ship);
-    laser.zPosition = 8.0;
-    laser.constrainProportions = true;
-    _lasers.add(laser);
-    _gameLayer.addChild(laser);
-
-    _soundPool.play(_sounds["laser"]);
-  }
-
-  void addNebula() {
-    _nebula = new Nebula.withImage(_imgNebula);
-    _gameLayer.addChild(_nebula);
-  }
-
-  void addExplosion(AsteroidSize asteroidSize, Point position) {
-    Node explosionNode = new Node();
-
-    // Add particles
-    ParticleSystem particlesDebris = new ParticleSystem(
-        _spriteSheet["explosion_particle.png"],
-        rotateToMovement: true,
-        startRotation:90.0,
-        startRotationVar: 0.0,
-        endRotation: 90.0,
-        startSize: 0.3,
-        startSizeVar: 0.1,
-        endSize: 0.3,
-        endSizeVar: 0.1,
-        numParticlesToEmit: 25,
-        emissionRate:1000.0,
-        greenVar: 127,
-        redVar: 127
-    );
-    particlesDebris.zPosition = 1010.0;
-    explosionNode.addChild(particlesDebris);
-
-    ParticleSystem particlesFire = new ParticleSystem(
-      _spriteSheet["fire_particle.png"],
-      colorSequence: new ColorSequence([new Color(0xffffff33), new Color(0xffff3333), new Color(0x00ff3333)], [0.0, 0.5, 1.0]),
-      numParticlesToEmit: 25,
-      emissionRate: 1000.0,
-      startSize: 0.5,
-      startSizeVar: 0.1,
-      endSize: 0.5,
-      endSizeVar: 0.1,
-      posVar: new Point(10.0, 10.0),
-      speed: 10.0,
-      speedVar: 5.0
-    );
-    particlesFire.zPosition = 1011.0;
-    explosionNode.addChild(particlesFire);
-
-
-    // Add ring
-    Sprite sprtRing = new Sprite(_spriteSheet["explosion_ring.png"]);
-    sprtRing.transferMode = sky.TransferMode.plus;
-    explosionNode.addChild(sprtRing);
-
-    Action scale = new ActionTween( (a) => sprtRing.scale = a, 0.2, 1.0, 1.5);
-    Action scaleAndRemove = new ActionSequence([scale, new ActionRemoveNode(sprtRing)]);
-    Action fade = new ActionTween( (a) => sprtRing.opacity = a, 1.0, 0.0, 1.5);
-    actions.run(scaleAndRemove);
-    actions.run(fade);
-
-    // Add streaks
-    for (int i = 0; i < 5; i++) {
-      Sprite sprtFlare = new Sprite(_spriteSheet["explosion_flare.png"]);
-      sprtFlare.pivot = new Point(0.3, 1.0);
-      sprtFlare.scaleX = 0.3;
-      sprtFlare.transferMode = sky.TransferMode.plus;
-      sprtFlare.rotation = _rand.nextDouble() * 360.0;
-      explosionNode.addChild(sprtFlare);
-
-      double multiplier = _rand.nextDouble() * 0.3 + 1.0;
-
-      Action scale = new ActionTween( (a) => sprtFlare.scaleY = a, 0.3 * multiplier, 0.8, 1.5 * multiplier);
-      Action scaleAndRemove = new ActionSequence([scale, new ActionRemoveNode(sprtFlare)]);
-      Action fadeIn = new ActionTween( (a) => sprtFlare.opacity = a, 0.0, 1.0, 0.5 * multiplier);
-      Action fadeOut = new ActionTween( (a) => sprtFlare.opacity = a, 1.0, 0.0, 1.0 * multiplier);
-      Action fadeInOut = new ActionSequence([fadeIn, fadeOut]);
-      actions.run(scaleAndRemove);
-      actions.run(fadeInOut);
-    }
-
-    explosionNode.position = position;
-    explosionNode.zPosition = 1010.0;
-
-    if (asteroidSize == AsteroidSize.large) {
-      explosionNode.scale = 1.5;
-    }
-
-    _gameLayer.addChild(explosionNode);
-
-    _soundPool.play(_sounds["explosion"]);
-  }
-
-  void update(double dt) {
-    // Move asteroids
-    for (Asteroid asteroid in _asteroids) {
-      asteroid.position = pointAdd(asteroid.position, asteroid._movementVector);
-    }
-
-    // Move lasers and remove expired lasers
-    for (int i = _lasers.length - 1; i >= 0; i--) {
-      Laser laser = _lasers[i];
-      laser.move();
-      if (laser._frameCount > _lifeTimeLaser) {
-        laser.removeFromParent();
-        _lasers.removeAt(i);
-      }
-    }
-
-    // Apply thrust to ship
-    if (_joystickX != 0.0 || _joystickY != 0.0) {
-      _ship.thrust(_joystickX, _joystickY);
-    }
-
-    // Move ship
-    _ship.move();
-    _shield.position = _ship.position;
-
-    // Check collisions between asteroids and lasers
-    for (int i = _lasers.length -1; i >= 0; i--) {
-      // Iterate over all the lasers
-      Laser laser = _lasers[i];
-
-      for (int j = _asteroids.length - 1; j >= 0; j--) {
-        // Iterate over all the asteroids
-        Asteroid asteroid = _asteroids[j];
-
-        // Check for collision
-        if (pointQuickDist(laser.position, asteroid.position) < laser.radius + asteroid.radius) {
-          // Remove laser
-          laser.removeFromParent();
-          _lasers.removeAt(i);
-
-          // Add asteroids and explosions
-          if (asteroid._asteroidSize == AsteroidSize.large) {
-            for (int a = 0; a < 3; a++) addAsteroid(AsteroidSize.medium, asteroid.position);
-          }
-          else if (asteroid._asteroidSize == AsteroidSize.medium) {
-            for (int a = 0; a < 5; a++) addAsteroid(AsteroidSize.small, asteroid.position);
-          }
-
-          addExplosion(asteroid._asteroidSize, asteroid.position);
-
-          // Remove asteroid
-          asteroid.removeFromParent();
-          _asteroids.removeAt(j);
-
-          // Scoring
-          if (asteroid._asteroidSize == AsteroidSize.large)
-            addScore(100);
-          else if (asteroid._asteroidSize == AsteroidSize.medium)
-            addScore(50);
-          else
-            addScore(10);
-          break;
-        }
-      }
-    }
-
-    // Check collisions between asteroids and ship
-    if (_numFrames > _numFramesShieldActive) {
-      // Shield is no longer active
-
-      for (int i = _asteroids.length - 1; i >= 0; i--) {
-        // Iterate over all the asteroids
-        Asteroid asteroid = _asteroids[i];
-
-        if (pointQuickDist(asteroid.position, _ship.position) < asteroid.radius + _ship.radius) {
-          killShip();
-        }
-      }
-    }
-
-    // Move objects to center camera and warp objects around the edges
-    centerCamera();
-    warpObjects();
-
-    // Check for level up
-    if (_asteroids.length == 0) {
-      _currentLevel++;
-      setupLevel(_currentLevel);
-    }
-
-    // Update shield
-    if (_numFrames > _numFramesShieldActive) _shield.visible = false;
-    else if (_numFrames > _numFramesShieldActive - _numFramesShieldFlickers) _shield.visible = !_shield.visible;
-
-    // Check for exit back to main screen
-    if (_isGameOver && _numFrames - _gameOverFrame == 60) {
-      _navigator.pop();
-    }
-
-    _numFrames++;
-  }
-
-  void centerCamera() {
-    const cameraDampening = 0.1;
-    Point delta = new Point(_gameSizeWidth/2 - _ship.position.x, _gameSizeHeight/2 - _ship.position.y);
-    delta = pointMult(delta, cameraDampening);
-
-    for (Node child in _gameLayer.children) {
-      child.position = pointAdd(child.position, delta);
-    }
-
-    // Update starfield
-    _starField.move(delta.x, delta.y);
-  }
-
-  void warpObjects() {
-    for (Node child in _gameLayer.children) {
-      if (child.position.x < 0) child.position = pointAdd(child.position, new Point(_gameSizeWidth, 0.0));
-      if (child.position.x >= _gameSizeWidth) child.position = pointAdd(child.position, new Point(-_gameSizeWidth, 0.0));
-      if (child.position.y < 0) child.position = pointAdd(child.position, new Point(0.0, _gameSizeHeight));
-      if (child.position.y >= _gameSizeHeight) child.position = pointAdd(child.position, new Point(0.0, -_gameSizeHeight));
-    }
-  }
-
-  void killShip() {
-    if (_isGameOver) return;
-
-    // Set game over
-    _isGameOver = true;
-    _gameOverFrame = _numFrames;
-    _gameOverCallback(_hud.score);
-
-    // Remove the ship
-    _ship.visible = false;
-
-    // Add an explosion
-    addExplosion(AsteroidSize.large, _ship.position);
-  }
-
-  // Handling controls
-
-  void controlSteering(double x, double y) {
-    // Reset controls if it's game over
-    if (_isGameOver) {
-      x = y = 0.0;
-    }
-
-    _joystickX = x;
-    _joystickY = y;
-  }
-
-  void controlFire() {
-    // Don't shoot if it's game over
-    if (_isGameOver) return;
-
-    addLaser();
-  }
-
-  // Handle pointer events
-
-  int _firstPointer = -1;
-  int _secondPointer = -1;
-  Point _firstPointerDownPos;
-
-  bool handleEvent(SpriteBoxEvent event) {
-
-    Point pointerPos = convertPointToNodeSpace(event.boxPosition);
-    int pointer = event.pointer;
-
-    switch (event.type) {
-      case 'pointerdown':
-        if (_firstPointer == -1) {
-          // Assign the first pointer
-          _firstPointer = pointer;
-          _firstPointerDownPos = pointerPos;
-        }
-        else if (_secondPointer == -1) {
-          // Assign second pointer
-          _secondPointer = pointer;
-          controlFire();
-        }
-        else {
-          // There is a pointer used for steering, let's fire instead
-          controlFire();
-        }
-        break;
-      case 'pointermove':
-        if (pointer == _firstPointer) {
-          // Handle turning control
-          double joystickX = 0.0;
-          double deltaX = pointerPos.x - _firstPointerDownPos.x;
-          if (deltaX > _steeringThreshold || deltaX < -_steeringThreshold) {
-            joystickX = (deltaX - _steeringThreshold)/(_steeringMax - _steeringThreshold);
-            if (joystickX > 1.0) joystickX = 1.0;
-            if (joystickX < -1.0) joystickX = -1.0;
-          }
-
-          double joystickY = 0.0;
-          double deltaY = pointerPos.y - _firstPointerDownPos.y;
-          if (deltaY > _steeringThreshold || deltaY < -_steeringThreshold) {
-            joystickY = (deltaY - _steeringThreshold)/(_steeringMax - _steeringThreshold);
-            if (joystickY > 1.0) joystickY = 1.0;
-            if (joystickY < -1.0) joystickY = -1.0;
-          }
-
-          controlSteering(joystickX, joystickY);
-        }
-        break;
-      case 'pointerup':
-      case 'pointercancel':
-        if (pointer == _firstPointer) {
-          // Un-assign the first pointer
-          _firstPointer = -1;
-          _firstPointerDownPos = null;
-          controlSteering(0.0, 0.0);
-        }
-        else if (pointer == _secondPointer) {
-          _secondPointer = -1;
-        }
-        break;
-      default:
-        break;
-    }
-    return true;
-  }
-
-  // Scoring and HUD
-  void addScore(int score) {
-    _hud.score += score;
-  }
-}
-
-// Game objects
-
-enum AsteroidSize {
-  small,
-  medium,
-  large,
-}
-
-class Asteroid extends Sprite {
-  Point _movementVector;
-  AsteroidSize _asteroidSize;
-  double _radius;
-
-  double get radius {
-    if (_radius != null) return _radius;
-    if (_asteroidSize == AsteroidSize.small) _radius = _smlAsteroidRadius;
-    else if (_asteroidSize == AsteroidSize.medium) _radius = _medAsteroidRadius;
-    else if (_asteroidSize == AsteroidSize.large) _radius = _lrgAsteroidRadius;
-    return _radius;
-  }
-
-  Asteroid(SpriteSheet spriteSheet, AsteroidSize this._asteroidSize) {
-    size = new Size(radius * 2.0, radius * 2.0);
-    position = new Point(_gameSizeWidth * _rand.nextDouble(), _gameSizeHeight * _rand.nextDouble());
-    rotation = 360.0 * _rand.nextDouble();
-
-    if (_asteroidSize == AsteroidSize.small) {
-      texture = spriteSheet["asteroid_small_${_rand.nextInt(2)}.png"];
-    } else {
-      texture = spriteSheet["asteroid_big_${_rand.nextInt(2)}.png"];
-    }
-
-    _movementVector = new Point(_rand.nextDouble() * _maxAsteroidSpeed * 2 - _maxAsteroidSpeed,
-                                _rand.nextDouble() * _maxAsteroidSpeed * 2 - _maxAsteroidSpeed);
-
-    userInteractionEnabled = true;
-
-    // Rotate forever
-    double direction = (_rand.nextBool()) ? 360.0 : -360.0;
-    ActionTween rot = new ActionTween( (a) => rotation = a, 0.0, direction, 2.0 * _rand.nextDouble() + 2.0);
-    ActionRepeatForever repeat = new ActionRepeatForever(rot);
-    actions.run(repeat);
-  }
-
-  bool handleEvent(SpriteBoxEvent event) {
-    if (event.type == "pointerdown") {
-      actions.stopWithTag("fade");
-      colorOverlay = new Color(0x99ffffff);
-    }
-    else if (event.type == "pointerup") {
-      // Fade out the color overlay
-      Action fadeOut = new ActionTween((a) => this.colorOverlay = a, new Color(0x99ffffff), new Color(0x00ffffff), 1.0);
-      Action fadeOutAndRemove = new ActionSequence([fadeOut, new ActionCallFunction(() => this.colorOverlay = null)]);
-      actions.run(fadeOutAndRemove, "fade");
-    }
-    return false;
-  }
-}
-
-class Ship extends Sprite {
-  Vector2 _movementVector;
-  double _rotationTarget;
-  double radius = _shipRadius;
-
-  Ship(Texture img) : super(img) {
-    _movementVector = new Vector2.zero();
-    rotation = _rotationTarget = 270.0;
-
-    // Create sprite
-    size = new Size(_shipRadius * 2.0, _shipRadius * 2.0);
-    position = new Point(_gameSizeWidth/2.0, _gameSizeHeight/2.0);
-  }
-
-  void thrust(double x, double y) {
-    _rotationTarget = convertRadians2Degrees(Math.atan2(y, x));
-    Vector2 directionVector = new Vector2(x, y).normalize();
-    _movementVector.addScaled(directionVector, 1.0);
-  }
-
-  void move() {
-    position = new Point(position.x + _movementVector[0], position.y + _movementVector[1]);
-    _movementVector.scale(0.9);
-
-    rotation = dampenRotation(rotation, _rotationTarget, 0.1);
-  }
-}
-
-class Laser extends Sprite {
-  int _frameCount = 0;
-  Point _movementVector;
-  double radius = 20.0;
-
-  Laser(Texture img, Ship ship) : super(img) {
-    size = new Size(30.0, 30.0);
-    position = ship.position;
-    rotation = ship.rotation + 90.0;
-    transferMode = sky.TransferMode.plus;
-    double rotRadians = convertDegrees2Radians(rotation);
-    _movementVector = pointMult(new Point(Math.sin(rotRadians), -Math.cos(rotRadians)), 10.0);
-    _movementVector = new Point(_movementVector.x + ship._movementVector[0], _movementVector.y + ship._movementVector[1]);
-  }
-
-  void move() {
-    position = pointAdd(position, _movementVector);
-    _frameCount++;
-  }
-}
-
-// Background starfield
-
-class StarField extends NodeWithSize {
-  sky.Image _image;
-  int _numStars;
-  bool _autoScroll;
-  List<Point> _starPositions;
-  List<double> _starScales;
-  List<Rect> _rects;
-  List<Color> _colors;
-  Paint _paint = new Paint()
-    ..setFilterQuality(sky.FilterQuality.low)
-    ..isAntiAlias = false
-    ..setTransferMode(sky.TransferMode.plus);
-
-  StarField(SpriteSheet spriteSheet, this._numStars, [this._autoScroll = false]) : super(new Size(1024.0, 1024.0)) {
-    _starPositions = [];
-    _starScales = [];
-    _colors = [];
-    _rects = [];
-
-    for (int i  = 0; i < _numStars; i++) {
-      _starPositions.add(new Point(_rand.nextDouble() * _gameSizeWidth, _rand.nextDouble() * _gameSizeHeight));
-      _starScales.add(_rand.nextDouble());
-      _colors.add(new Color.fromARGB((255.0 * (_rand.nextDouble() * 0.5 + 0.5)).toInt(), 255, 255, 255));
-      _rects.add(spriteSheet["star_${_rand.nextInt(2)}.png"].frame);
-    }
-
-    _image = spriteSheet.image;
-  }
-
-  void paint(PaintingCanvas canvas) {
-    // Create a transform for each star
-    List<sky.RSTransform> transforms = [];
-    for (int i = 0; i < _numStars; i++) {
-      sky.RSTransform transform = new sky.RSTransform(_starScales[i], 0.0, _starPositions[i].x, _starPositions[i].y);
-      transforms.add(transform);
-    }
-
-    // Draw the stars
-    canvas.drawAtlas(_image, transforms, _rects, _colors, sky.TransferMode.modulate, null, _paint);
-  }
-
-  void move(double dx, double dy) {
-    for (int i  = 0; i < _numStars; i++) {
-      double xPos = _starPositions[i].x;
-      double yPos = _starPositions[i].y;
-      double scale = _starScales[i];
-
-      xPos += dx * scale;
-      yPos += dy * scale;
-
-      if (xPos >= _gameSizeWidth) xPos -= _gameSizeWidth;
-      if (xPos < 0) xPos += _gameSizeWidth;
-      if (yPos >= _gameSizeHeight) yPos -= _gameSizeHeight;
-      if (yPos < 0) yPos += _gameSizeHeight;
-
-      _starPositions[i] = new Point(xPos, yPos);
-    }
-  }
-
-  void update(double dt) {
-    if (_autoScroll) {
-      move(dt * 100.0, 0.0);
-    }
-  }
-}
-
-class Hud extends NodeWithSize {
-  SpriteSheet spriteSheetUI;
-  Sprite sprtBgScore;
-  Sprite sprtBgShield;
-  bool _dirtyScore = true;
-
-  int _score = 0;
-
-  int get score => _score;
-
-  set score(int score) {
-    _score = score;
-    _dirtyScore = true;
-  }
-
-  Hud(this.spriteSheetUI) : super(Size.zero) {
-    pivot = Point.origin;
-
-    sprtBgScore = new Sprite(spriteSheetUI["scoreboard.png"]);
-    sprtBgScore.pivot = new Point(1.0, 0.0);
-    sprtBgScore.scale = 0.6;
-    addChild(sprtBgScore);
-
-    sprtBgShield = new Sprite(spriteSheetUI["bar_shield.png"]);
-    sprtBgShield.pivot = Point.origin;
-    sprtBgShield.scale = 0.6;
-    // TODO: Add shield
-    //addChild(sprtBgShield);
-  }
-
-  void spriteBoxPerformedLayout() {
-    // Set the size and position of HUD display
-    position = spriteBox.visibleArea.topLeft;
-    size = spriteBox.visibleArea.size;
-
-    // Position hud objects
-    sprtBgShield.position = new Point(20.0, 20.0);
-    sprtBgScore.position = new Point(size.width - 20.0, 20.0);
-  }
-
-  void update(double dt) {
-    // Update score
-    if (_dirtyScore) {
-
-      sprtBgScore.removeAllChildren();
-
-      String scoreStr = _score.toString();
-      double xPos = -50.0;
-      for (int i = scoreStr.length - 1; i >= 0; i--) {
-        String numStr = scoreStr.substring(i, i + 1);
-        Sprite numSprt = new Sprite(spriteSheetUI["number_$numStr.png"]);
-        numSprt.position = new Point(xPos, 49.0);
-        sprtBgScore.addChild(numSprt);
-        xPos -= 37.0;
-      }
-      _dirtyScore = false;
-    }
-    // Update power bar
-  }
-}
-
-class Nebula extends Node {
-
-  Nebula.withImage(sky.Image img) {
-    for (int i = 0; i < 2; i++) {
-      for (int j = 0; j < 2; j++) {
-        Sprite sprt = new Sprite.fromImage(img);
-        sprt.transferMode = sky.TransferMode.plus;
-        sprt.pivot = Point.origin;
-        sprt.position = new Point(i * _gameSizeWidth - _gameSizeWidth, j * _gameSizeHeight - _gameSizeHeight);
-        addChild(sprt);
-      }
-    }
-  }
-}
-
-// Convenience methods
-
-Point pointAdd(Point a, Point b) {
-  return new Point(a.x+ b.x, a.y + b.y);
-}
-
-Point pointMult(Point a, double multiplier) {
-  return new Point(a.x * multiplier, a.y * multiplier);
-}
-
-double dampenRotation(double src, double dst, double dampening) {
-  double delta = dst - src;
-  while (delta > 180.0) delta -= 360;
-  while (delta < -180) delta += 360;
-  delta *= dampening;
-
-  return src + delta;
-}
-
-double pointQuickDist(Point a, Point b) {
-  double dx = a.x - b.x;
-  double dy = a.y - b.y;
-  if (dx < 0.0) dx = -dx;
-  if (dy < 0.0) dy = -dy;
-  if (dx > dy) {
-    return dx + dy/2.0;
-  }
-  else {
-    return dy + dx/2.0;
-  }
-}
diff --git a/examples/game/lib/main.dart b/examples/game/lib/main.dart
index 1a6aeb6..1efda12 100644
--- a/examples/game/lib/main.dart
+++ b/examples/game/lib/main.dart
@@ -77,10 +77,19 @@
 class GameDemoApp extends App {
 
   NavigationState _navigationState;
-  GameDemoWorld _game;
+  NodeWithSize _game;
   int _lastScore = 0;
 
   void initState() {
+    // _game = new GameDemoNode(
+    //   _imageMap,
+    //   _spriteSheet,
+    //   _spriteSheetUI,
+    //   _sounds,
+    //   (lastScore) {
+    //     setState(() {_lastScore = lastScore;});
+    //   });
+
     _navigationState = new NavigationState([
       new Route(
         name: '/',
@@ -112,24 +121,23 @@
   }
 
   Widget _buildGameScene(navigator, route) {
-    return new SpriteWidget(_game);
+    return new SpriteWidget(_game, SpriteBoxTransformMode.fixedWidth);
   }
 
   Widget _buildMainScene(navigator, route) {
     return new Stack([
-      new SpriteWidget(new MainScreenBackground()),
+      new SpriteWidget(new MainScreenBackground(), SpriteBoxTransformMode.fixedWidth),
       new Flex([
         new TextureButton(
           onPressed: () {
-            _game = new GameDemoWorld(
-              _app,
-              navigator,
+            _game = new GameDemoNode(
               _imageMap,
               _spriteSheet,
               _spriteSheetUI,
               _sounds,
               (lastScore) {
                 setState(() {_lastScore = lastScore;});
+                navigator.pop();
               }
             );
             navigator.pushNamed('/game');
@@ -243,14 +251,19 @@
 }
 
 class MainScreenBackground extends NodeWithSize {
-  MainScreenBackground() : super(new Size(1024.0, 1024.0)) {
-    Sprite sprtBackground = new Sprite.fromImage(_imageMap['assets/starfield.png']);
-    sprtBackground.position = new Point(512.0, 512.0);
-    addChild(sprtBackground);
+  MainScreenBackground() : super(new Size(320.0, 320.0)) {
+    // Sprite sprtBackground = new Sprite.fromImage(_imageMap['assets/starfield.png']);
+    // sprtBackground.position = new Point(160.0, 160.0);
+    // addChild(sprtBackground);
 
     assert(_spriteSheet.image != null);
 
     StarField starField = new StarField(_spriteSheet, 200, true);
     addChild(starField);
   }
+
+  void paint(PaintingCanvas canvas) {
+    canvas.drawRect(new Rect.fromLTWH(0.0, 0.0, 320.0, 320.0), new Paint()..color=new Color(0xff000000));
+    super.paint(canvas);
+  }
 }
diff --git a/examples/game/lib/util.dart b/examples/game/lib/util.dart
index fc8f08c..71edbad 100644
--- a/examples/game/lib/util.dart
+++ b/examples/game/lib/util.dart
@@ -80,4 +80,21 @@
       }
     }
   }
+
+  static double pointQuickDist(Point a, Point b) {
+    double dx = a.x - b.x;
+    double dy = a.y - b.y;
+    if (dx < 0.0) dx = -dx;
+    if (dy < 0.0) dy = -dy;
+    if (dx > dy) {
+      return dx + dy/2.0;
+    }
+    else {
+      return dy + dx/2.0;
+    }
+  }
+
+  static double filter (double a, double b, double filterFactor) {
+      return (a * (1-filterFactor)) + b * filterFactor;
+  }
 }