     (.scale ctx (/ w SCALE-W) (/ h SCALE-H))
     (draw-phase! ctx)))
 
+(defn- event-loop!
+  [state ch]
+  (go-loop []
+    (when-let [event (<! ch)]
+      (cond
+        (contains? #{:up :down} event)
+        (let [id (:current-paddle @state)
+              paddle (id @state)]
+          (swap! state assoc id (control! paddle event)))
+
+        (contains? #{:paddle-1 :paddle-2} event)
+        (swap! state assoc :current-paddle event)
+
+        :else
+        (println "Unhandled event:" event))
+      (recur))))
+
+(def control-stream
+  (comp (map #(or (get KEYBOARD %) :unknown))
+        (filter #(not= % :unknown))))
+
 (defn- main
   []
   (println "Welcome to the Canvas Scratch App.")
-  (listen! js/window "resize" resize!)
-  (resize! nil)
-  (animate!))
+  (let [ctx (.getContext (by-id "canvas") "2d")
+        events (chan 1 control-stream)]
+    (event-loop! state events)
+    (listen! js/window "resize" (partial resize! ctx))
+    (listen! js/document "keydown" #(put! events (.-keyCode %)))
+    (resize! ctx nil)
+    (animate! ctx)))
 
 (set! (.-onload js/window) main)

  (swap! state #(assoc % :ball (move (:ball %)))))

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

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

(defn resize!
  [e]
  (let [w (- (.-innerWidth js/window) 40)
        h (- (int (/ (* w 9) 16)) 40)]
    (-> (by-id "canvas")

(defn listen!
  [el type fn]
  (.addEventListener el type fn false))

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

