diff --git a/common/src/app/common/geom/matrix.cljc b/common/src/app/common/geom/matrix.cljc
index ac7443f2d..0158fe7df 100644
--- a/common/src/app/common/geom/matrix.cljc
+++ b/common/src/app/common/geom/matrix.cljc
@@ -9,6 +9,7 @@
    #?(:cljs [cljs.pprint :as pp]
       :clj  [clojure.pprint :as pp])
    [app.common.data :as d]
+   [app.common.data.macros :as dm]
    [app.common.geom.point :as gpt]
    [app.common.math :as mth]
    [app.common.spec :as us]
@@ -123,6 +124,35 @@
   ([m1 m2 & others]
    (reduce multiply (multiply m1 m2) others)))
 
+(defn multiply!
+  [^Matrix m1 ^Matrix m2]
+  (let [m1a (.-a m1)
+        m1b (.-b m1)
+        m1c (.-c m1)
+        m1d (.-d m1)
+        m1e (.-e m1)
+        m1f (.-f m1)
+        m2a (.-a m2)
+        m2b (.-b m2)
+        m2c (.-c m2)
+        m2d (.-d m2)
+        m2e (.-e m2)
+        m2f (.-f m2)]
+    #?@(:cljs [(set! (.-a m1) (+ (* m1a m2a) (* m1c m2b)))
+               (set! (.-b m1) (+ (* m1b m2a) (* m1d m2b)))
+               (set! (.-c m1) (+ (* m1a m2c) (* m1c m2d)))
+               (set! (.-d m1) (+ (* m1b m2c) (* m1d m2d)))
+               (set! (.-e m1) (+ (* m1a m2e) (* m1c m2f) m1e))
+               (set! (.-f m1) (+ (* m1b m2e) (* m1d m2f) m1f))
+               m1]
+        :clj  [(Matrix.
+                (+ (* m1a m2a) (* m1c m2b))
+                (+ (* m1b m2a) (* m1d m2b))
+                (+ (* m1a m2c) (* m1c m2d))
+                (+ (* m1b m2c) (* m1d m2d))
+                (+ (* m1a m2e) (* m1c m2f) m1e)
+                (+ (* m1b m2e) (* m1d m2f) m1f))])))
+
 (defn add-translate
   "Given two TRANSLATE matrixes (only e and f have significative
   values), combine them. Quicker than multiplying them, for this
@@ -147,26 +177,31 @@
   (= v base))
 
 (defn translate-matrix
-  ([{x :x y :y :as pt}]
+  ([pt]
    (assert (gpt/point? pt))
-   (Matrix. 1 0 0 1 x y))
+   (Matrix. 1 0 0 1
+            (dm/get-prop pt :x)
+            (dm/get-prop pt :y)))
 
   ([x y]
-   (translate-matrix (gpt/point x y))))
+   (Matrix. 1 0 0 1 x y)))
 
 (defn scale-matrix
   ([pt center]
-   (multiply (translate-matrix center)
-             (scale-matrix pt)
-             (translate-matrix (gpt/negate center))))
-  ([{x :x y :y :as pt}]
+   (-> (matrix)
+       (multiply! (translate-matrix center))
+       (multiply! (scale-matrix pt))
+       (multiply! (translate-matrix (gpt/negate center)))))
+  ([pt]
    (assert (gpt/point? pt))
-   (Matrix. x 0 0 y 0 0)))
+   (Matrix. (dm/get-prop pt :x) 0 0 (dm/get-prop pt :y) 0 0)))
 
 (defn rotate-matrix
-  ([angle point] (multiply (translate-matrix point)
-                           (rotate-matrix angle)
-                           (translate-matrix (gpt/negate point))))
+  ([angle point]
+   (-> (matrix)
+       (multiply! (translate-matrix point))
+       (multiply! (rotate-matrix angle))
+       (multiply! (translate-matrix (gpt/negate point)))))
   ([angle]
    (let [a (mth/radians angle)]
      (Matrix. (mth/cos a)
@@ -200,11 +235,23 @@
   ([m scale center]
    (multiply m (scale-matrix scale center))))
 
+(defn scale!
+  "Apply scale transformation to the matrix."
+  ([m scale]
+   (multiply! m (scale-matrix scale)))
+  ([m scale center]
+   (multiply! m (scale-matrix scale center))))
+
 (defn translate
   "Apply translate transformation to the matrix."
   [m pt]
   (multiply m (translate-matrix pt)))
 
+(defn translate!
+  "Apply translate transformation to the matrix."
+  [m pt]
+  (multiply! m (translate-matrix pt)))
+
 (defn skew
   "Apply translate transformation to the matrix."
   ([m angle-x angle-y]
diff --git a/common/src/app/common/geom/point.cljc b/common/src/app/common/geom/point.cljc
index 5421af3ce..e15f0fff0 100644
--- a/common/src/app/common/geom/point.cljc
+++ b/common/src/app/common/geom/point.cljc
@@ -323,8 +323,9 @@
             (/ (dm/get-prop p1 :y) p-length))))
 
 (defn perpendicular
-  [{:keys [x y]}]
-  (Point. (- y) x))
+  [pt]
+  (Point. (- (dm/get-prop pt :y))
+          (dm/get-prop pt :x)))
 
 (defn project
   "V1 perpendicular projection on vector V2"
diff --git a/common/src/app/common/geom/shapes/constraints.cljc b/common/src/app/common/geom/shapes/constraints.cljc
index 35ddd66a8..6275e673f 100644
--- a/common/src/app/common/geom/shapes/constraints.cljc
+++ b/common/src/app/common/geom/shapes/constraints.cljc
@@ -282,7 +282,7 @@
 (defn calc-child-modifiers
   [parent child modifiers ignore-constraints child-bounds parent-bounds transformed-parent-bounds]
 
-  (let [modifiers (ctm/select-child-modifiers modifiers)
+  (let [modifiers (ctm/select-child modifiers)
 
         constraints-h
         (if-not ignore-constraints
@@ -299,7 +299,7 @@
 
       (let [transformed-parent-bounds @transformed-parent-bounds
 
-            modifiers (ctm/select-child-modifiers modifiers)
+            modifiers (ctm/select-child modifiers)
             transformed-child-bounds (gtr/transform-bounds child-bounds modifiers)
             modifiers (normalize-modifiers constraints-h constraints-v
                                            modifiers parent
diff --git a/common/src/app/common/geom/shapes/flex_layout/modifiers.cljc b/common/src/app/common/geom/shapes/flex_layout/modifiers.cljc
index 8ad936478..481d27390 100644
--- a/common/src/app/common/geom/shapes/flex_layout/modifiers.cljc
+++ b/common/src/app/common/geom/shapes/flex_layout/modifiers.cljc
@@ -34,7 +34,7 @@
         resize-origin (gpo/origin transformed-child-bounds)]
 
     (-> modifiers
-        (ctm/select-child-modifiers)
+        (ctm/select-child)
         (ctm/resize
          resize-vector
          resize-origin
diff --git a/common/src/app/common/geom/shapes/modifiers.cljc b/common/src/app/common/geom/shapes/modifiers.cljc
index d0588cfa9..0677a4293 100644
--- a/common/src/app/common/geom/shapes/modifiers.cljc
+++ b/common/src/app/common/geom/shapes/modifiers.cljc
@@ -107,7 +107,7 @@
 
       ;; Check the constraints, then resize
       (let [parent-id (:id parent)
-            parent-bounds (gtr/transform-bounds @(get bounds parent-id) (ctm/select-parent-modifiers modifiers))]
+            parent-bounds (gtr/transform-bounds @(get bounds parent-id) (ctm/select-parent modifiers))]
         (loop [modif-tree modif-tree
                children (seq children)]
           (if (empty? children)
diff --git a/common/src/app/common/geom/shapes/transforms.cljc b/common/src/app/common/geom/shapes/transforms.cljc
index 38a4ee7e6..4383b28e8 100644
--- a/common/src/app/common/geom/shapes/transforms.cljc
+++ b/common/src/app/common/geom/shapes/transforms.cljc
@@ -483,9 +483,9 @@
 
   ([points center modifiers]
    (let [transform (ctm/modifiers->transform modifiers)]
-    (cond-> points
-      (some? transform)
-      (gco/transform-points center transform)))))
+     (cond-> points
+       (some? transform)
+       (gco/transform-points center transform)))))
 
 (defn transform-selrect
   [selrect modifiers]
diff --git a/common/src/app/common/types/modifiers.cljc b/common/src/app/common/types/modifiers.cljc
index 608d78ab1..b0c882700 100644
--- a/common/src/app/common/types/modifiers.cljc
+++ b/common/src/app/common/types/modifiers.cljc
@@ -7,7 +7,6 @@
 (ns app.common.types.modifiers
   (:refer-clojure :exclude [empty empty?])
   (:require
-   [app.common.perf :as perf]
    [app.common.data :as d]
    [app.common.data.macros :as dm]
    [app.common.geom.matrix :as gmt]
@@ -40,44 +39,123 @@
 ;;     * rotation
 ;;     * change-properties
 
+(defrecord Modifiers
+    [geometry-parent
+     geometry-child
+     structure-parent
+     structure-child])
+
+(defrecord GeometricOperation
+    [type
+     vector
+     origin
+     transform
+     transform-inverse
+     rotation
+     center])
+
+(defrecord StructureOperation
+    [type
+     property
+     value
+     index])
+
+;; Record constructors
+
+(defn move-op
+  [vector]
+  (GeometricOperation. :move vector nil nil nil nil nil))
+
+(defn resize-op
+  ([vector origin]
+   (GeometricOperation. :resize vector origin nil nil nil nil))
+  ([vector origin transform transform-inverse]
+   (GeometricOperation. :resize vector origin transform transform-inverse nil nil)))
+
+(defn rotation-geom-op
+  [center angle]
+  (GeometricOperation. :rotation nil nil nil nil angle center))
+
+(defn rotation-struct-op
+  [angle]
+  (StructureOperation. :rotation nil angle nil))
+
+(defn remove-children-op
+  [shapes]
+  (StructureOperation. :remove-children nil shapes nil))
+
+(defn add-children-op
+  [shapes index]
+  (StructureOperation. :add-children nil shapes index))
+
+(defn reflow-op
+  []
+  (StructureOperation. :reflow nil nil nil))
+
+(defn scale-content-op
+  [value]
+  (StructureOperation. :scale-content nil value nil))
+
+(defn change-property-op
+  [property value]
+  (StructureOperation. :change-property property value nil))
+
+
 ;; Private aux functions
 
-(def conjv (fnil conj []))
+(defn- move-vec?
+  [vector]
+  (or (not (mth/almost-zero? (dm/get-prop vector :x)))
+      (not (mth/almost-zero? (dm/get-prop vector :y)))))
 
-(defn- move-vec? [vector]
-  (or (not (mth/almost-zero? (:x vector)))
-      (not (mth/almost-zero? (:y vector)))))
-
-(defn- resize-vec? [vector]
-  (or (not (mth/almost-zero? (- (:x vector) 1)))
-      (not (mth/almost-zero? (- (:y vector) 1)))))
+(defn- resize-vec?
+  [vector]
+  (or (not (mth/almost-zero? (- (dm/get-prop vector :x) 1)))
+      (not (mth/almost-zero? (- (dm/get-prop vector :y) 1)))))
 
 (defn- mergeable-move?
   [op1 op2]
-  (and (= :move (:type op1))
-       (= :move (:type op2))))
+  (let [type-op1 (dm/get-prop op1 :type)
+        type-op2 (dm/get-prop op2 :type)]
+    (and (= :move type-op1) (= :move type-op2))))
 
 (defn- mergeable-resize?
   [op1 op2]
-  (and (= :resize (:type op1))
-       (= :resize (:type op2))
+  (let [type-op1          (dm/get-prop op1 :type)
+        transform-op1     (or (dm/get-prop op1 :transform) (gmt/matrix))
+        transform-inv-op1 (or (dm/get-prop op1 :transform-inverse) (gmt/matrix))
+        origin-op1        (dm/get-prop op1 :origin)
 
-       ;; Same transforms
-       (gmt/close? (or (:transform op1) (gmt/matrix)) (or (:transform op2) (gmt/matrix)))
-       (gmt/close? (or (:transform-inverse op1) (gmt/matrix)) (or (:transform-inverse op2) (gmt/matrix)))
+        type-op2          (dm/get-prop op2 :type)
+        transform-op2     (or (dm/get-prop op2 :transform) (gmt/matrix))
+        transform-inv-op2 (or (dm/get-prop op2 :transform-inverse) (gmt/matrix))
+        origin-op2        (dm/get-prop op2 :origin)]
+    (and (= :resize type-op1) (= :resize type-op2)
 
-       ;; Same origin
-       (gpt/close? (:origin op1) (:origin op2))))
+         ;; Same origin
+         (gpt/close? origin-op1 origin-op2)
+
+         ;; Same transforms
+         (gmt/close? transform-op1 transform-op2)
+         (gmt/close? transform-inv-op1 transform-inv-op2))))
 
 (defn- merge-move
   [op1 op2]
-  {:type :move
-   :vector (gpt/add (:vector op1) (:vector op2))})
+  (let [vector-op1 (dm/get-prop op1 :vector)
+        vector-op2 (dm/get-prop op2 :vector)]
+    (move-op (gpt/add vector-op1 vector-op2))))
 
 (defn- merge-resize
   [op1 op2]
-  (let [vector (gpt/point (* (-> op1 :vector :x) (-> op2 :vector :x))
-                          (* (-> op1 :vector :y) (-> op2 :vector :y)))]
+  (let [op1-vector (dm/get-prop op1 :vector)
+        op1-x (dm/get-prop op1-vector :x)
+        op1-y (dm/get-prop op1-vector :y)
+
+        op2-vector (dm/get-prop op2 :vector)
+        op2-x (dm/get-prop op2-vector :x)
+        op2-y (dm/get-prop op2-vector :y)
+
+        vector (gpt/point (* op1-x op2-x) (* op1-y op2-y))]
     (assoc op1 :vector vector)))
 
 (defn- maybe-add-move
@@ -89,7 +167,7 @@
       (if (mergeable-move? head op)
         (let [item (merge-move head op)]
           (cond-> (pop operations)
-            (move-vec? (:vector item))
+            (move-vec? (dm/get-prop item :vector))
             (conj item)))
         (conj operations op)))))
 
@@ -103,21 +181,23 @@
       (if (mergeable-resize? head op)
         (let [item (merge-resize head op)]
           (cond-> (pop operations)
-            (resize-vec? (:vector item))
+            (resize-vec? (dm/get-prop item :vector))
             (conj item)))
         (conj operations op)))))
 
 (defn valid-vector?
-  [{:keys [x y]}]
-  (and (some? x)
-       (some? y)
-       (not (mth/nan? x))
-       (not (mth/nan? y))))
+  [vector]
+  (let [x (dm/get-prop vector :x)
+        y (dm/get-prop vector :y)]
+    (and (some? x)
+         (some? y)
+         (not (mth/nan? x))
+         (not (mth/nan? y)))))
 
 ;; Public builder API
 
 (defn empty []
-  {})
+  (Modifiers. [] [] [] []))
 
 (defn move-parent
   ([modifiers x y]
@@ -125,143 +205,118 @@
 
   ([modifiers vector]
    (assert (valid-vector? vector) (dm/str "Invalid move vector: " (:x vector) "," (:y vector)))
-   (cond-> modifiers
+   (cond-> (or modifiers (empty))
      (move-vec? vector)
-     (update :geometry-parent maybe-add-move {:type :move :vector vector}))))
+     (update :geometry-parent maybe-add-move (move-op vector)))))
 
 (defn resize-parent
   ([modifiers vector origin]
    (assert (valid-vector? vector) (dm/str "Invalid move vector: " (:x vector) "," (:y vector)))
-   (cond-> modifiers
+   (cond-> (or modifiers (empty))
      (resize-vec? vector)
-     (update :geometry-parent maybe-add-resize {:type :resize
-                                                :vector vector
-                                                :origin origin})))
+     (update :geometry-parent maybe-add-resize (resize-op vector origin))))
 
   ([modifiers vector origin transform transform-inverse]
    (assert (valid-vector? vector) (dm/str "Invalid move vector: " (:x vector) "," (:y vector)))
-   (cond-> modifiers
+   (cond-> (or modifiers (empty))
      (resize-vec? vector)
-     (update :geometry-parent maybe-add-resize {:type :resize
-                                                :vector vector
-                                                :origin origin
-                                                :transform transform
-                                                :transform-inverse transform-inverse}))))
+     (update :geometry-parent maybe-add-resize (resize-op vector origin transform transform-inverse)))))
+
 (defn move
   ([modifiers x y]
    (move modifiers (gpt/point x y)))
 
   ([modifiers vector]
    (assert (valid-vector? vector) (dm/str "Invalid move vector: " (:x vector) "," (:y vector)))
-   (cond-> modifiers
+   (cond-> (or modifiers (empty))
      (move-vec? vector)
-     (update :geometry-child maybe-add-move {:type :move :vector vector}))))
+     (update :geometry-child maybe-add-move (move-op vector)))))
 
 (defn resize
   ([modifiers vector origin]
    (assert (valid-vector? vector) (dm/str "Invalid move vector: " (:x vector) "," (:y vector)))
-   (cond-> modifiers
+   (cond-> (or modifiers (empty))
      (resize-vec? vector)
-     (update :geometry-child maybe-add-resize {:type :resize
-                                               :vector vector
-                                               :origin origin})))
+     (update :geometry-child maybe-add-resize (resize-op vector origin))))
 
   ([modifiers vector origin transform transform-inverse]
    (assert (valid-vector? vector) (dm/str "Invalid move vector: " (:x vector) "," (:y vector)))
-   (cond-> modifiers
+   (cond-> (or modifiers (empty))
      (resize-vec? vector)
-     (update :geometry-child maybe-add-resize {:type :resize
-                                               :vector vector
-                                               :origin origin
-                                               :transform transform
-                                               :transform-inverse transform-inverse}))))
+     (update :geometry-child maybe-add-resize (resize-op vector origin transform transform-inverse)))))
 
 (defn rotation
   [modifiers center angle]
-  (cond-> modifiers
+  (cond-> (or modifiers (empty))
     (not (mth/close? angle 0))
-    (-> (update :structure-child conjv {:type :rotation
-                                        :rotation angle})
-        (update :geometry-child conjv {:type :rotation
-                                       :center center
-                                       :rotation angle}))))
+    (-> (update :structure-child conj (rotation-struct-op angle))
+        (update :geometry-child conj (rotation-geom-op center angle)))))
 
 (defn remove-children
   [modifiers shapes]
-  (cond-> modifiers
+  (cond-> (or modifiers (empty))
     (d/not-empty? shapes)
-    (update :structure-parent conjv {:type :remove-children
-                                     :value shapes})))
+    (update :structure-parent conj (remove-children-op shapes))))
 
 (defn add-children
   [modifiers shapes index]
-  (cond-> modifiers
+  (cond-> (or modifiers (empty))
     (d/not-empty? shapes)
-    (update :structure-parent conjv {:type :add-children
-                                     :value shapes
-                                     :index index})))
+    (update :structure-parent conj (add-children-op shapes index))))
 
 (defn reflow
   [modifiers]
-  (-> modifiers
-      (update :structure-parent conjv {:type :reflow})))
+  (-> (or modifiers (empty))
+      (update :structure-parent conj (reflow-op))))
 
 (defn scale-content
   [modifiers value]
-  (-> modifiers
-      (update :structure-child conjv {:type :scale-content :value value})))
+  (-> (or modifiers (empty))
+      (update :structure-child conj (scale-content-op value))))
 
 (defn change-property
   [modifiers property value]
-  (-> modifiers
-      (update :structure-child conjv {:type :change-property
-                                      :property property
-                                      :value value})))
+  (-> (or modifiers (empty))
+      (update :structure-child conj (change-property-op property value))))
 
 (defn- merge-geometry
-  [geometry other]
+  [operations other]
 
   (cond
-    (c/empty? geometry)
+    (c/empty? operations)
     other
 
     (c/empty? other)
-    geometry
+    operations
 
     :else
-    (loop [result geometry
-           modifiers (seq other)]
-      (if (c/empty? modifiers)
+    (loop [result operations
+           operations (seq other)]
+      (if (c/empty? operations)
         result
-        (let [current (first modifiers)
+        (let [current (first operations)
               result
               (cond
-                (= :move (:type current))
+                (= :move (dm/get-prop current :type))
                 (maybe-add-move result current)
 
-                (= :resize (:type current))
+                (= :resize (dm/get-prop current :type))
                 (maybe-add-resize result current)
 
                 :else
                 (conj result current))]
 
-          (recur result (rest modifiers)))))))
+          (recur result (rest operations)))))))
 
 (defn add-modifiers
   [modifiers new-modifiers]
-
-  (cond-> modifiers
-    (some? (:geometry-child new-modifiers))
-    (update :geometry-child merge-geometry (:geometry-child new-modifiers))
-
-    (some? (:geometry-parent new-modifiers))
-    (update :geometry-parent merge-geometry (:geometry-parent new-modifiers))
-
-    (some? (:structure-parent new-modifiers))
-    (update :structure-parent #(d/concat-vec [] % (:structure-parent new-modifiers)))
-
-    (some? (:structure-child new-modifiers))
-    (update :structure-child #(d/concat-vec [] % (:structure-child new-modifiers)))))
+  (let [modifiers (or modifiers (empty))
+        new-modifiers (or new-modifiers (empty))]
+    (-> modifiers
+        (update :geometry-child merge-geometry (dm/get-prop new-modifiers :geometry-child))
+        (update :geometry-parent merge-geometry (dm/get-prop new-modifiers :geometry-parent))
+        (update :structure-parent #(d/concat-vec [] % (dm/get-prop new-modifiers :structure-parent)))
+        (update :structure-child #(d/concat-vec [] % (dm/get-prop new-modifiers :structure-child))))))
 
 
 ;; These are convenience methods to create single operation modifiers without the builder
@@ -385,27 +440,27 @@
 
 (defn empty?
   [modifiers]
-  (and (c/empty? (:geometry-child modifiers))
-       (c/empty? (:geometry-parent modifiers))
-       (c/empty? (:structure-parent modifiers))
-       (c/empty? (:structure-child modifiers))))
+  (and (c/empty? (dm/get-prop modifiers :geometry-child))
+       (c/empty? (dm/get-prop modifiers :geometry-parent))
+       (c/empty? (dm/get-prop modifiers :structure-parent))
+       (c/empty? (dm/get-prop modifiers :structure-child))))
 
 (defn child-modifiers?
-  [{:keys [geometry-child structure-child]}]
-  (or (d/not-empty? geometry-child)
-      (d/not-empty? structure-child)))
+  [modifiers]
+  (or (d/not-empty? (dm/get-prop modifiers :geometry-child))
+      (d/not-empty? (dm/get-prop modifiers :structure-child))))
 
 (defn only-move?
   "Returns true if there are only move operations"
-  [{:keys [geometry-child geometry-parent]}]
-  (let [move-op? #(= :move (:type %))]
-    (and (every? move-op? geometry-child)
-         (every? move-op? geometry-parent))))
+  [modifiers]
+  (let [move-op? #(= :move (dm/get-prop % :type))]
+    (and (every? move-op? (dm/get-prop modifiers :geometry-child))
+         (every? move-op? (dm/get-prop modifiers :geometry-parent)))))
 
 (defn has-geometry?
-  [{:keys [geometry-parent geometry-child]}]
-  (or (d/not-empty? geometry-parent)
-      (d/not-empty? geometry-child)))
+  [modifiers]
+  (or (d/not-empty? (dm/get-prop modifiers :geometry-parent))
+      (d/not-empty? (dm/get-prop modifiers :geometry-child))))
 
 (defn has-structure?
   [{:keys [structure-parent structure-child]}]
@@ -414,25 +469,25 @@
 
 ;; Extract subsets of modifiers
 
-(defn select-child-modifiers
+(defn select-child
   [modifiers]
-  (select-keys modifiers [:geometry-child :structure-child]))
+  (assoc (or modifiers (empty)) :geometry-parent [] :structure-parent []))
 
-(defn select-child-geometry-modifiers
+(defn select-parent
   [modifiers]
-  (select-keys modifiers [:geometry-child]))
-
-(defn select-parent-modifiers
-  [modifiers]
-  (select-keys modifiers [:geometry-parent :structure-parent]))
+  (assoc (or modifiers (empty)) :geometry-child [] :structure-child []))
 
 (defn select-structure
   [modifiers]
-  (select-keys modifiers [:structure-parent :structure-child]))
+  (assoc (or modifiers (empty)) :geometry-child [] :geometry-parent []))
 
 (defn select-geometry
   [modifiers]
-  (select-keys modifiers [:geometry-parent :geometry-child]))
+  (assoc (or modifiers (empty)) :structure-child [] :structure-parent []))
+
+(defn select-child-geometry-modifiers
+  [modifiers]
+  (-> modifiers select-child select-geometry))
 
 (defn added-children-frames
   "Returns the frames that have an 'add-children' operation"
@@ -456,46 +511,53 @@
 (defn modifiers->transform
   "Given a set of modifiers returns its transformation matrix"
   [modifiers]
+  (let [modifiers (concat (dm/get-prop modifiers :geometry-parent)
+                          (dm/get-prop modifiers :geometry-child))]
 
-  (let [modifiers
-        (if (d/not-empty? (:geometry-parent modifiers))
-          (concat (:geometry-parent modifiers) (:geometry-child modifiers))
-          (:geometry-child modifiers))]
+    (loop [matrix    (gmt/matrix)
+           modifiers (seq modifiers)]
+      (if (c/empty? modifiers)
+        matrix
+        (let [modifier (first modifiers)
+              type   (dm/get-prop modifier :type)
 
-    (when (d/not-empty? modifiers)
-      (loop [matrix (gmt/matrix)
-             modifiers (seq modifiers)]
-        (if (c/empty? modifiers)
-          matrix
-          (let [{:keys [type vector rotation center origin transform transform-inverse] :as modifier} (first modifiers)
-                matrix
-                (case type
-                  :move
-                  (gmt/multiply (gmt/translate-matrix vector) matrix)
+              matrix
+              (case type
+                :move
+                (-> (dm/get-prop modifier :vector)
+                    (gmt/translate-matrix)
+                    (gmt/multiply! matrix))
 
-                  :resize
-                  (let [origin (cond-> origin
-                                 (or (some? transform-inverse)(some? transform))
-                                 (gpt/transform transform-inverse))]
-                    (gmt/multiply
-                     (-> (gmt/matrix)
-                         (cond-> (some? transform)
-                           (gmt/multiply transform))
-                         (gmt/translate origin)
-                         (gmt/scale vector)
-                         (gmt/translate (gpt/negate origin))
-                         (cond-> (some? transform-inverse)
-                           (gmt/multiply transform-inverse)))
-                     matrix))
+                :resize
+                (let [tf     (dm/get-prop modifier :transform)
+                      tfi    (dm/get-prop modifier :transform-inverse)
+                      vector (dm/get-prop modifier :vector)
+                      origin (dm/get-prop modifier :origin)
+                      origin (if ^boolean (some? tfi)
+                               (gpt/transform origin tfi)
+                               origin)]
 
-                  :rotation
-                  (gmt/multiply
+                  (gmt/multiply!
                    (-> (gmt/matrix)
-                       (gmt/translate center)
-                       (gmt/multiply (gmt/rotate-matrix rotation))
-                       (gmt/translate (gpt/negate center)))
-                   matrix))]
-            (recur matrix (rest modifiers))))))))
+                       (cond-> ^boolean (some? tf)
+                         (gmt/multiply! tf))
+                       (gmt/translate! origin)
+                       (gmt/scale! vector)
+                       (gmt/translate! (gpt/negate origin))
+                       (cond-> ^boolean (some? tfi)
+                         (gmt/multiply! tfi)))
+                   matrix))
+
+                :rotation
+                (let [center   (dm/get-prop modifier :center)
+                      rotation (dm/get-prop modifier :rotation)]
+                  (gmt/multiply!
+                   (-> (gmt/matrix)
+                       (gmt/translate! center)
+                       (gmt/multiply! (gmt/rotate-matrix rotation))
+                       (gmt/translate! (gpt/negate center)))
+                   matrix)))]
+          (recur matrix (next modifiers)))))))
 
 (defn apply-structure-modifiers
   "Apply structure changes to a shape"
@@ -519,36 +581,48 @@
             (cond-> shape
               (cph/text-shape? shape)
               (update :content scale-text-content value)))]
+
     (let [remove-children
           (fn [shapes children-to-remove]
             (let [remove? (set children-to-remove)]
               (d/removev remove? shapes)))
 
           apply-modifier
-          (fn [shape {:keys [type property value index rotation]}]
-            (cond-> shape
-              (= type :rotation)
-              (update :rotation #(mod (+ % rotation) 360))
+          (fn [shape operation]
+            (let [type (dm/get-prop operation :type)]
+              (case type
+                :rotation
+                (let [rotation (dm/get-prop operation :value)]
+                  (update shape :rotation #(mod (+ (or % 0) rotation) 360)))
 
-              (and (= type :add-children) (some? index))
-              (update :shapes
-                      (fn [shapes]
-                        (if (vector? shapes)
-                          (cph/insert-at-index shapes index value)
-                          (d/concat-vec shapes value))))
+                :add-children
+                (let [value (dm/get-prop operation :value)
+                      index (dm/get-prop operation :index)]
+                  (if (some? index)
+                    (update shape :shapes
+                            (fn [shapes]
+                              (if (vector? shapes)
+                                (cph/insert-at-index shapes index value)
+                                (d/concat-vec shapes value))))
+                    (update shape :shapes d/concat-vec value)))
 
-              (and (= type :add-children) (nil? index))
-              (update :shapes d/concat-vec value)
+                :remove-children
+                (let [value (dm/get-prop operation :value)]
+                  (update shape :shapes remove-children value))
 
-              (= type :remove-children)
-              (update :shapes remove-children value)
 
-              (= type :scale-content)
-              (apply-scale-content value)
+                :scale-content
+                (let [value (dm/get-prop operation :value)]
+                  (apply-scale-content shape value))
 
-              (= type :change-property)
-              (assoc property value)))]
+                :change-property
+                (let [property (dm/get-prop operation :property)
+                      value (dm/get-prop operation :value)]
+                  (assoc shape property value))
+
+                ;; :default => no change to shape
+                shape)))]
 
       (as-> shape $
-        (reduce apply-modifier $ (:structure-parent modifiers))
-        (reduce apply-modifier $ (:structure-child modifiers))))))
+        (reduce apply-modifier $ (dm/get-prop modifiers :structure-parent))
+        (reduce apply-modifier $ (dm/get-prop modifiers :structure-child))))))
diff --git a/common/test/common_tests/geom_point_test.cljc b/common/test/common_tests/geom_point_test.cljc
index b83d70e7c..c98052315 100644
--- a/common/test/common_tests/geom_point_test.cljc
+++ b/common/test/common_tests/geom_point_test.cljc
@@ -6,6 +6,7 @@
 
 (ns common-tests.geom-point-test
   (:require
+   [app.common.math :as mth]
    [app.common.geom.point :as gpt]
    [clojure.test :as t]))
 
@@ -14,32 +15,32 @@
         p2 (gpt/point 2 3)
         rs (gpt/add p1 p2)]
     (t/is (gpt/point? rs))
-    (t/is (= 3 (:x rs)))
-    (t/is (= 5 (:y rs)))))
+    (t/is (mth/close? 3 (:x rs)))
+    (t/is (mth/close? 5 (:y rs)))))
 
 (t/deftest substract-points
   (let [p1 (gpt/point 1 2)
         p2 (gpt/point 2 3)
         rs (gpt/subtract p1 p2)]
     (t/is (gpt/point? rs))
-    (t/is (= -1 (:x rs)))
-    (t/is (= -1 (:y rs)))))
+    (t/is (mth/close? -1 (:x rs)))
+    (t/is (mth/close? -1 (:y rs)))))
 
 (t/deftest multiply-points
   (let [p1 (gpt/point 1 2)
         p2 (gpt/point 2 3)
         rs (gpt/multiply p1 p2)]
     (t/is (gpt/point? rs))
-    (t/is (= 2 (:x rs)))
-    (t/is (= 6 (:y rs)))))
+    (t/is (mth/close? 2 (:x rs)))
+    (t/is (mth/close? 6 (:y rs)))))
 
 (t/deftest divide-points
   (let [p1 (gpt/point 1 2)
         p2 (gpt/point 2 5)
         rs (gpt/divide p1 p2)]
     (t/is (gpt/point? rs))
-    (t/is (= 0.5 (:x rs)))
-    (t/is (= 0.4 (:y rs)))))
+    (t/is (mth/close? 0.5 (:x rs)))
+    (t/is (mth/close? 0.4 (:y rs)))))
 
 (t/deftest min-point
   (let [p1 (gpt/point 1 2)
@@ -49,19 +50,19 @@
       (t/is (nil? rs)))
 
     (let [rs (gpt/min p1)]
-      (t/is (= rs p1)))
+      (t/is (gpt/close? rs p1)))
 
     (let [rs (gpt/min nil p1)]
-      (t/is (= rs p1)))
+      (t/is (gpt/close? rs p1)))
 
     (let [rs (gpt/min p1 nil)]
-      (t/is (= rs p1)))
+      (t/is (gpt/close? rs p1)))
 
     (let [rs (gpt/min p1 p2)]
-      (t/is (= rs p1)))
+      (t/is (gpt/close? rs p1)))
 
     (let [rs (gpt/min p2 p1)]
-      (t/is (= rs p1)))
+      (t/is (gpt/close? rs p1)))
     ))
 
 (t/deftest max-point
@@ -72,140 +73,140 @@
       (t/is (nil? rs)))
 
     (let [rs (gpt/max p1)]
-      (t/is (= rs p1)))
+      (t/is (gpt/close? rs p1)))
 
     (let [rs (gpt/max nil p1)]
-      (t/is (= rs p1)))
+      (t/is (gpt/close? rs p1)))
 
     (let [rs (gpt/max p1 nil)]
-      (t/is (= rs p1)))
+      (t/is (gpt/close? rs p1)))
 
     (let [rs (gpt/max p1 p2)]
-      (t/is (= rs p2)))
+      (t/is (gpt/close? rs p2)))
 
     (let [rs (gpt/max p2 p1)]
-      (t/is (= rs p2)))
+      (t/is (gpt/close? rs p2)))
     ))
 
 (t/deftest inverse-point
   (let [p1 (gpt/point 1 2)
         rs (gpt/inverse p1)]
     (t/is (gpt/point? rs))
-    (t/is (= 1 (:x rs)))
-    (t/is (= 0.5 (:y rs)))))
+    (t/is (mth/close? 1 (:x rs)))
+    (t/is (mth/close? 0.5 (:y rs)))))
 
 (t/deftest negate-point
   (let [p1 (gpt/point 1 2)
         rs (gpt/negate p1)]
     (t/is (gpt/point? rs))
-    (t/is (= -1 (:x rs)))
-    (t/is (= -2 (:y rs)))))
+    (t/is (mth/close? -1 (:x rs)))
+    (t/is (mth/close? -2 (:y rs)))))
 
 (t/deftest distance-between-two-points
   (let [p1 (gpt/point 1 2)
         p2 (gpt/point 4 6)
         rs (gpt/distance p1 p2)]
     (t/is (number? rs))
-    (t/is (= 5 rs))))
+    (t/is (mth/close? 5 rs))))
 
 (t/deftest distance-vector-between-two-points
   (let [p1 (gpt/point 1 2)
         p2 (gpt/point 2 3)
         rs (gpt/distance-vector p1 p2)]
     (t/is (gpt/point? rs))
-    (t/is (= 1 (:x rs)))
-    (t/is (= 1 (:y rs)))))
+    (t/is (mth/close? 1 (:x rs)))
+    (t/is (mth/close? 1 (:y rs)))))
 
 (t/deftest point-length
   (let [p1 (gpt/point 1 10)
         rs (gpt/length p1)]
     (t/is (number? rs))
-    (t/is (= 10.04987562112089 rs))))
+    (t/is (mth/close? 10.04987562112089 rs))))
 
 (t/deftest point-angle-1
   (let [p1 (gpt/point 1 3)
         rs (gpt/angle p1)]
     (t/is (number? rs))
-    (t/is (= 71.56505117707799 rs))))
+    (t/is (mth/close? 71.56505117707799 rs))))
 
 (t/deftest point-angle-2
   (let [p1 (gpt/point 1 3)
         p2 (gpt/point 2 4)
         rs (gpt/angle p1 p2)]
     (t/is (number? rs))
-    (t/is (= -135 rs))))
+    (t/is (mth/close? -135 rs))))
 
 (t/deftest point-angle-with-other
   (let [p1 (gpt/point 1 3)
         p2 (gpt/point 1 5)
         rs (gpt/angle-with-other p1 p2)]
     (t/is (number? rs))
-    (t/is (= 7.125016348901757 rs))))
+    (t/is (mth/close? 7.125016348901757 rs))))
 
 (t/deftest point-angle-sign
   (let [p1 (gpt/point 1 3)
         p2 (gpt/point 1 5)
         rs (gpt/angle-sign p1 p2)]
     (t/is (number? rs))
-    (t/is (= 1 rs)))
+    (t/is (mth/close? 1 rs)))
 
   (let [p1 (gpt/point -11 -3)
         p2 (gpt/point 1 5)
         rs (gpt/angle-sign p1 p2)]
     (t/is (number? rs))
-    (t/is (= -1 rs)))
+    (t/is (mth/close? -1 rs)))
   )
 
 (t/deftest update-angle
   (let [p1 (gpt/point 1 3)
         rs (gpt/update-angle p1 10)]
     (t/is (gpt/point? rs))
-    (t/is (= 3.1142355569111246 (:x rs)))
-    (t/is (= 0.5491237529650835 (:y rs)))))
+    (t/is (mth/close? 3.1142355569111246 (:x rs)))
+    (t/is (mth/close? 0.5491237529650835 (:y rs)))))
 
 
 (t/deftest point-quadrant
   (let [p1 (gpt/point 1 3)
         rs (gpt/quadrant p1)]
     (t/is (number? rs))
-    (t/is (= 1 rs)))
+    (t/is (mth/close? 1 rs)))
 
   (let [p1 (gpt/point 1 -3)
         rs (gpt/quadrant p1)]
     (t/is (number? rs))
-    (t/is (= 4 rs)))
+    (t/is (mth/close? 4 rs)))
 
   (let [p1 (gpt/point -1 3)
         rs (gpt/quadrant p1)]
     (t/is (number? rs))
-    (t/is (= 2 rs)))
+    (t/is (mth/close? 2 rs)))
 
   (let [p1 (gpt/point -1 -3)
         rs (gpt/quadrant p1)]
     (t/is (number? rs))
-    (t/is (= 3 rs)))
+    (t/is (mth/close? 3 rs)))
   )
 
 (t/deftest round-point
   (let [p1 (gpt/point 1.34567 3.34567)
         rs (gpt/round p1)]
     (t/is (gpt/point? rs))
-    (t/is (= 1 (:x rs)))
-    (t/is (= 3 (:y rs))))
+    (t/is (mth/close? 1 (:x rs)))
+    (t/is (mth/close? 3 (:y rs))))
 
   (let [p1 (gpt/point 1.34567 3.34567)
         rs (gpt/round p1 2)]
     (t/is (gpt/point? rs))
-    (t/is (= 1.35 (:x rs)))
-    (t/is (= 3.35 (:y rs))))
+    (t/is (mth/close? 1.35 (:x rs)))
+    (t/is (mth/close? 3.35 (:y rs))))
   )
 
 (t/deftest halft-round-point
   (let [p1 (gpt/point 1.34567 3.34567)
         rs (gpt/half-round p1)]
     (t/is (gpt/point? rs))
-    (t/is (= 1.5 (:x rs)))
-    (t/is (= 3.5 (:y rs)))))
+    (t/is (mth/close? 1.5 (:x rs)))
+    (t/is (mth/close? 3.5 (:y rs)))))
 
 (t/deftest transform-point
   ;;todo
@@ -215,30 +216,30 @@
   (let [p1 (gpt/point 1.5 3)
         rs (gpt/scale p1 2)]
     (t/is (gpt/point? rs))
-    (t/is (= 3 (:x rs)))
-    (t/is (= 6 (:y rs)))))
+    (t/is (mth/close? 3 (:x rs)))
+    (t/is (mth/close? 6 (:y rs)))))
 
 (t/deftest dot-point
   (let [p1 (gpt/point 1.5 3)
         p2 (gpt/point 2 6)
         rs (gpt/dot p1 p2)]
     (t/is (number? rs))
-    (t/is (= 21 rs))))
+    (t/is (mth/close? 21 rs))))
 
 (t/deftest unit-point
   (let [p1 (gpt/point 2 3)
         rs (gpt/unit p1)]
     (t/is (gpt/point? rs))
-    (t/is (= 0.5547001962252291 (:x rs)))
-    (t/is (= 0.8320502943378437 (:y rs)))))
+    (t/is (mth/close? 0.5547001962252291 (:x rs)))
+    (t/is (mth/close? 0.8320502943378437 (:y rs)))))
 
 (t/deftest project-point
   (let [p1 (gpt/point 1 3)
         p2 (gpt/point 1 6)
         rs (gpt/project p1 p2)]
     (t/is (gpt/point? rs))
-    (t/is (= 0.5135135135135135 (:x rs)))
-    (t/is (= 3.081081081081081 (:y rs)))))
+    (t/is (mth/close? 0.5135135135135135 (:x rs)))
+    (t/is (mth/close? 3.081081081081081 (:y rs)))))
 
 (t/deftest center-points
   (let [points [(gpt/point 0.5 0.5)
@@ -246,22 +247,22 @@
                 (gpt/point 20 65.2)
                 (gpt/point 12 -10)]
         rs     (gpt/center-points points)]
-    (t/is (= 7.875 (:x rs)))
-    (t/is (= 13.425 (:y rs)))))
+    (t/is (mth/close? 7.875 (:x rs)))
+    (t/is (mth/close? 13.425 (:y rs)))))
 
 (t/deftest normal-left-point
   (let [p1 (gpt/point 2 3)
         rs (gpt/normal-left p1)]
     (t/is (gpt/point? rs))
-    (t/is (= -0.8320502943378437 (:x rs)))
-    (t/is (= 0.5547001962252291 (:y rs)))))
+    (t/is (mth/close? -0.8320502943378437 (:x rs)))
+    (t/is (mth/close? 0.5547001962252291 (:y rs)))))
 
 (t/deftest normal-right-point
   (let [p1 (gpt/point 2 3)
         rs (gpt/normal-right p1)]
     (t/is (gpt/point? rs))
-    (t/is (= 0.8320502943378437 (:x rs)))
-    (t/is (= -0.5547001962252291 (:y rs)))))
+    (t/is (mth/close? 0.8320502943378437 (:x rs)))
+    (t/is (mth/close? -0.5547001962252291 (:y rs)))))
 
 (t/deftest point-line-distance
   (let [p1 (gpt/point 2 -3)
@@ -269,7 +270,7 @@
         p3 (gpt/point 5 6)
         rs (gpt/point-line-distance p1 p2 p3)]
     (t/is (number? rs))
-    (t/is (= 7.58946638440411 rs))))
+    (t/is (mth/close? 7.58946638440411 rs))))
 
 (t/deftest almost-zero-predicate
   (let [p1 (gpt/point 0.000001 0.0000002)
@@ -282,14 +283,14 @@
         p2 (gpt/point 2 3)
         rs (gpt/lerp p1 p2 2)]
     (t/is (gpt/point? rs))
-    (t/is (= 3 (:x rs)))
-    (t/is (= 4 (:y rs)))))
+    (t/is (mth/close? 3 (:x rs)))
+    (t/is (mth/close? 4 (:y rs)))))
 
 (t/deftest rotate-point
   (let [p1 (gpt/point 1 2)
         p2 (gpt/point 2 3)
         rs (gpt/rotate p1 p2 11)]
     (t/is (gpt/point? rs))
-    (t/is (= 1.2091818119288809 (:x rs)))
-    (t/is (= 1.8275638211757912 (:y rs)))))
+    (t/is (mth/close? 1.2091818119288809 (:x rs)))
+    (t/is (mth/close? 1.8275638211757912 (:y rs)))))
 
diff --git a/common/test/common_tests/geom_shapes_test.cljc b/common/test/common_tests/geom_shapes_test.cljc
index a9d1e2df7..07f971e6f 100644
--- a/common/test/common_tests/geom_shapes_test.cljc
+++ b/common/test/common_tests/geom_shapes_test.cljc
@@ -140,7 +140,7 @@
   (t/testing "Transform shape with rotation modifiers"
     (t/are [type]
         (let [shape-before (create-test-shape type)
-              modifiers (ctm/rotation-modifiers shape-before (gsh/center-shape shape-before) 30 )
+              modifiers (ctm/rotation-modifiers shape-before (gsh/center-shape shape-before) 30)
               shape-before (assoc shape-before :modifiers modifiers)
               shape-after  (gsh/transform-shape shape-before)]
 
diff --git a/frontend/dev/bench.cljs b/frontend/dev/bench.cljs
index 41171bafe..bc1b6cdf8 100644
--- a/frontend/dev/bench.cljs
+++ b/frontend/dev/bench.cljs
@@ -3,8 +3,10 @@
    [app.common.data :as d]
    [app.common.data.macros :as dm]
    [app.common.geom.point :as gpt]
+   [app.common.geom.point :as gpt]
    [app.common.geom.shapes.rect :as gsr]
    [app.common.perf :as perf]
+   [app.common.types.modifiers :as ctm]
    [clojure.spec.alpha :as s]
    [clojure.test.check.generators :as gen]))
 
@@ -24,8 +26,40 @@
    :samples 20
    :name "optimized"))
 
+(def modifiers
+  (-> (ctm/empty)
+      (ctm/move (gpt/point 100 200))
+      (ctm/resize (gpt/point 100 200) (gpt/point 2.0 0.5))
+      (ctm/move (gpt/point -100 -200))
+      (ctm/resize (gpt/point 100 200) (gpt/point 2.0 0.5))
+      (ctm/rotation (gpt/point 0 0) -100)
+      (ctm/resize (gpt/point 100 200) (gpt/point 2.0 0.5))))
+
+(defn bench-modifiers
+  []
+  (perf/benchmark
+   :f #(ctm/modifiers->transform modifiers)
+   :max-iterations 50000
+   :samples 20
+   :name "current")
+
+  #_(perf/benchmark
+   :f #(ctm/modifiers->transform-2 modifiers)
+   :max-iterations 50000
+   :samples 20
+   :name "optimized"))
+
+;; (ctm/modifiers->transform-2 modifiers)
+
+(defn ^:dev/after-load after-load
+  []
+  #_(bench-modifiers))
+
 (defn main
   [& [name]]
   (case name
     "points" (bench-points)
-    (println "available: points")))
+    "modifiers" (bench-modifiers)
+    (println "available: points"))
+  #_(.exit js/process 0))
+
diff --git a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts_html.cljs b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts_html.cljs
index 6798a4a5e..673f47a35 100644
--- a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts_html.cljs
+++ b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts_html.cljs
@@ -47,10 +47,8 @@
 (defn process-shape [modifiers {:keys [id] :as shape}]
   (let [modifier (dm/get-in modifiers [id :modifiers])]
     (-> shape
-        (cond-> (and (some? modifier)
-                     (not (ctm/only-move? modifier)))
+        (cond-> (and (some? modifier) (not (ctm/only-move? modifier)))
           (fix-position modifier))
-
         (cond-> (nil? (:position-data shape))
           (assoc :migrate true))
         strip-position-data)))
@@ -132,6 +130,21 @@
                          :shape shape
                          :grow-type (:grow-type shape)}]))
 
+(defn text-properties-equal?
+  [shape other]
+  (or (identical? shape other)
+      (and
+       ;; Check if both shapes are equivalent removing their geometry data
+       (= (dissoc shape :migrate :points :selrect :height :width :x :y)
+          (dissoc other :migrate :points :selrect :height :width :x :y))
+
+       ;; Check if the position and size is close. If any of these changes the shape has changed
+       ;; and if not there is no geometry relevant change
+       (mth/close? (:x shape) (:x other))
+       (mth/close? (:y shape) (:y other))
+       (mth/close? (:width shape) (:width other))
+       (mth/close? (:height shape) (:height other)))))
+
 (mf/defc viewport-texts-wrapper
   {::mf/wrap-props false
    ::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]}
@@ -149,12 +162,9 @@
                 old-modifiers (ctm/select-geometry (get prev-modifiers id))
                 new-modifiers (ctm/select-geometry (get modifiers id))
 
-                remote? (some? (-> new-shape meta :session-id)) ]
-
+                remote? (some? (-> new-shape meta :session-id))]
             (or (and (not remote?)
-                     (not (identical? old-shape new-shape))
-                     (not= (dissoc old-shape :migrate)
-                           (dissoc new-shape :migrate)))
+                     (not (text-properties-equal? old-shape new-shape)))
 
                 (and (not= new-modifiers old-modifiers)
                      (or (not (ctm/only-move? new-modifiers))
@@ -172,6 +182,7 @@
 
         handle-update-modifier (mf/use-callback update-text-modifier)
         handle-update-shape (mf/use-callback update-text-shape)]
+
     [:*
      (for [{:keys [id] :as shape} changed-texts]
        [:& text-container {:shape shape