   [& args]
   (.apply (.-log js/console) js/console (clj->js (map clj->js args))))
 
-(defn time-since
-  [time]
-  (let [start (c/to-long time)
-        now (c/to-long (t/now))]
-    (- now start)))
+(defn now []
+  (if-let [now (.-now (.-performance js/window))]
+    (.call now (.-performance js/window))
+    (.now js/Date)))
+
+(defn on-raf
+  [step]
+  (if-let [native (let [vendors ["" "ms" "moz" "webkit" "o"]]
+                    (->> vendors
+                         (map #(aget js/window (str % "requestAnimationFrame")))
+                         (filter identity)
+                         (first)))]
+    (let [frame (chan)]
+      (go-loop []
+               (.call native js/window (fn [time]
+                                         (put! frame time)))
+               (let [time (<! frame)]
+                 (when (step time)
+                   (recur)))))
+    (let [speed-target 16
+          speed-tolerance 1
+          speed-step 0.5]
+
+      (go-loop [last-time (t/now)
+                wait speed-target]
+
+               (let [time (now)]
+
+                 (when (step time)
+
+                   ;; converge wait time on 60fps and recur
+                   (let [speed-last (- time last-time)
+                         speed-new (if (< (- speed-target speed-tolerance)
+                                          speed-last
+                                          (+ speed-target speed-tolerance))
+                                     wait
+                                     ((if (< speed-target speed-last) - +) wait speed-step))]
+
+                     (when (< 0 wait)
+                       (<! (timeout wait)))
+
+                     (recur time
+                            speed-new))))))))
 
 (defn transition
   ([state target] (transition state target 500))

     (let [out (chan (sliding-buffer 1))
           interpolator (interpolate state target)
           ease-fn (ease easing ease-args)
           start (t/now)
           speed-target 16
           speed-tolerance 1
           speed-step 0.5]

       (go-loop [last-time start
                 wait speed-target]

                (let [since (time-since start)
                      done? (> since duration)
                      t (if done?
                          1
                          (ease-fn (/ since duration)))
                      step (interpolator (ease-fn t))]

                  (put! out step)

                  (if done?
                    (close! out)

                    ;; converge wait time on 60fps and recur
                    (let [speed-last (- since last-time)
                          speed-new (if (< (- speed-target speed-tolerance)
                                           speed-last
                                           (+ speed-target speed-tolerance))
                                      wait
                                      ((if (< speed-target speed-last) - +) wait speed-step))]

                      (when (< 0 wait)
                        (<! (timeout wait)))

                      (recur since
                             speed-new)))))
       out)))


;; examples


(ns bardo.core
  (:require-macros [cljs.core.async.macros :refer [go go-loop alt!]])
  (:require [cljs.core.async
             :refer [put! take! <! >! chan timeout sliding-buffer close! alts!]
             :as async]
            [cljs-time.core :as t]
            [cljs-time.coerce :as c]
