diff --git a/package-lock.json b/package-lock.json
index e88266b839c919cc811be65632c52d7a667af8ac..6796e5ac07630fa91a9c03e636cb4a8b617aa03e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -517,6 +517,11 @@
         "@types/istanbul-lib-report": "*"
       }
     },
+    "@types/node": {
+      "version": "12.12.7",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.7.tgz",
+      "integrity": "sha512-E6Zn0rffhgd130zbCbAr/JdXfXkoOUFAKNs/rF8qnafSJ8KYaA/j3oz7dcwal+lYjLA7xvdd5J4wdYpCTlP8+w=="
+    },
     "@types/stack-utils": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
@@ -1458,6 +1463,11 @@
       "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
       "dev": true
     },
+    "canvas-renderer": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/canvas-renderer/-/canvas-renderer-2.1.1.tgz",
+      "integrity": "sha512-/V0XetN7s1Mk3NO7x2wxPZYv0pLMQtGAhecuOuKR88beiYCUle1AbCcFZNLu+4NVzi9RVHS0rXtIgzPEaKidLw=="
+    },
     "capture-exit": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz",
@@ -3883,6 +3893,14 @@
       "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
       "dev": true
     },
+    "humanhash": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/humanhash/-/humanhash-1.0.4.tgz",
+      "integrity": "sha512-fxOhEl/Ezv7PobYOTomDmQKWaSC0hk0mzl5et5McPtr+6LRBP7LYoeFLPjKW6xOSGmMNLj50BufrrgX+M5EvEA==",
+      "requires": {
+        "uuid": "^3.3.2"
+      }
+    },
     "iana-hashes": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/iana-hashes/-/iana-hashes-1.1.0.tgz",
@@ -4339,6 +4357,15 @@
         "handlebars": "^4.1.2"
       }
     },
+    "jdenticon": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/jdenticon/-/jdenticon-2.2.0.tgz",
+      "integrity": "sha512-WGqwpjN9pab/Sah9pGnFH5tQc3HF3WbLV/tPVbykvk5nuAkxG/zhzQYWC2owvpnS+/A0HmlSx35rtY8kyN+x7Q==",
+      "requires": {
+        "@types/node": "*",
+        "canvas-renderer": "~2.1.1"
+      }
+    },
     "jest": {
       "version": "24.9.0",
       "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz",
@@ -6134,9 +6161,9 @@
       }
     },
     "react-is": {
-      "version": "16.11.0",
-      "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.11.0.tgz",
-      "integrity": "sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw==",
+      "version": "16.12.0",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz",
+      "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==",
       "dev": true
     },
     "read-pkg": {
diff --git a/package.json b/package.json
index 7767796697e115d85aa50bda71bc22f26bbb6ba2..52c352f7ee82f185f75055355355ae4d5ae3fa6c 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,8 @@
     "d3-shape": "^1.3.5",
     "dotenv": "^8.2.0",
     "express": "^4.17.1",
+    "humanhash": "^1.0.4",
+    "jdenticon": "^2.2.0",
     "liowebrtc": "file:src/liowebrtc",
     "pako": "^1.0.10",
     "signalbuddy": "file:src/signalbuddy",
diff --git a/public/styles.css b/public/styles.css
index 04842b1391d4b38b4a2f47628dbab4ee2335405e..3eb118cac54c599e8a414432ff1738adf6f8e3d0 100644
--- a/public/styles.css
+++ b/public/styles.css
@@ -79,7 +79,7 @@ button.selected {
   min-width: 160px;
   box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
   z-index: 999;
-  padding: 16px;
+  padding: 8px;
 }
 
 .peers a {
@@ -515,3 +515,7 @@ button.selected {
     url("./assets/fonts/martel-v4-latin/martel-v4-latin-regular.svg#Martel")
       format("svg");
 }
+
+.avatar {
+  margin-right: 5px;
+}
diff --git a/src/app.js b/src/app.js
index ae6d6305e2b39f7d13fffac0da058795fd199c32..0665da2af040d7d58733e1a9650c84dd4884bb3a 100644
--- a/src/app.js
+++ b/src/app.js
@@ -9,6 +9,8 @@ import { computeErasureIntervals, combineErasureIntervals } from "./erasure.js"
 import { connect } from "./room.js"
 import * as toolSelection from "./tool-selection.js"
 import recognizeFromPoints, { Shapes } from "./shapes.js"
+import * as humanhash from "humanhash"
+import jdenticon from "jdenticon"
 
 const DEFAULT_ROOM = "imperial"
 
@@ -33,6 +35,8 @@ const getPressureFactor = (pressure) => {
 
 let room = null
 
+const humanHasher = new humanhash()
+
 function eraseEverythingAtPosition(x, y, radius, room) {
   const mousePos = [x, y]
   room.getPaths().forEach((points, pathID) => {
@@ -95,7 +99,7 @@ const onRoomConnect = (room_) => {
     const medium = "/quality-medium.svg"
     const low = "/quality-low.svg"
 
-    const peer = getOrInsertPeerById(id).children[0]
+    const peer = getOrInsertPeerById(id).children[1]
     if (quality < 0.33) {
       if (!peer.src.includes(high)) {
         peer.src = high
@@ -112,18 +116,18 @@ const onRoomConnect = (room_) => {
   })
 
   room.addEventListener("weSyncedWithPeer", ({ detail: id }) => {
-    getOrInsertPeerById(id).children[1].className = "peer-status upload synced"
+    getOrInsertPeerById(id).children[2].className = "peer-status upload synced"
     updateOverallStatusIcon()
   })
 
   room.addEventListener("waitingForSyncStep", ({ detail: id }) => {
-    getOrInsertPeerById(id).children[2].className =
+    getOrInsertPeerById(id).children[3].className =
       "peer-status download negotiating"
     updateOverallStatusIcon()
   })
 
   room.addEventListener("peerSyncedWithUs", ({ detail: id }) => {
-    getOrInsertPeerById(id).children[2].className =
+    getOrInsertPeerById(id).children[3].className =
       "peer-status download synced"
     updateOverallStatusIcon()
   })
@@ -259,13 +263,18 @@ HTML.roomIDElem.addEventListener("keydown", (event) => {
 
 const getOrInsertPeerById = (id) => {
   for (const peerElem of HTML.connectedPeers.children) {
-    const peerId = peerElem.children[3].innerHTML
+    const peerId = peerElem.children[4].id
     if (peerId == id) {
       return peerElem
     }
   }
 
   const peerElem = document.createElement("li")
+
+  const avatarImage = document.createElement("svg")
+  avatarImage.innerHTML = jdenticon.toSvg(id, 50)
+  avatarImage.className = "avatar"
+
   const quality = document.createElement("img")
   quality.src = "/quality-low.svg"
   quality.alt = "Peer quality icon"
@@ -279,8 +288,12 @@ const getOrInsertPeerById = (id) => {
 
   const peerId = document.createElement("div")
   peerId.style.marginLeft = "5px"
-  peerId.innerHTML = id
 
+  peerId.id = id
+
+  peerId.innerHTML = humanHasher.humanize(id, 2)
+
+  peerElem.appendChild(avatarImage)
   peerElem.appendChild(quality)
   peerElem.appendChild(ourStatus)
   peerElem.appendChild(theirStatus)
@@ -294,8 +307,8 @@ const getOrInsertPeerById = (id) => {
 const updateOverallStatusIcon = () => {
   for (const peerElem of HTML.connectedPeers.children) {
     if (
-      !peerElem.children[1].classList.contains("synced") ||
-      !peerElem.children[2].classList.contains("synced")
+      !peerElem.children[2].classList.contains("synced") ||
+      !peerElem.children[3].classList.contains("synced")
     ) {
       HTML.overallStatusIcon.className = "synchronising"
       HTML.overallStatusIconImage.src = "synchronising.svg"
diff --git a/src/room.js b/src/room.js
index af3bfbf9697cfc899b7bb1b7ae31583e12606bd0..d1ac2ff4d8f227a186cdbd3e4360c8f147d20f48 100644
--- a/src/room.js
+++ b/src/room.js
@@ -1,6 +1,7 @@
 import uuidv4 from "uuid/v4"
 import yArray from "y-array"
 import yMap from "y-map"
+import yUnion, { Union } from "./y-union.js"
 import yMemory from "y-memory"
 import Y from "yjs"
 
@@ -8,15 +9,13 @@ import yP2PMesh from "./y-p2p-mesh.js"
 import WebRTCConnection from "./connection/WebRTC.js"
 
 yMemory(Y)
+Y.Struct.Union = Union
+yUnion(Y)
 yMap(Y)
 yArray(Y)
 yP2PMesh(Y)
 
-import {
-  combineErasureIntervals,
-  spreadErasureIntervals,
-  flattenErasureIntervals,
-} from "./erasure.js"
+import { spreadErasureIntervals, flattenErasureIntervals } from "./erasure.js"
 
 class Room extends EventTarget {
   constructor(name) {
@@ -33,7 +32,10 @@ class Room extends EventTarget {
 
   addPath([x, y, w, colour]) {
     const id = uuidv4()
+
     this.shared.strokePoints.set(id, Y.Array).push([[x, y, w, colour]])
+    this.shared.eraseIntervals.set(id, Y.Union)
+
     return id
   }
 
@@ -42,25 +44,9 @@ class Room extends EventTarget {
   }
 
   extendErasureIntervals(pathID, pointID, newIntervals) {
-    const self = this
-
-    // eslint-disable-next-line require-yield
-    this._y.db.requestTransaction(function* requestTransaction() {
-      const prevJSON = self.shared.eraseIntervals.get(pathID) || "[]"
-      const pathIntervals = JSON.parse(prevJSON)
-
-      const combinedIntervals = combineErasureIntervals(
-        [pathIntervals],
-        [flattenErasureIntervals({ [pointID]: newIntervals })],
-      )[0]
-      const postJSON = JSON.stringify(combinedIntervals)
-
-      if (prevJSON == postJSON) {
-        return
-      }
-
-      self.shared.eraseIntervals.set(pathID, postJSON)
-    })
+    this.shared.eraseIntervals
+      .get(pathID)
+      .merge(flattenErasureIntervals({ [pointID]: newIntervals }))
   }
 
   // TODO: Refactor duplication
@@ -114,7 +100,7 @@ class Room extends EventTarget {
 
     if (!intervals) return []
 
-    return spreadErasureIntervals(JSON.parse(intervals))
+    return spreadErasureIntervals(intervals.get())
   }
 
   inviteUser(id) {
@@ -215,7 +201,13 @@ class Room extends EventTarget {
       }
     })
     this.shared.eraseIntervals.observe((lineEvent) => {
-      dispatchRemovedIntervalsEvent(lineEvent)
+      if (lineEvent.type == "add") {
+        dispatchRemovedIntervalsEvent(lineEvent)
+
+        lineEvent.value.observe(() => {
+          dispatchRemovedIntervalsEvent(lineEvent)
+        })
+      }
     })
   }
 }
diff --git a/src/tool-selection.js b/src/tool-selection.js
index 16c8e687aaeb060a30deb47b712b7673195de69c..a6c2d0563bc9e59d55bc5e79998ed1c3f90f76a2 100644
--- a/src/tool-selection.js
+++ b/src/tool-selection.js
@@ -60,11 +60,18 @@ HTML.picker.addEventListener("change", () => {
 
 HTML.slider.oninput = function() {
   HTML.output.innerHTML = this.value
-  strokeRadius = this.value / 10
+  strokeRadius = this.value / 2
 }
 
 HTML.output.innerHTML = HTML.slider.value
 
+// If the page has been refreshed
+if (performance.navigation.type == 1) {
+  const sliderValue = parseInt(HTML.output.innerHTML)
+  HTML.slider.setAttribute("value", sliderValue)
+  strokeRadius = sliderValue / 2
+}
+
 const x = window.matchMedia(
   "only screen and (orientation: landscape) and (max-width: 600px)",
 )
diff --git a/src/y-union.js b/src/y-union.js
new file mode 100644
index 0000000000000000000000000000000000000000..aabe816396061e64ec907505ecd051d8206dd6ae
--- /dev/null
+++ b/src/y-union.js
@@ -0,0 +1,155 @@
+/* global Y */
+
+import { combineErasureIntervals } from "./erasure.js"
+
+export const Union = {
+  create: function(id) {
+    return {
+      id: id,
+      union: null,
+      struct: "Union",
+    }
+  },
+  encode: function(op) {
+    const e = {
+      struct: "Union",
+      type: op.type,
+      id: op.id,
+      union: null,
+    }
+    if (op.requires != null) {
+      e.requires = op.requires
+    }
+    if (op.info != null) {
+      e.info = op.info
+    }
+    return e
+  },
+  requiredOps: function() {
+    return []
+  },
+  execute: function*() {},
+}
+
+export default function extendYUnion(Y) {
+  class YUnion extends Y.utils.CustomType {
+    constructor(os, model, contents) {
+      super()
+      this._model = model.id
+      this._parent = null
+      this._deepEventHandler = new Y.utils.EventListenerHandler()
+      this.os = os
+      this.union = model.union ? Y.utils.copyObject(model.union) : null
+      this.contents = contents
+      this.eventHandler = new Y.utils.EventHandler((op) => {
+        // compute op event
+        if (op.struct === "Insert") {
+          if (!Y.utils.compareIds(op.id, this.union)) {
+            const mergedContents = this._merge(JSON.parse(op.content[0]))
+            this.union = op.id
+            if (this.contents == mergedContents) {
+              return
+            }
+            this.contents = mergedContents
+            Y.utils.bubbleEvent(this, {
+              object: this,
+              type: "merge",
+            })
+          }
+        } else {
+          throw new Error("Unexpected Operation!")
+        }
+      })
+    }
+    _getPathToChild(/*childId*/) {
+      return undefined
+    }
+    _destroy() {
+      this.eventHandler.destroy()
+      this.eventHandler = null
+      this.contents = null
+      this._model = null
+      this._parent = null
+      this.os = null
+      this.union = null
+    }
+    get() {
+      return JSON.parse(this.contents)
+    }
+    _merge(newIntervals) {
+      const prevIntervals = this.get()
+
+      const mergedIntervals = combineErasureIntervals(
+        [prevIntervals],
+        [newIntervals],
+      )[0]
+
+      return JSON.stringify(mergedIntervals)
+    }
+    merge(newIntervals) {
+      const mergedContents = this._merge(newIntervals)
+
+      if (this.contents == mergedContents) {
+        return
+      }
+
+      const insert = {
+        id: this.os.getNextOpId(1),
+        left: null,
+        right: this.union,
+        origin: null,
+        parent: this._model,
+        content: [mergedContents],
+        struct: "Insert",
+      }
+
+      const eventHandler = this.eventHandler
+      this.os.requestTransaction(function*() {
+        yield* eventHandler.awaitOps(this, this.applyCreatedOperations, [
+          [insert],
+        ])
+      })
+      // always remember to do that after this.os.requestTransaction
+      // (otherwise values might contain a undefined reference to type)
+      eventHandler.awaitAndPrematurelyCall([insert])
+    }
+    observe(f) {
+      this.eventHandler.addEventListener(f)
+    }
+    observeDeep(f) {
+      this._deepEventHandler.addEventListener(f)
+    }
+    unobserve(f) {
+      this.eventHandler.removeEventListener(f)
+    }
+    unobserveDeep(f) {
+      this._deepEventHandler.removeEventListener(f)
+    }
+    // eslint-disable-next-line require-yield
+    *_changed(transaction, op) {
+      this.eventHandler.receivedOp(op)
+    }
+  }
+  Y.extend(
+    "Union",
+    new Y.utils.CustomTypeDefinition({
+      name: "Union",
+      class: YUnion,
+      struct: "Union",
+      initType: function* YUnionInitializer(os, model) {
+        const union = model.union
+        const contents =
+          union !== null ? yield* this.getOperation(union).content[0] : "[]"
+        return new YUnion(os, model, contents)
+      },
+      createType: function YUnionCreator(os, model) {
+        const union = new YUnion(os, model, "[]")
+        return union
+      },
+    }),
+  )
+}
+
+if (typeof Y !== "undefined") {
+  extendYUnion(Y)
+}