 
     _jump: (ending = no) ->
       if ending
-        return unless @_jumpTimer?
+        return unless @_jumpTimer.running
         @acceleration.y = @gravity.y
         @physics.drag.x = @_originalDrag.x
-        clearTimeout @_jumpTimer
-        @_jumpTimer = null
-        @debug 'jump:end'
-
-        @nextState = 'falling'
+        @debug 'jump:end', @_jumpTimer.ms,
+          position: @physics.position.toString()
+        @_jumpTimer.stop()
         return
 
-      # Faster the run, higher the jump.
-      kVelocity = 4 * (1 + Math.abs @velocity.x / @maxVelocity.x)
-      kDuration = kVelocity * (@jumpTimeRange.max - @jumpTimeRange.min)
-      duration = parseInt (@jumpTimeRange.min / 1000) * kDuration
-      @_jumpTimer = setTimeout @_jump.bind(@, yes), duration
-      @debug 'jump:start', duration
+      @_jumpTimer.start()
+      @debug 'jump:start', @physics.position.toString()
 
       @nextAction = 'jump'
       @nextState = 'rising'
-      @acceleration.set 0, -3000
+      # Faster the run, higher the base jump, up to 33% improvement.
+      kVelocity = (3 + Math.abs @velocity.x / @maxVelocity.x) / 4
+      @acceleration.set 0, @jumpAcceleration * kVelocity
       @physics.drag.x = 2 * @_originalDrag.x
 
     _run: (direction) ->

      tracking of previous velocity is an extra complexity. The possibility of
      hitting the ceiling during jump is another one.
      ###
      if @cursors.up.isDown and not @isInMidAir() and not @_jumpTimer?
        @_jump()

      else if @cursors.up.isUp and @_jumpTimer? # Cancel early.
        @_jump yes

      # Constrain jump and decay the jump force. Negative is up, positive is down.
      if @cursors.up.isDown and @_jumpTimer?
        @velocity.y = Math.max @velocity.y, -320
        @acceleration.y += (@gravity.y - @acceleration.y) / @_jumpDeacceleration
        @debug 'jump', 'build'

    _xOffset: (direction = @direction) -> direction * 10


      @_yOffset = h / 4
      @physics.setSize (w / 2), (h / 2), @_xOffset(), @_yOffset

      @jumpAcceleration = -4000 # A burst of energy on launch.
      @jumpMaxDuration = 500
      @maxVelocity =
        x: 200 # Run velocity.
        y: 1500 # Escape velocity.

      @_jumpTimer = @sprite.game.time.create() # This also acts like a flag.
      @_justTurned = no # Because velocity won't be 0 when just turning.

    _changeAnimation: (nameOrFrame, interrupt = yes) ->
