   (.send ws (encode-msg msg))
   (swap! tal-state assoc :last-send-time (js/Date.)))
 
+(defn filter-repeats [messages filter-ops]
+  (->> (reduce (fn [acc msg]
+                 (if (contains? filter-ops (:op msg))
+                   (if (contains? (:seen-filtered-ops acc) (:op msg))
+                     acc
+                     (assoc acc
+                            :results (conj (:results acc) msg)
+                            :seen-filtered-ops (conj (:seen-filtered-ops acc) (:op msg))))
+                   (assoc acc :results (conj (:results acc) msg))))
+               {:results '() ; list for inserting at beginning
+                :seen-filtered-ops #{}}
+               (rseq messages))
+    :results
+    vec))
+
 (defn consume-send-queue [tal-state timer-atom]
   (let [send-queue (:send-queue @tal-state)]
     (when-let [ws (:ws @tal-state)]

                          (swap! (:recv-queue @tal-state) (fn [q] (apply conj q (decode-msg (.-data %))))))
                       on-reconnect)]
    (swap! tal-state assoc :ws w)
    (start-send-queue tal-state 100)
    (.open w)))

(defn setup-ws [url-parts tal-state & {:keys [on-open on-close on-error on-reconnect reconnecting?]

        (js/clearTimeout timer-id))
      (let [messages (pop-all send-queue)]
        (when (seq messages)
          (send-msg tal-state ws (filter-repeats messages #{:frontend/mouse-position
                                                            :tal/ping})))))))

(defn start-send-queue [tal-state delay-ms]
  (let [send-queue (:send-queue @tal-state)
