   ([s] (filter-events s))
   ([s & ss] (filter-events (merge-events-helper (cons s ss)))))
 
-;;(merge-events [[0 :a] [1 :b]] [[2 :c]])
+;;; Effects
+(defrecord RainEffect
+  [terminal layer-id rain-state]
+  ACmdStream
+  (stream [this]
+    (let [dt      33
+          [vw vh] (size terminal)]
+      (go-loop [rain-state rain-state]
+        (<! (timeout dt))
+        (zat/clear! terminal layer-id)
+        (let [chrs (for [[y line] (map-indexed vector rain-state)
+                         [x cell] (map-indexed vector line)
+                         :when cell]
+                     (rain-cell-char x y cell))]
+          (zat/put-chars!
+             terminal
+             layer-id
+             chrs)
+          (zat/refresh! terminal)
+          (recur (step-rain rain-state vw vh)))))))
+
+(defn make-rain-effect
+  [layer-id vw vh]
+  (let [rain-state (repeat vh (vec (repeat vw nil)))]
+    (fn [terminal]
+      (RainEffect. terminal layer-id rain-state))))
+
+(defrecord TransformEffect
+  [terminal layer-id ch from to duration]
+  ACmdStream
+  (stream [this]
+    (let [path (zutil/line-segment-fast-without-endpoints from to)
+          dt   (* 1000 (/ duration (count path)))]
+      (go-loop [[x y] (first path) xys (next path)]
+        (zat/clear! terminal layer-id)
+        (zat/put-chars! terminal layer-id [(assoc ch :x x :y y)])
+        (zat/refresh! terminal)
+        (<! (timeout dt))
+        (if xys
+          (recur (first xys) (next xys)))))))
+
+(defn make-transform-effect
+  [terminal layer-id ch from to duration]
+    (fn [terminal]
+      (TransformEffect. terminal layer-id ch from to duration)))
+
+(defrecord BlinkEffect
+  [terminal layer-id characters initial intervals]
+  ACmdStream
+  (stream [this]
+    (let [dt 1000]
+      (go-loop [state initial]
+        (zat/clear! terminal layer-id)
+        (when state
+          (zat/put-chars! terminal layer-id characters))
+        (zat/refresh! terminal)
+        (<! (timeout dt))
+        (recur (not state))))))
+
+(defn make-blink-effect
+  ([layer-id characters]
+    (make-blink-effect layer-id characters true))
+  ([layer-id characters initial]
+    (make-blink-effect layer-id characters initial (constantly 1000)))
+  ([layer-id characters initial intervals]
+    (fn [terminal]
+      (BlinkEffect. terminal layer-id characters initial intervals))))
+
+(defn make-blip-effect
+  [layer-id characters duration]
+  (make-blink-effect layer-id characters [duration]))
 
 ;;; Queue cmds in a chan and foreard them to dst-chan on `(refresh)`.
 (defrecord WrappedAnimatedTerminal [terminal cmds dst-chan]

           nil)
         (rain-transition advanced-cell old-cell))))))))

(defprotocol ACmdStream
  (stream [this]))

(defrecord RainEffect
  [terminal layer-id rain-state]
  ACmdStream
  (stream [this]
    (let [dt      33
          [vw vh] (size terminal)]
      (go-loop [rain-state rain-state]
        (<! (timeout dt))
        (zat/clear! terminal layer-id)
        (let [chrs (for [[y line] (map-indexed vector rain-state)
                         [x cell] (map-indexed vector line)
                         :when cell]
                     (rain-cell-char x y cell))]
          (zat/put-chars!
             terminal
             layer-id
             chrs)
          (zat/refresh! terminal)
          (recur (step-rain rain-state vw vh)))))))

(defn make-rain-effect
  [layer-id vw vh]
  (let [rain-state (repeat vh (vec (repeat vw nil)))]
    (fn [terminal]
      (RainEffect. terminal layer-id rain-state))))

(defrecord TransformEffect
  [terminal layer-id ch from to duration]
  ACmdStream
  (stream [this]
    (let [path (zutil/line-segment-fast-without-endpoints from to)
          dt   (* 1000 (/ duration (count path)))]
      (go-loop [[x y] (first path) xys (next path)]
        (zat/clear! terminal layer-id)
        (zat/put-chars! terminal layer-id [(assoc ch :x x :y y)])
        (zat/refresh! terminal)
        (<! (timeout dt))
        (if xys
          (recur (first xys) (next xys)))))))

(defn make-transform-effect
  [terminal layer-id ch from to duration]
    (fn [terminal]
      (TransformEffect. terminal layer-id ch from to duration)))

(defrecord BlinkEffect
  [terminal layer-id characters initial intervals]
  ACmdStream
  (stream [this]
    (let [dt 1000]
      (go-loop [state initial]
        (zat/clear! terminal layer-id)
        (when state
          (zat/put-chars! terminal layer-id characters))
        (zat/refresh! terminal)
        (<! (timeout dt))
        (recur (not state))))))

(defn make-blink-effect
  ([layer-id characters]
    (make-blink-effect layer-id characters true))
  ([layer-id characters initial]
    (make-blink-effect layer-id characters initial (constantly 1000)))
  ([layer-id characters initial intervals]
    (fn [terminal]
      (BlinkEffect. terminal layer-id characters initial intervals))))

(defn make-blip-effect
  [layer-id characters duration]
  (make-blink-effect layer-id characters [duration]))


;; s is a seq of [dt [layer-id [{:c \* :x 1 :y 2 :fg [r g b] :bg [r g b]}...]]]
(defrecord SeqEffect
  [terminal s]

        (when xs
          (recur (first xs) (next xs)))))))

(defn effect-from-events
  [s]
  (fn [terminal]
    (->SeqEffect terminal s)))

(defn delay-events [duration s]
  (cons [duration []] s))
  
(defn merge-events-helper [ss]
  (cond
    (empty? ss) []
