         speed (:speed player)
         all-frames (-> player :source :frames)
         frames (-> all-frames (next-frames start-at) (frames-at-speed speed))
-        frames-ch (coll->chan frames)
+        stop-playback-ch (chan)
+        frames-ch (pipe (coll->chan frames) stop-playback-ch)
         events-ch (screen-events frames-ch)
         elapsed-time #(* (elapsed-time-since start) speed)
-        timer-ch (coll->chan (repeatedly (fn []
-                                           [0.3 (+ start-at (elapsed-time))])))
-        stop-playback-ch (chan)
+        time-events (repeatedly (fn [] [0.3 (+ start-at (elapsed-time))]))
+        timer-ch (pipe (coll->chan time-events) stop-playback-ch)
         stop-fn (fn []
                   (close! stop-playback-ch)
                   (elapsed-time))]

  for each element. It tries to always stay 'on the schedule' by measuring
  elapsed time and skipping elements if necessary."
  [coll]
  (let [ch (chan (sliding-buffer 1))
        start (js/Date.)]
    (go
      (loop [coll coll

                          (recur))
                        (dispatch player [:finished]))
            stop-playback-ch nil))) ; do nothing, break the loop
      (close! stop-playback-ch)
      (dispatch player [:blink true]))
    (-> player
        (update-screen (screen-state-at all-frames start-at))
