anchor.diff:  import 'dart:async' show Stream, StreamController, StreamSubscription;
   import 'package:pixelcycle2/src/movie.dart' show WIDTH, HEIGHT, ALL, Movie, Frame, Size;
   
  -/// The Player represents the position and speed at which the movie is playing.
  +/// A Player contains the position and speed at which the movie is playing.
  +/// A position is represented as float between 0 up to (and not including) the number of frames in the movie.
  +/// The first frame is shown for positions 0 to 1, the second, from 1 to 2, and so on.
  +/// Time is in seconds since the zero time used by window.performance.now(), which is typically the page load.
  +/// Speeds are in frames per second.
   class Player {
     final Movie movie;
     final StreamController<Player> _onChange = new StreamController<Player>.broadcast();
   
  -  // The time in seconds since the window.performance.now() epoch when the movie started playing, or null if not playing.
  +  // The time when the movie started playing, or null if it's not playing.
     num _startTime = null;
   
  -  // The position in the movie where it started playing, or will play.
  -  // This is a number between 0 (the first frame) and the length of the movie in frames.
  +  // The position where the movie started playing, or will play if it's not playing.
     num _startPosition = 0;
   
     // The speed at which the movie is playing, or will play. May be negative to play backwards.
  
positive.code:   final Player player;
    final StreamSubscription moveSub;
    final touchId;
    num lastTime;
    num lastPos;
  
    PlayDrag.start(this.player, this.moveSub, num pos, {this.touchId}) {
      player.playing = false;
      player.speed = 0;
      lastTime = now();
      lastPos = pos;
    }
  
    void update(num pos) {
      num now = window.performance.now() / 1000.0;
      num deltaPos = pos - lastPos;
      num deltaT = now - lastTime;
      // negate the position because dragging forward causes the movie to play backwards.
      player.drag(-deltaPos, deltaT);
      lastPos = pos;
      lastTime = now;
    }
  
    void finish() {
      num time = now();
      if (time - lastTime > 0.2) {
        player.speed = 0;
      }
      player.playing = true;
      moveSub.cancel();
    }
  }
  
  // The time in seconds since the window.performance.now() Epoch.
  num now() {
    return window.performance.now() / 1000;
  }
  
negative.code:   }
  
    set speed(num newValue) {
      if (playing) {
        num t = now();
        _startPosition = positionAt(t);
        _startTime = t;
      }
      _speed = newValue;
      if (_speed == 0) {
        playing = false;
      }
    }
  
    /// Stops playing and adds the given delta to the position.
    void drag(num deltaPos) {
      if (playing) {
        _stop();
      }
      _startPosition = (_startPosition + deltaPos) % movie.frames.length;
      if (_onChange.hasListener) {
        _onChange.add(this);
      }
  