         height (.-clientHeight element)
         svg (setup-svg! selector width height)
         current-state (atom (initial-state svg width height))
-        gesture-manager (hammer-manager svg)]
+        gesture-manager (hammer-manager svg)
+        [animation-observable
+         animation-signaller] (animation-observable-and-signaller)]
     (sync-graph! @current-state)
     (load-graph "miserables.json"
                 (fn [graph]
                   (let [{:keys [nodes links]} graph]
                     (swap-state! current-state assoc :nodes nodes :links links))))
-                (move-camera-on-arrow-keys! current-state)
-                (move-camera-on-pan! gesture-manager current-state)
-                (move-camera-on-swipe! gesture-manager current-state)
-                (zoom-out-on-pinch! gesture-manager current-state)
-                (zoom-in-out-on-interval! current-state)))
-
-
+                (move-camera-on-arrow-keys! current-state animation-observable)
+                (move-camera-on-pan! gesture-manager current-state animation-observable)
+                (move-camera-on-swipe! gesture-manager current-state animation-observable)
+                (zoom-out-on-pinch! gesture-manager current-state animation-observable)
+                (zoom-in-out-on-interval! current-state animation-signaller)))


(def tick-observable (js/Rx.Observable.interval update-interval-ms))

;; Arrow keys

(def key-down-observable (.fromEvent rx-observable js/document "keydown"))

(defn move-camera! [current-state d]
  (update-state! current-state :camera-pos add-vec d))

(defn set-projection! [current-state projection animation-signaller]
  (swap! current-state assoc :projection projection)
  (sync-graph! @current-state :animation-signaller animation-signaller))

;; Layout

