diff --git a/__tests__/benchmark.data.js b/__tests__/benchmark.data.js
index 97283a38890b0bbe53c1a7d8dc9aa781db52927d..86df444825ed4c4c413f9c8ac57aa43fd4d526c8 100644
--- a/__tests__/benchmark.data.js
+++ b/__tests__/benchmark.data.js
@@ -1,128 +1,130 @@
+export function createMessageReceivedEvent(
+  message,
+  channel = "y-js",
+  uid = "moritz",
+) {
+  return {
+    detail: {
+      uid,
+      channel,
+      message,
+    },
+  }
+}
+
 export const handshake = {
-  uid: "tiger",
-  channel: "tw-ml",
-  message: {
-    uuid: "42c4566d-0cfe-4c00-a645-254b7887e477",
-    message: Uint8Array.of(162, 109, 108),
-    slice: 0,
-    length: 1,
-    compressed: false,
-  },
+  uuid: "42c4566d-0cfe-4c00-a645-254b7887e477",
+  message: Uint8Array.of(162, 109, 108),
+  slice: 0,
+  length: 1,
+  compressed: false,
 }
 
 export const syncStep1 = {
-  uid: "tiger",
-  channel: "y-js",
-  message: {
-    uuid: "6e20b20d-e1d8-405d-8a61-d56cb1c47a24",
-    message: Uint8Array.of(
-      120,
-      156,
-      107,
-      93,
-      82,
-      82,
-      89,
-      144,
-      186,
-      186,
-      184,
-      50,
-      47,
-      89,
-      161,
-      184,
-      36,
-      181,
-      64,
-      193,
-      112,
-      69,
-      113,
-      73,
-      98,
-      73,
-      106,
-      112,
-      106,
-      73,
-      195,
-      202,
-      148,
-      212,
-      156,
-      84,
-      8,
-      115,
-      125,
-      65,
-      81,
-      126,
-      73,
-      126,
-      114,
-      126,
-      78,
-      88,
-      106,
-      81,
-      113,
-      102,
-      126,
-      30,
-      247,
-      146,
-      196,
-      210,
-      146,
-      140,
-      3,
-      0,
-      80,
-      113,
-      26,
-      230,
-    ),
-    slice: 0,
-    length: 1,
-    compressed: true,
-  },
+  uuid: "6e20b20d-e1d8-405d-8a61-d56cb1c47a24",
+  message: Uint8Array.of(
+    120,
+    156,
+    107,
+    93,
+    82,
+    82,
+    89,
+    144,
+    186,
+    186,
+    184,
+    50,
+    47,
+    89,
+    161,
+    184,
+    36,
+    181,
+    64,
+    193,
+    112,
+    69,
+    113,
+    73,
+    98,
+    73,
+    106,
+    112,
+    106,
+    73,
+    195,
+    202,
+    148,
+    212,
+    156,
+    84,
+    8,
+    115,
+    125,
+    65,
+    81,
+    126,
+    73,
+    126,
+    114,
+    126,
+    78,
+    88,
+    106,
+    81,
+    113,
+    102,
+    126,
+    30,
+    247,
+    146,
+    196,
+    210,
+    146,
+    140,
+    3,
+    0,
+    80,
+    113,
+    26,
+    230,
+  ),
+  slice: 0,
+  length: 1,
+  compressed: true,
 }
 
 export const syncDone = {
-  uid: "tiger",
-  channel: "y-js",
-  message: {
-    message: Uint8Array.of(
-      120,
-      156,
-      107,
-      92,
-      82,
-      82,
-      89,
-      144,
-      186,
-      178,
-      184,
-      50,
-      47,
-      89,
-      33,
-      37,
-      63,
-      47,
-      21,
-      0,
-      64,
-      79,
-      7,
-      20,
-    ),
-    slice: 0,
-    length: 1,
-    compressed: true,
-  },
+  message: Uint8Array.of(
+    120,
+    156,
+    107,
+    92,
+    82,
+    82,
+    89,
+    144,
+    186,
+    178,
+    184,
+    50,
+    47,
+    89,
+    33,
+    37,
+    63,
+    47,
+    21,
+    0,
+    64,
+    79,
+    7,
+    20,
+  ),
+  slice: 0,
+  length: 1,
+  compressed: true,
 }
 
 export const dotDraw = [[209, 88, 5.000000000000001, "#0000ff"]]
diff --git a/__tests__/benchmark.test.js b/__tests__/benchmark.test.js
index dc9ec46e8bcbd4e8dd97cf96a582568f90c9e090..7784d3a5627c7649ac8887ed72761099d2577e23 100644
--- a/__tests__/benchmark.test.js
+++ b/__tests__/benchmark.test.js
@@ -13,10 +13,11 @@ import MockConnection, {
   terminatePeer,
   destructor,
   addEventListener,
-  eventListeners,
+  getEventListener,
 } from "../src/connection/MockConnection.js"
 
 import {
+  createMessageReceivedEvent,
   handshake,
   syncStep1,
   syncDone,
@@ -47,6 +48,8 @@ function printBenchmark(title, iterations, results) {
 }
 
 let room = null
+let updateRoom = null
+let syncRoom = null
 
 describe("drawing app mesh", () => {
   beforeEach(async () => {
@@ -60,13 +63,34 @@ describe("drawing app mesh", () => {
     addEventListener.mockClear()
     MockConnection.mockClear()
 
-    room = await connect("data", MockConnection)
-    eventListeners.get("messageReceived")({ detail: handshake })
+    room = await connect("room", MockConnection)
+    getEventListener(
+      "room",
+      "messageReceived",
+    )(createMessageReceivedEvent(handshake, "tw-ml"))
+
+    updateRoom = await connect("update", MockConnection)
+    getEventListener(
+      "update",
+      "messageReceived",
+    )(createMessageReceivedEvent(handshake, "tw-ml"))
+
+    syncRoom = await connect("sync", MockConnection)
+    getEventListener(
+      "sync",
+      "messageReceived",
+    )(createMessageReceivedEvent(handshake, "tw-ml"))
   })
 
   afterEach(() => {
     room.disconnect()
     room = null
+
+    updateRoom.disconnect()
+    updateRoom = null
+
+    syncRoom.disconnect()
+    syncRoom = null
   })
 
   it("benchmarks a single draw and erase update sequentially", () => {
@@ -160,7 +184,10 @@ describe("drawing app mesh", () => {
 
           prevTime = process.hrtime()
 
-          eventListeners.get("messageReceived")({ detail: syncStep1 })
+          getEventListener(
+            "room",
+            "messageReceived",
+          )(createMessageReceivedEvent(syncStep1))
         }),
     )
   })
@@ -269,24 +296,41 @@ describe("drawing app mesh", () => {
 
             prevTime = process.hrtime()
 
-            eventListeners.get("messageReceived")({ detail: syncStep1 })
+            getEventListener(
+              "room",
+              "messageReceived",
+            )(createMessageReceivedEvent(syncStep1))
           }),
       )
   })
 
   it("communicates a single draw and erase update", () => {
+    jest.setTimeout(30000)
+
     let dotID
 
-    let syncPackets = 0
+    const addPackets = []
+    const erasePackets = []
+    const syncPackets = []
     let syncDonePacket = -1
 
+    const updatePaths = {}
+    const updateIntervals = {}
+    const syncPaths = {}
+    const syncIntervals = {}
+
     let timeout
 
     return new Promise((resolve) => {
+      // Draw the dot path
       broadcastListener.callback = (channel, message) => {
         expect(channel).toEqual("y-js")
         expect(message.message instanceof Uint8Array).toBe(true)
-        resolve()
+
+        addPackets.push(message)
+
+        clearTimeout(timeout)
+        timeout = setTimeout(() => resolve(), 1000)
       }
 
       dotID = room.addPath(dotDraw[0])
@@ -294,10 +338,15 @@ describe("drawing app mesh", () => {
       .then(
         () =>
           new Promise((resolve) => {
+            // Erase the dot path
             broadcastListener.callback = (channel, message) => {
               expect(channel).toEqual("y-js")
               expect(message.message instanceof Uint8Array).toBe(true)
-              resolve()
+
+              erasePackets.push(message)
+
+              clearTimeout(timeout)
+              timeout = setTimeout(() => resolve(), 1000)
             }
 
             room.extendErasureIntervals(dotID, dotErase[0][0], dotErase[0][1])
@@ -306,32 +355,160 @@ describe("drawing app mesh", () => {
       .then(
         () =>
           new Promise((resolve) => {
+            // Request a sync step 2
             sendListener.callback = (uid, channel, message) => {
-              expect(uid).toEqual("tiger")
+              expect(uid).toEqual("moritz")
               expect(channel).toEqual("y-js")
               expect(message.message instanceof Uint8Array).toBe(true)
 
-              syncPackets += 1
+              syncPackets.push(message)
 
               if (
-                message.message.length == syncDone.message.message.length &&
-                JSON.stringify(Object.assign(message, { uuid: undefined })) ==
-                  JSON.stringify(syncDone.message)
+                message.message.length == syncDone.message.length &&
+                JSON.stringify(
+                  Object.assign({}, message, { uuid: undefined }),
+                ) == JSON.stringify(syncDone)
               ) {
                 expect(syncDonePacket).toEqual(-1)
 
-                syncDonePacket = syncPackets
+                syncDonePacket = syncPackets.length
               }
 
               clearTimeout(timeout)
               timeout = setTimeout(() => {
-                expect(syncDonePacket).toEqual(syncPackets)
+                expect(syncDonePacket).toEqual(syncPackets.length)
 
                 resolve()
               }, 1000)
             }
 
-            eventListeners.get("messageReceived")({ detail: syncStep1 })
+            getEventListener(
+              "room",
+              "messageReceived",
+            )(createMessageReceivedEvent(syncStep1))
+          }),
+      )
+      .then(
+        () =>
+          new Promise((resolve) => {
+            // Replay the draw updates
+            updateRoom.addEventListener(
+              "addOrUpdatePath",
+              ({ detail: { id, points } }) => {
+                updatePaths[id] = points
+
+                clearTimeout(timeout)
+                timeout = setTimeout(() => resolve(), 1000)
+              },
+            )
+
+            updateRoom.addEventListener(
+              "removedIntervalsChange",
+              ({ detail: { id, intervals } }) => {
+                updateIntervals[id] = intervals
+
+                clearTimeout(timeout)
+                timeout = setTimeout(() => resolve(), 1000)
+              },
+            )
+
+            for (const addPacket of addPackets) {
+              getEventListener(
+                "update",
+                "messageReceived",
+              )(createMessageReceivedEvent(addPacket))
+            }
+          }),
+      )
+      .then(
+        () =>
+          new Promise((resolve) => {
+            // Check the draw updates
+            expect(updatePaths).toStrictEqual({ [dotID]: dotDraw })
+            expect(updateIntervals).toStrictEqual({ [dotID]: {} })
+
+            resolve()
+          }),
+      )
+      .then(
+        () =>
+          new Promise((resolve) => {
+            // Replay the erase updates
+            expect(updatePaths).toStrictEqual({ [dotID]: dotDraw })
+            expect(updateIntervals).toStrictEqual({ [dotID]: {} })
+
+            updateRoom.addEventListener(
+              "removedIntervalsChange",
+              ({ detail: { id, intervals } }) => {
+                updateIntervals[id] = intervals
+
+                clearTimeout(timeout)
+                timeout = setTimeout(() => resolve(), 1000)
+              },
+            )
+
+            for (const erasePacket of erasePackets) {
+              getEventListener(
+                "update",
+                "messageReceived",
+              )(createMessageReceivedEvent(erasePacket))
+            }
+          }),
+      )
+      .then(
+        () =>
+          new Promise((resolve) => {
+            // Check the erase updates
+            expect(updatePaths).toStrictEqual({ [dotID]: dotDraw })
+            expect(updateIntervals).toStrictEqual({
+              [dotID]: { [dotErase[0][0]]: dotErase[0][1] },
+            })
+
+            resolve()
+          }),
+      )
+      .then(
+        () =>
+          new Promise((resolve) => {
+            // Replay the synchronisation
+            syncRoom.addEventListener(
+              "addOrUpdatePath",
+              ({ detail: { id, points } }) => {
+                syncPaths[id] = points
+
+                clearTimeout(timeout)
+                timeout = setTimeout(() => resolve(), 1000)
+              },
+            )
+
+            syncRoom.addEventListener(
+              "removedIntervalsChange",
+              ({ detail: { id, intervals } }) => {
+                syncIntervals[id] = intervals
+
+                clearTimeout(timeout)
+                timeout = setTimeout(() => resolve(), 1000)
+              },
+            )
+
+            for (const syncPacket of syncPackets) {
+              getEventListener(
+                "sync",
+                "messageReceived",
+              )(createMessageReceivedEvent(syncPacket))
+            }
+          }),
+      )
+      .then(
+        () =>
+          new Promise((resolve) => {
+            // Check the synchronisation
+            expect(syncPaths).toStrictEqual({ [dotID]: dotDraw })
+            expect(syncIntervals).toStrictEqual({
+              [dotID]: { [dotErase[0][0]]: dotErase[0][1] },
+            })
+
+            resolve()
           }),
       )
   })
@@ -443,7 +620,10 @@ describe("drawing app mesh", () => {
 
           prevTime = process.hrtime()
 
-          eventListeners.get("messageReceived")({ detail: syncStep1 })
+          getEventListener(
+            "room",
+            "messageReceived",
+          )(createMessageReceivedEvent(syncStep1))
         }),
     )
   })
@@ -569,7 +749,10 @@ describe("drawing app mesh", () => {
 
             prevTime = process.hrtime()
 
-            eventListeners.get("messageReceived")({ detail: syncStep1 })
+            getEventListener(
+              "room",
+              "messageReceived",
+            )(createMessageReceivedEvent(syncStep1))
           }),
       )
   })
@@ -620,16 +803,16 @@ describe("drawing app mesh", () => {
         () =>
           new Promise((resolve) => {
             sendListener.callback = (uid, channel, message) => {
-              expect(uid).toEqual("tiger")
+              expect(uid).toEqual("moritz")
               expect(channel).toEqual("y-js")
               expect(message.message instanceof Uint8Array).toBe(true)
 
               syncPackets += 1
 
               if (
-                message.message.length == syncDone.message.message.length &&
+                message.message.length == syncDone.message.length &&
                 JSON.stringify(Object.assign(message, { uuid: undefined })) ==
-                  JSON.stringify(syncDone.message)
+                  JSON.stringify(syncDone)
               ) {
                 expect(syncDonePacket).toEqual(-1)
 
@@ -644,7 +827,10 @@ describe("drawing app mesh", () => {
               }, 1000)
             }
 
-            eventListeners.get("messageReceived")({ detail: syncStep1 })
+            getEventListener(
+              "room",
+              "messageReceived",
+            )(createMessageReceivedEvent(syncStep1))
           }),
       )
   })
diff --git a/package-lock.json b/package-lock.json
index 09644afd57c11daafb2df572e15af4710115ac2d..2417731f8da044be4423a51efbbefab9d595adff 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -928,9 +928,9 @@
       },
       "dependencies": {
         "acorn": {
-          "version": "6.3.0",
-          "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz",
-          "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==",
+          "version": "6.4.0",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz",
+          "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==",
           "dev": true
         }
       }
@@ -1251,9 +1251,9 @@
       "dev": true
     },
     "aws4": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
-      "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.0.tgz",
+      "integrity": "sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==",
       "dev": true
     },
     "babel-code-frame": {
@@ -8698,9 +8698,9 @@
       "dev": true
     },
     "resolve": {
-      "version": "1.13.0",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.13.0.tgz",
-      "integrity": "sha512-HHZ3hmOrk5SvybTb18xq4Ek2uLqLO5/goFCYUyvn26nWox4hdlKlfC/+dChIZ6qc4ZeYcN9ekTz0yyHsFgumMw==",
+      "version": "1.13.1",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.13.1.tgz",
+      "integrity": "sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w==",
       "dev": true,
       "requires": {
         "path-parse": "^1.0.6"
@@ -11004,9 +11004,9 @@
       },
       "dependencies": {
         "acorn": {
-          "version": "6.3.0",
-          "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz",
-          "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==",
+          "version": "6.4.0",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz",
+          "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==",
           "dev": true
         },
         "eslint-scope": {
@@ -11043,9 +11043,9 @@
       },
       "dependencies": {
         "acorn": {
-          "version": "6.3.0",
-          "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz",
-          "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==",
+          "version": "6.4.0",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz",
+          "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==",
           "dev": true
         },
         "chalk": {
@@ -11424,6 +11424,12 @@
       "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
       "dev": true
     },
+    "yaeti": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-1.0.2.tgz",
+      "integrity": "sha512-sc1JByruVRqL6GYdIKbcvYw8PRmYeuwtSd376fM13DNE+JjBh37qIlKjCtqg9mKV2N2+xCfyil3Hd6BXN9W1uQ==",
+      "dev": true
+    },
     "yallist": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
diff --git a/package.json b/package.json
index 6e50ba9cb172bf6207fdd7d9948da3fd221d739f..0d23c135e1320c1d063a58623d00563ae51c7884 100644
--- a/package.json
+++ b/package.json
@@ -63,7 +63,8 @@
     "webpack-bundle-analyzer": "^3.6.0",
     "webpack-cli": "^3.3.9",
     "webpack-merge": "^4.2.2",
-    "webpack-preprocessor-loader": "^1.1.2"
+    "webpack-preprocessor-loader": "^1.1.2",
+    "yaeti": "^1.0.2"
   },
   "pre-commit": [
     "lint",
diff --git a/src/app.js b/src/app.js
index 968d9b0315a47774ba310ac4bcd5fc54c3a17ce0..57f9c36c3389c68657532106b4f80928a9a8be7b 100644
--- a/src/app.js
+++ b/src/app.js
@@ -5,7 +5,7 @@
 
 import * as canvas from "./canvas.js"
 import * as HTML from "./elements.js"
-import { computeErasureIntervals, combineErasureIntervals } from "./erasure.js"
+import { computeErasureIntervals } from "./erasure.js"
 import { connect } from "./room.js"
 import WebRTCConnection from "./connection/WebRTC.js"
 import * as toolSelection from "./tool-selection.js"
@@ -40,26 +40,10 @@ const humanHasher = new humanhash()
 function eraseEverythingAtPosition(x, y, radius, room) {
   const mousePos = [x, y]
   room.getPaths().forEach((points, pathID) => {
-    const prevPathIntervals =
-      (room.erasureIntervals || { [pathID]: {} })[pathID] || {}
-    const newPathIntervals = computeErasureIntervals(
-      points,
-      mousePos,
-      radius,
-      prevPathIntervals,
-    )
-
-    const erasureIntervalsForPath = combineErasureIntervals(
-      prevPathIntervals,
-      newPathIntervals,
-    )
+    const newPathIntervals = computeErasureIntervals(points, mousePos, radius)
 
-    Object.keys(erasureIntervalsForPath).forEach((pointID) =>
-      room.extendErasureIntervals(
-        pathID,
-        pointID,
-        erasureIntervalsForPath[pointID],
-      ),
+    Object.keys(newPathIntervals).forEach((pointID) =>
+      room.extendErasureIntervals(pathID, pointID, newPathIntervals[pointID]),
     )
   })
 }
@@ -146,17 +130,13 @@ const onRoomConnect = (room_) => {
   })
 
   room.addEventListener("addOrUpdatePath", ({ detail: { id, points } }) => {
-    canvas.renderPath(id, points, room.erasureIntervals[id] || [])
+    canvas.renderPath(id, points, room.getErasureIntervals(id))
   })
 
   room.addEventListener(
     "removedIntervalsChange",
-    ({ detail: { id, intervals, points } }) => {
-      room.erasureIntervals[id] = combineErasureIntervals(
-        room.erasureIntervals[id] || {},
-        intervals,
-      )
-      canvas.renderPath(id, points, room.erasureIntervals[id] || [])
+    ({ detail: { id, intervals } }) => {
+      canvas.renderPath(id, room.getPathPoints(id), intervals)
     },
   )
 }
diff --git a/src/connection/MockConnection.js b/src/connection/MockConnection.js
index edc563fe116c1738f5e7d98c1ee5796bb8ffef58..2fa9825675d0f5e13fe8d0be23d871fed665fb25 100644
--- a/src/connection/MockConnection.js
+++ b/src/connection/MockConnection.js
@@ -1,4 +1,4 @@
-export const getUserID = jest.fn(() => "moritz")
+export const getUserID = jest.fn((uid) => uid)
 export const getPeerHandle = jest.fn((/*uid*/) => undefined)
 export const getPeerFootprint = jest.fn((/*uid*/) =>
   Promise.resolve(Date.now()))
@@ -30,9 +30,11 @@ broadcast.mockClear = () => {
 export const terminatePeer = jest.fn()
 export const destructor = jest.fn(() => eventListeners.clear())
 
-export const eventListeners = new Map()
-export const addEventListener = jest.fn((event, callback) =>
-  eventListeners.set(event, callback),
+const eventListeners = new Map()
+export const getEventListener = (room, event) =>
+  eventListeners.get(`${room}:${event}`)
+export const addEventListener = jest.fn((room, event, callback) =>
+  eventListeners.set(`${room}:${event}`, callback),
 )
 const addEventListenerMockClear = addEventListener.mockClear
 addEventListener.mockClear = () => {
@@ -40,22 +42,24 @@ addEventListener.mockClear = () => {
   addEventListenerMockClear()
 }
 
-const MockConnection = jest.fn().mockImplementation((/*options*/) => {
+const MockConnection = jest.fn().mockImplementation(({ room }) => {
   setTimeout(
     () =>
-      eventListeners.has("roomJoined") && eventListeners.get("roomJoined")(),
+      getEventListener(room, "roomJoined") &&
+      getEventListener(room, "roomJoined")(),
     0,
   )
 
   return {
-    getUserID,
+    getUserID: () => getUserID(room),
     getPeerHandle,
     getPeerFootprint,
     send,
     broadcast,
     terminatePeer,
     destructor,
-    addEventListener,
+    addEventListener: (event, callback) =>
+      addEventListener(room, event, callback),
   }
 })
 
diff --git a/src/room.js b/src/room.js
index ca330db4421a55d8e7940ffb2dd5cd9424e40cf4..69d11a2697c9b8916efbd2abf9d246cf6b615a56 100644
--- a/src/room.js
+++ b/src/room.js
@@ -16,13 +16,17 @@ yP2PMesh(Y)
 
 import { spreadErasureIntervals, flattenErasureIntervals } from "./erasure.js"
 
+/* webpack should NOT import the yaeti NodeJS polyfill */
+// #!if false
+import { EventTarget } from "yaeti"
+// #!endif
+
 class Room extends EventTarget {
   constructor(name) {
     super()
     this.name = name
     this._y = null
     this.ownID = null
-    this.erasureIntervals = {}
   }
 
   disconnect() {
@@ -58,6 +62,14 @@ class Room extends EventTarget {
     return paths
   }
 
+  getErasureIntervals(pathID) {
+    return this._generateRemovedIntervals(pathID)
+  }
+
+  getPathPoints(pathID) {
+    return this._generatePath(pathID)
+  }
+
   get shared() {
     return this._y.share
   }
@@ -148,8 +160,7 @@ class Room extends EventTarget {
     const dispatchRemovedIntervalsEvent = (lineEvent) => {
       const id = lineEvent.name
       const intervals = this._generateRemovedIntervals(id)
-      const points = this._generatePath(id)
-      const detail = { id, intervals, points }
+      const detail = { id, intervals }
       this.dispatchEvent(
         new CustomEvent("removedIntervalsChange", {
           detail,