diff --git a/.gitignore b/.gitignore
index 8ac5267c1ffc9b4a6bedbc26cca1d33b7ba2ab39..c8f936e58843319736767cdb33dab019671612e0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
-
 # Created by https://www.gitignore.io/api/vim,node,macos,visualstudiocode
 # Edit at https://www.gitignore.io/?templates=vim,node,macos,visualstudiocode
 
@@ -12,6 +11,23 @@ src/signalbuddy
 src/yjs
 src/tiny-worker
 
+# Temporary test dump files
+dot-seq-add.json
+dot-seq-erase.json
+dot-seq-sync.json
+
+dot-par-add.json
+dot-par-erase.json
+dot-par-sync.json
+
+path-seq-add.json
+path-seq-erase.json
+path-seq-sync.json
+
+path-par-add.json
+path-par-erase.json
+path-par-sync.json
+
 ### macOS ###
 # General
 .DS_Store
diff --git a/__tests__/benchmark.test.js b/__tests__/benchmark.test.js
index 9305a443d1a8d2215a33c0c81a635d68d97d8c0a..b05b4bb2d6f242983d8f2beebaecc5ea44af124b 100644
--- a/__tests__/benchmark.test.js
+++ b/__tests__/benchmark.test.js
@@ -1,3 +1,4 @@
+import fs from "fs"
 import chalk from "chalk"
 
 import { connect } from "../src/room.js"
@@ -27,6 +28,38 @@ import {
   pathErase,
 } from "./benchmark.data.js"
 
+// Adapted from https://github.com/jprichardson/buffer-json (MIT license)
+
+function stringify(value, space) {
+  return JSON.stringify(value, replacer, space)
+}
+
+function parse(text) {
+  return JSON.parse(text, reviver)
+}
+
+function replacer(key, value) {
+  if (value instanceof Uint8Array) {
+    return "base64:" + Buffer.from(value).toString("base64")
+  }
+  return value
+}
+
+function reviver(key, value) {
+  if (typeof value == "string" && value.startsWith("base64:")) {
+    return Uint8Array.from(Buffer.from(value.slice("base64:".length), "base64"))
+  }
+  return value
+}
+
+function dumpBSON(filename, data) {
+  return fs.writeFileSync(filename, stringify(data))
+}
+
+function loadBSON(filename) {
+  return parse(fs.readFileSync(filename))
+}
+
 function printBenchmark(title, iterations, results) {
   process.stdout.write(`\n  ${title} (${iterations} iterations):\n`)
 
@@ -52,12 +85,12 @@ function printBenchmark(title, iterations, results) {
   process.stdout.write(`\n`)
 }
 
-let room = null
-let updateRoom = null
-let syncRoom = null
+//let room = null
+//let updateRoom = null
+//let syncRoom = null
 
 describe("drawing app mesh", () => {
-  beforeEach(async () => {
+  beforeEach(() => {
     getUserID.mockClear()
     getPeerHandle.mockClear()
     getPeerFootprint.mockClear()
@@ -68,7 +101,7 @@ describe("drawing app mesh", () => {
     addEventListener.mockClear()
     MockConnection.mockClear()
 
-    room = await connect("room", MockConnection)
+    /*room = await connect("room", MockConnection)
     getEventListener(
       "room",
       "messageReceived",
@@ -84,10 +117,10 @@ describe("drawing app mesh", () => {
     getEventListener(
       "sync",
       "messageReceived",
-    )(createMessageReceivedEvent(handshake, "tw-ml"))
+    )(createMessageReceivedEvent(handshake, "tw-ml"))*/
   })
 
-  afterEach(() => {
+  /*afterEach(() => {
     room.disconnect()
     room = null
 
@@ -96,12 +129,12 @@ describe("drawing app mesh", () => {
 
     syncRoom.disconnect()
     syncRoom = null
-  })
+  })*/
 
   it("benchmarks a single draw and erase update sequentially", () => {
     const ITERATIONS = 1000
 
-    jest.setTimeout(ITERATIONS * (10 + 300))
+    jest.setTimeout(ITERATIONS * 400)
 
     const dotIDs = []
     let prevTime
@@ -109,13 +142,13 @@ describe("drawing app mesh", () => {
     let broadcasts = 0
 
     let addLocTime = 0
-    const addPackets = []
+    let addPackets = []
     let addSize = 0
     let eraseLocTime = 0
-    const erasePackets = []
+    let erasePackets = []
     let eraseSize = 0
     let syncLocTime
-    const syncPackets = []
+    let syncPackets = []
     let syncSize = 0
 
     let addRemTime = 0
@@ -127,43 +160,69 @@ describe("drawing app mesh", () => {
 
     let timeout
 
-    return new Promise((resolve) => {
-      broadcastListener.callback = (channel, message) => {
-        const currTime = process.hrtime()
+    let room
+    let updateRoom
+    let syncRoom
+
+    return new Promise(async (resolve) => {
+      room = await connect("room", MockConnection)
+      getEventListener(
+        "room",
+        "messageReceived",
+      )(createMessageReceivedEvent(handshake, "tw-ml"))
+      return resolve()
+    })
+      .then(
+        () =>
+          new Promise((resolve) => {
+            broadcastListener.callback = (channel, message) => {
+              const currTime = process.hrtime()
 
-        broadcasts += 1
+              broadcasts += 1
 
-        if (broadcasts <= ITERATIONS) {
-          addLocTime +=
-            (currTime[0] - prevTime[0]) * 1e9 + (currTime[1] - prevTime[1])
-          addPackets.push(message)
-          addSize += message.message.length
-        } else {
-          eraseLocTime +=
-            (currTime[0] - prevTime[0]) * 1e9 + (currTime[1] - prevTime[1])
-          erasePackets.push(message)
-          eraseSize += message.message.length
-        }
+              if (broadcasts <= ITERATIONS) {
+                addLocTime +=
+                  (currTime[0] - prevTime[0]) * 1e9 +
+                  (currTime[1] - prevTime[1])
+                addPackets.push(message)
+                addSize += message.message.length
+              } else {
+                eraseLocTime +=
+                  (currTime[0] - prevTime[0]) * 1e9 +
+                  (currTime[1] - prevTime[1])
+                erasePackets.push(message)
+                eraseSize += message.message.length
+              }
 
-        prevTime = process.hrtime()
+              prevTime = process.hrtime()
 
-        if (broadcasts < ITERATIONS) {
-          dotIDs.push(room.addPath(dotDraw[0]))
-        } else if (broadcasts < ITERATIONS * 2) {
-          room.extendErasureIntervals(
-            dotIDs[broadcasts - ITERATIONS],
-            dotErase[0][0],
-            dotErase[0][1],
-          )
-        } else {
-          resolve()
-        }
-      }
+              if (broadcasts < ITERATIONS) {
+                dotIDs.push(room.addPath(dotDraw[0]))
+              } else if (broadcasts < ITERATIONS * 2) {
+                room.extendErasureIntervals(
+                  dotIDs[broadcasts - ITERATIONS],
+                  dotErase[0][0],
+                  dotErase[0][1],
+                )
+              } else {
+                resolve()
+              }
+            }
 
-      prevTime = process.hrtime()
+            prevTime = process.hrtime()
 
-      dotIDs.push(room.addPath(dotDraw[0]))
-    })
+            dotIDs.push(room.addPath(dotDraw[0]))
+          }),
+      )
+      .then(() => {
+        broadcastListener.callback = null
+
+        dumpBSON("dot-seq-add.json", addPackets)
+        addPackets = null
+
+        dumpBSON("dot-seq-erase.json", erasePackets)
+        erasePackets = null
+      })
       .then(
         () =>
           new Promise((resolve) => {
@@ -187,6 +246,30 @@ describe("drawing app mesh", () => {
             )(createMessageReceivedEvent(syncStep1))
           }),
       )
+      .then(() => {
+        sendListener.callback = null
+
+        dumpBSON("dot-seq-sync.json", syncPackets)
+        syncPackets = null
+
+        room.disconnect()
+        room = null
+      })
+      .then(
+        () =>
+          new Promise(async (resolve) => {
+            updateRoom = await connect("update", MockConnection)
+            getEventListener(
+              "update",
+              "messageReceived",
+            )(createMessageReceivedEvent(handshake, "tw-ml"))
+
+            addPackets = loadBSON("dot-seq-add.json")
+            erasePackets = loadBSON("dot-seq-erase.json")
+
+            return resolve()
+          }),
+      )
       .then(
         () =>
           new Promise((resolve) => {
@@ -256,6 +339,27 @@ describe("drawing app mesh", () => {
             )(createMessageReceivedEvent(addPackets[0]))
           }),
       )
+      .then(() => {
+        addPackets = null
+        erasePackets = null
+
+        updateRoom.disconnect()
+        updateRoom = null
+      })
+      .then(
+        () =>
+          new Promise(async (resolve) => {
+            syncRoom = await connect("sync", MockConnection)
+            getEventListener(
+              "sync",
+              "messageReceived",
+            )(createMessageReceivedEvent(handshake, "tw-ml"))
+
+            syncPackets = loadBSON("dot-seq-sync.json")
+
+            return resolve()
+          }),
+      )
       .then(
         () =>
           new Promise((resolve) => {
@@ -292,24 +396,34 @@ describe("drawing app mesh", () => {
           }),
       )
       .then(() => {
+        syncPackets = null
+
+        syncRoom.disconnect()
+        syncRoom = null
+      })
+      .then(() => {
+        addPackets = loadBSON("dot-seq-add.json").length
+        erasePackets = loadBSON("dot-seq-erase.json").length
+        syncPackets = loadBSON("dot-seq-sync.json").length
+
         printBenchmark("single draw and erase [sequential]", ITERATIONS, {
           addPath: {
             timeLoc: addLocTime,
-            packets: addPackets.length,
+            packets: addPackets,
             size: addSize,
             timeRem: addRemTime,
             events: addEvents,
           },
           extendErasureIntervals: {
             timeLoc: eraseLocTime,
-            packets: erasePackets.length,
+            packets: erasePackets,
             size: eraseSize,
             timeRem: eraseRemTime,
             events: eraseEvents,
           },
           synchronisation: {
             timeLoc: syncLocTime,
-            packets: syncPackets.length,
+            packets: syncPackets,
             size: syncSize,
             timeRem: syncRemTime,
             events: syncEvents,
@@ -318,7 +432,7 @@ describe("drawing app mesh", () => {
       })
   })
 
-  it("benchmarks a single draw and erase update in parallel", () => {
+  /*it("benchmarks a single draw and erase update in parallel", () => {
     const ITERATIONS = 1000
 
     jest.setTimeout(ITERATIONS * 20)
@@ -1480,5 +1594,5 @@ describe("drawing app mesh", () => {
             resolve()
           }),
       )
-  })
+  })*/
 })