   (let [ctx (.getContext (by-id "canvas") "2d")
         events (chan (sliding-buffer 10) control-stream)]
     (event-loop! state events)
-    (listen! js/window   "resize"  #(resize! ctx))
+    (listen! js/window   "resize"  #(resize! state ctx))
     (listen! js/document "keydown" #(put! events (.-keyCode %)))
-    (resize! ctx)
-    (animate! ctx)))
+    (resize! state ctx)
+    (animate-loop! state ctx)))
 
 (set! (.-onload js/window) main)

;;-----------------------------------------------------------------------------

(defn draw-phase!
  [ctx]
  (.clearRect ctx 0 0 SCALE-W SCALE-H)
  (doseq [o (vals @state)]
    (when (satisfies? IDrawable o)
      (draw! o ctx))))

(defn move-phase!
  []
  (swap! state #(assoc % :ball (move (:ball %)))))

(defn animate!
  [ctx]
  (move-phase!)
  (draw-phase! ctx)
  (.requestAnimationFrame js/window #(animate! ctx)))

;;-----------------------------------------------------------------------------
;; Control
;;-----------------------------------------------------------------------------

(defn resize!
  [ctx]
  (let [w (- (.-innerWidth js/window) 40)
        h (- (int (/ (* w 9) 16)) 40)]
    (-> (by-id "canvas")
        (attr! "width" w)
        (attr! "height" h))
    (.scale ctx (/ w SCALE-W) (/ h SCALE-H))
    (draw-phase! ctx)))

(defn- event-loop!
  [state ch]

(def state
  ;; Order matters
  (atom {:current-paddle :paddle-1
         ;; use div behind canvas
         :background (Rect. 0 0 SCALE-W SCALE-H "black")
          ;; use div behind canvas
         :border     (Frame. 0 0 SCALE-W SCALE-H "#333" 2)
         :paddle-1   (Paddle. 20 (- SCALE-H 120) 20 100 10 "dodgerblue")
         :paddle-2   (Paddle. (- SCALE-W 40) 20 20 100 10 "peru")
         :ball       (Ball. 400 225 13 3 2 "lime")}))
