diff --git a/pkg/drawing_crdt_bg.wasm b/pkg/drawing_crdt_bg.wasm
index c0180fe87e4a7c9d29c9c3e05bd162b246d184fc..677c51d3bd3d9499322d80ce67e2a411d9f817d4 100644
Binary files a/pkg/drawing_crdt_bg.wasm and b/pkg/drawing_crdt_bg.wasm differ
diff --git a/src/crdt.rs b/src/crdt.rs
index a88234b3811f85d0554440fdf3b063c56a2e3a30..455ab0b07cc493a535a440101d7ebd64429cf52e 100644
--- a/src/crdt.rs
+++ b/src/crdt.rs
@@ -8,14 +8,16 @@ use std::collections::hash_map::Entry::{Occupied, Vacant};
 use std::collections::HashMap;
 use std::convert::TryInto;
 use std::fmt;
-use std::fmt::Debug;
 use std::hash::{Hash, Hasher};
 use std::ops::{Deref, DerefMut};
 use twox_hash::XxHash64 as XXHasher;
 use uuid::Uuid;
 use vec_map::VecMap;
 
-#[derive(Debug)]
+#[cfg(debug_assertions)]
+use std::fmt::Debug;
+
+#[cfg_attr(debug_assertions, derive(Debug))]
 struct Dirty<T> {
     value: T,
     dirty_events: bool,
@@ -67,7 +69,8 @@ impl<T> Dirty<T> {
     }
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone)]
+#[cfg_attr(debug_assertions, derive(Debug))]
 pub struct Point {
     x: i32,
     y: i32,
@@ -160,7 +163,14 @@ impl Point {
     }
 }
 
-pub trait IntervalBound: Debug + Copy + Clone + Default + Ord + Hash {
+#[cfg(debug_assertions)]
+pub trait IntervalBound: Copy + Clone + Default + Ord + Hash + Debug {
+    fn up(self) -> Self;
+    fn down(self) -> Self;
+    fn sub(self, nom: u8, denom: u8) -> Self;
+}
+#[cfg(not(debug_assertions))]
+pub trait IntervalBound: Copy + Clone + Default + Ord + Hash {
     fn up(self) -> Self;
     fn down(self) -> Self;
     fn sub(self, nom: u8, denom: u8) -> Self;
@@ -195,7 +205,8 @@ impl IntervalBound for usize {
     }
 }
 
-#[derive(Copy, Clone, PartialEq, Hash, Debug)]
+#[derive(Copy, Clone, PartialEq, Hash)]
+#[cfg_attr(debug_assertions, derive(Debug))]
 pub struct Interval<T>
 where
     T: IntervalBound,
@@ -230,7 +241,8 @@ where
     }
 }
 
-#[derive(Clone, Hash, Debug)]
+#[derive(Clone, Hash)]
+#[cfg_attr(debug_assertions, derive(Debug))]
 pub struct IntervalUnion<T>(Vec<Interval<T>>)
 where
     T: IntervalBound;
@@ -341,8 +353,10 @@ where
 
         let mut intervals: Vec<Interval<T>> = Vec::new();
 
-        while si < self.0.len() && oi < other.0.len() {
-            let interval = if self.0[si].from < other.0[oi].from {
+        while si < self.0.len() || oi < other.0.len() {
+            let interval = if (oi >= other.0.len())
+                || ((si < self.0.len()) && (self.0[si].from < other.0[oi].from))
+            {
                 let interval = self.0[si];
                 si += 1;
                 interval
@@ -364,25 +378,6 @@ where
             };
         }
 
-        let (interval, tail) = if si < self.0.len() {
-            (self.0[si], &self.0[si + 1..])
-        } else {
-            (other.0[oi], &other.0[oi + 1..])
-        };
-
-        match intervals.last_mut() {
-            Some(top) => {
-                if interval.from <= top.to.up() {
-                    top.to = cmp::max(top.to, interval.to)
-                } else {
-                    intervals.push(interval)
-                }
-            }
-            None => intervals.push(interval),
-        };
-
-        intervals.extend_from_slice(tail);
-
         let changed = intervals != self.0;
 
         self.0 = intervals;
@@ -452,7 +447,7 @@ where
     }
 }
 
-#[derive(Debug)]
+#[cfg_attr(debug_assertions, derive(Debug))]
 struct Stroke {
     index: usize,
     points: Dirty<VecMap<Point>>,
@@ -469,7 +464,7 @@ impl Stroke {
     }
 }
 
-#[derive(Debug)]
+#[cfg_attr(debug_assertions, derive(Debug))]
 pub struct StrokeDelta {
     points: HashMap<usize, Point>,
     intervals: IntervalUnion<NotNan<f32>>,
@@ -531,7 +526,7 @@ impl StrokeDelta {
     }
 }
 
-#[derive(Debug)]
+#[cfg_attr(debug_assertions, derive(Debug))]
 struct User {
     strokes: Vec<Stroke>,
 }
@@ -550,7 +545,8 @@ impl User {
     }
 }
 
-#[derive(Clone, Hash, Eq, PartialEq, Debug)]
+#[derive(Clone, Hash, Eq, PartialEq)]
+#[cfg_attr(debug_assertions, derive(Debug))]
 pub struct StrokeID(u128, usize);
 
 impl Serialize for StrokeID {
@@ -610,7 +606,8 @@ impl StrokeID {
     }
 }
 
-#[derive(Clone, PartialEq, Eq, Debug)]
+#[derive(Clone, PartialEq, Eq)]
+#[cfg_attr(debug_assertions, derive(Debug))]
 pub struct IntervalUnionState<T>
 where
     T: IntervalBound,
@@ -811,7 +808,7 @@ where
     }
 }
 
-#[derive(Debug)]
+#[cfg_attr(debug_assertions, derive(Debug))]
 pub struct DeltaVec(HashMap<StrokeID, StrokeDelta>);
 
 impl Serialize for DeltaVec {
@@ -952,7 +949,7 @@ impl<'de> Deserialize<'de> for DeltaVec {
     }
 }
 
-#[derive(Debug)]
+#[cfg_attr(debug_assertions, derive(Debug))]
 pub struct StateVec {
     strokes: HashMap<u128, IntervalUnion<usize>>,
     intervals: HashMap<StrokeID, IntervalUnionState<NotNan<f32>>>,