 
    })
 
+(defn resolve-tween
+  "Resolve the tween to a function if it's a name."
+  [tween]
+  (cond-> tween
+    (keyword? tween) tweens))
+
 (defn animate!
-  [{:keys [a b duration tween]
-    :or {tween (:swing tweens)}} callback]
-  (let [c (chan)
+  "Pass given animation values to the given callback.
+   Returns a channel that closes when done."
+  [{:keys [a b duration tween] :or {tween :swing} :as opts} callback]
+  (let [tween (resolve-tween tween)
+        c (chan)
         dv (- b a)]
     (tap tick-tap c)
     (go-loop [t 0]

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

(def initial-state
  {:slide 0
   :cam {:x 0
         :y 0

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

(def slide-actions
  "Actions to take for each slide."
  [nil
   go-go-slide1!
   ])

(def num-slides
  (count slide-actions))

;; Saved slide states, so we can revisit previous slides.
(defonce slide-states
  (atom []))

(defn save-slide-state!
  "Save the current application state as the current slide's initial state."
  []
  (let [i (:slide @state)]
    (swap! slide-states assoc i @state)))

(def in-transition?
  "True if we are in the middle of a slide's actions (i.e. animation)"
  (atom false))

(defn next-slide!
  "Go to next slide if we can."
  []
  (when-not @in-transition?
    (when-let [action (get slide-actions (inc (:slide @state)))]
