         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)))

  (.map (gesture-observable manager svg "pinchmove")
        #(.-scale %)))

(defn zoom-out-on-pinch! [manager current-state]
  (-> (pinch-observable manager (:svg @current-state))
      (.filter #(< % 0.5))
      (.subscribe #(js/alert "TODO: zoom out"))))

;; Temporary animation test function

(defn zoom-in-out-on-interval! [current-state]
  ((fn f [projection]
     (.setTimeout js/window
                  (fn []
                    (set-projection! current-state projection)
                    (f (if (= projection :fisheye) :zoom-out :fisheye)))
                  2000)) :zoom-out))


(defn projectors-for-state [state]
  (if (= (:projection state) :fisheye) fisheye-projectors zoom-out-projectors))

(defn sync-graph! [state & {:keys [animation-signaller]}]
  (let [{:keys [svg nodes links camera-pos view-size]} state
        {:keys [project-displacement
                project-radius
                project-opacity]} (projectors-for-state state)
        transition (fn [selection]
                     (animation-signaller true)
                     (-> selection
                         .transition
                         (.duration 500)
                         (.each "end" #(animation-signaller false))))
        maybe-transition (if animation-signaller transition identity)
        positions (mapv (fn [node]
                          (view-position state
                                         (:pos node)
