     {
         // TODO.. maybe just redispatch
         log.warning("player error: " + event.value);
+    }
+
+    protected function adjustSeek (trackX :Number) :void
+    {
+        // see if we can seek to a position
+        const dur :Number = _player.getDuration();
+        if (isNaN(dur)) {
+            log.debug("Duration is NaN, not seeking.");
+            return;
+        }
+        if (trackX == _lastKnobX) {
+            return;
+        }
+        _lastKnobX = trackX;
+        var perc :Number = trackX / TRACK_WIDTH;
+        perc = Math.max(0, Math.min(1, perc));
+        log.debug("Seek", "x", trackX, "perc", perc, "pos", (perc * dur));
+        _player.seek(perc * dur);
     }
 
     protected function showHUD (show :Boolean) :void

import flash.display.Shape;
import flash.display.Sprite;

import flash.events.MouseEvent;

import flash.geom.Point;

import com.threerings.util.Log;
import com.threerings.util.ValueEvent;

        _track.y = NATIVE_HEIGHT - PAD - TRACK_HEIGHT - _hud.y;
        g = _track.graphics;
        g.beginFill(0x000000, .7);
        g.drawRect(0, 0, TRACK_WIDTH, TRACK_HEIGHT);
        g.endFill();
        g.lineStyle(2, 0xFFFFFF);
        g.drawRect(0, 0, TRACK_WIDTH, TRACK_HEIGHT);
        // _track is not added to _hud until we know the duration

        const trackMask :Shape = new Shape();
