     framesBeforeGeneration = stepsPerGen
 
     imageNameTemplate = E('upload-name').value
-    @beginWork()
-    @uploadWorker = setInterval (=>
+    @_beginWork()
+    uploadStep = =>
+      @uploadWorker = null
       observer.navigateTo @startChain, @startOffset
       p = index / totalSteps
       observer.modifyView M.hyperbolicInv Tinterp(p)
       drawEverything()
+      
       imageName = formatString imageNameTemplate, [pad(index,4)]
-      uploadToServer imageName, (ajax)->
+      uploadToServer imageName, (ajax)=>
         if ajax.readyState is XMLHttpRequest.DONE and ajax.status is 200
           console.log "Upload success"
+          index +=1
+          framesBeforeGeneration -= 1
+          if framesBeforeGeneration is 0
+            doStep()
+            framesBeforeGeneration = stepsPerGen
+
+          if index <= totalSteps
+            console.log "request next frame"
+            @uploadWorker = flipSetTimeout 1, uploadStep
+          else
+            @_endWork()
         else
-          console.log "Upload failure"
+          console.log "Upload failure, cancel"
           console.log ajax.responseText
-      index +=1
-      framesBeforeGeneration -= 1
-      if framesBeforeGeneration is 0
-        doStep()
-        framesBeforeGeneration = stepsPerGen
-
-      if index > totalSteps
-        clearInterval @uploadWorker
-        @uploadWorker = null
-        @endWork()
-      ), 100
+          @_endWork()
+          
+    uploadStep()
+    
+flipSetTimeout = (t, cb) -> setTimeout cb, t
 
 serverSupportsUpload = -> ((""+window.location).match /:8000\//) and true
 # ============================================  app code ===============

    [canvas.width, canvas.height] = @oldSize
    @oldSize = null
    canvasSizeUpdateBlocked = false

  beginWork: ->
    @_setCanvasSize()
    console.log "Started animation"
    
  endWork: ->
    @_restoreCanvasSize()
    console.log "End animation"
        
  cancelWork: ->
    return unless @uploadWorker
    clearInterval @uploadWorker
    @uploadWorker = null
    @endWork()
    
  animate: (observer, stepsPerGen, generations, callback)->
    return unless @startChain? and @endChain?

  constructor: ->
    @oldSize = null
    @uploadWorker = null
    @busy = false
    @reset()

  assertNotBusy: ->
    if @busy
      throw new Error "Animator is busy"
      
  reset: ->
    @cancelWork() if @busy
    @startChain = null
    @startOffset = null
    @endChain = null
