           (.each
             (fn [node]
               (this-as elem (add-tap-listener elem #(node-tap-signaller node))))))
-      (-> new-node-selection (.append "text")
+      ; Having too many labels at once hurts performance. Add/remove labels as
+      ; they move in and out of view.
+      (-> node-selection
+          ; Nodes within a distance threshold which do not have labels
+          (.filter
+            (fn [node]
+              (this-as elem (and (< (distance-for-node node) label-visible-threshold)
+                                 (nil? (.querySelector elem ".node-label"))))))
+          (.append "text")
           (.attr "class" "node-label")
-          (.attr "y" ".6em")
-          (.attr "font-family" label-font)
-          (.attr "font-size" (str label-font-size))
+          (.attr "dy" ".6em")
+          (.style "font-family" label-font)
+          (.style "font-size" (str label-font-size))
           (.style "text-anchor" "middle")
-          (.attr "transform" label-y-transform))
+          (.attr "y" label-y))
+      (-> node-selection
+          ; Nodes beyond a distance threshold which do have labels
+          (.filter #(>= (distance-for-node %) label-visible-threshold))
+          (.selectAll ".node-label")
+          (.remove))
       (-> node-selection
           maybe-transition
           (.attr "transform"


(def label-y 42)
(def label-y-transform (str "translate(0," label-y ")"))

(def zoom-out-scale 0.25)



(def update-interval-ms 15)

(def swipe-damping-factor 0.75)
(def min-slide-speed-sq 16)

; A factor of 1 is an ellipse fitting the bounds of the window. Higher factors
