diff --git a/2023-12_usb-real-data-rates.md b/2023-12_usb-real-data-rates.md
index f1343b3854f4057f51c5a5616446fe4a175de798..3301a2e4b64f713beda1b063b94db26d5e463055 100644
--- a/2023-12_usb-real-data-rates.md
+++ b/2023-12_usb-real-data-rates.md
@@ -90,6 +90,17 @@ For future-architecture, my assumption is that I would do something like... one
 
 So, I presume this will be a lot heavier-handed programming wise, I'll put a stake down before carrying on. Neil also reminded me that Urumbu has implemented multiprocessing, so I should take a look at that as well. 
 
+So, here's the spin-up multiproc:
+
+![01](images/2023-12-27_multi-01.png)
+![02](images/2023-12-27_multi-02.png)
+![03](images/2023-12-27_multi-03.png)
+![04](images/2023-12-27_multi-04.png)
+
+So - clearly the winner actually, althought the gains are small. We see tighter distributions, no fly-aways at the right side of the plot, and consistently a-little-bit-faster reception. 
+
+That's about enough of this for me, then - it will be multiprocessing with queues, that should be simple enough to spin up. I will get on with an ethernet test suite next, and then on to UART/PIO links, which I suspect will be (surprising!) faster than these USB links. 
+
 ### Due Dilligence on Serial Sources
 
 The underlying 0.4MBit/s is pretty underwhelming, I should:
diff --git a/code/serial_multi_sink_multiprocessing/cobs_usb_serial_multi.py b/code/serial_multi_sink_multiprocessing/cobs_usb_serial_multi.py
new file mode 100644
index 0000000000000000000000000000000000000000..5cb79a4c0d7c4af5e92c688dc95c38c52ba4ffc3
--- /dev/null
+++ b/code/serial_multi_sink_multiprocessing/cobs_usb_serial_multi.py
@@ -0,0 +1,58 @@
+from cobs import cobs
+import serial
+
+# since multiproc takes functions as targets, 
+# we'll org like this:
+
+def serial_process(port, queue):
+  try:
+    ser = serial.Serial(port, baudrate=115200, timeout = 1)
+    buffer = bytearray()
+
+    while True:
+      byte = ser.read(1)
+      if not byte:
+        continue
+      if byte == b"\x00":
+        if len(buffer) > 0:
+          data = cobs.decode(buffer)
+          buffer = bytearray() 
+          queue.put(data)
+        else: 
+          continue 
+      else:
+        buffer += byte
+  except serial.SerialException as e:
+    print(f"Serial exception on {port}: {e}")
+
+
+# class CobsUsbSerial:
+#   def __init__(self, port, baudrate=115200):
+#     self.port = port
+#     self.ser = serial.Serial(port, baudrate=baudrate, timeout=1)
+#     self.buffer = bytearray()
+
+#   def write(self, data: bytes):
+#     data_enc = cobs.encode(data) + b"\x00"
+#     self.ser.write(data_enc)
+
+#   def read(self):
+#     byte = self.ser.read(1)
+#     if not byte:
+#       return 
+#     if byte == b"\x00":
+#       if len(self.buffer) > 0:
+#         data = cobs.decode(self.buffer)
+#         self.buffer = bytearray() 
+#         return data
+#       else: 
+#         return 
+#     else:
+#       self.buffer += byte 
+
+#   async def attach(self, rx_func):
+#     while True:
+#       bts = self.read()
+#       if bts:
+#         rx_func(bts)
+#       await asyncio.sleep(0)
diff --git a/code/serial_multi_sink_multiprocessing/multi_sink_multiprocessing.py b/code/serial_multi_sink_multiprocessing/multi_sink_multiprocessing.py
new file mode 100644
index 0000000000000000000000000000000000000000..5043610c2e6b9b96a7d56d2658c8e82bcd270f2b
--- /dev/null
+++ b/code/serial_multi_sink_multiprocessing/multi_sink_multiprocessing.py
@@ -0,0 +1,48 @@
+from cobs_usb_serial_multi import serial_process
+from plot_stamps import plot_stamps 
+import struct, multiprocessing, time 
+import numpy as np 
+
+stamp_count = 1000
+pck_len = 128
+
+if __name__ == "__main__":
+  ports = ["COM23", "COM31", "COM33", "COM36"]
+
+  queues = [multiprocessing.Queue() for _ in ports]
+
+  processes = [multiprocessing.Process(target=serial_process, args=(port, queue)) for port, queue in zip(ports, queues)]
+
+  stamps = [np.zeros(stamp_count) for _ in ports]
+  stamps_lengths = [0 for _ in ports]
+  plot_states = [False for _ in ports]
+
+  for p in processes:
+    p.start()
+
+  try:
+    # infinite loop over process-comm objects; 
+    while True:
+      for q, queue in enumerate(queues):
+        if not queue.empty():
+          data = queue.get()
+          if len(data) == pck_len:
+            if(stamps_lengths[q] >= stamp_count):
+              continue
+            # unpack each as an integer stamp and store in stamps 
+            stamp = struct.unpack("=I", data[:4])[0]
+            stamps[q][stamps_lengths[q]] = stamp
+            stamps_lengths[q] += 1
+            # plot, if the list is done: 
+            if stamps_lengths[q] >= stamp_count and not plot_states[q]:
+              plot_states[q] = True
+              plot_stamps(stamps[q], stamp_count, pck_len)
+      
+      # to let the processor chill for 1us 
+      time.sleep(0.000001)
+  except KeyboardInterrupt:
+    print("halting...")
+  
+  for p in processes:
+    p.terminate()
+    p.join()
diff --git a/code/serial_multi_sink_multiprocessing/plot_stamps.py b/code/serial_multi_sink_multiprocessing/plot_stamps.py
new file mode 100644
index 0000000000000000000000000000000000000000..856f35e1dc3d350431c1961d5b96d32ba5c97083
--- /dev/null
+++ b/code/serial_multi_sink_multiprocessing/plot_stamps.py
@@ -0,0 +1,43 @@
+import pandas as pd 
+import matplotlib.pyplot as plt 
+
+def plot_stamps(stamps, stamp_count, pck_len):
+  # make df from stamps 
+  df = pd.DataFrame({'timestamps': stamps})
+
+  # calculate deltas between stamps 
+  df['deltas'] = df['timestamps'].diff() 
+
+  # clean NaN's 
+  df = df.dropna()
+
+  # wipe obviously-wrong deltas (i.e. the 1st, which goes 0-start-us) 
+  df = df[df['deltas'] < 100000]
+
+  # Plotting
+  fig, ax1 = plt.subplots(figsize=(11, 3))
+
+  ax1.set_xlim([1750, 2750])
+
+  # Primary x-axis (time deltas)
+  df['deltas'].plot(kind='hist', bins=100, ax=ax1)
+  ax1.set_xlabel('Time-Stamp Deltas (us) and equivalent (MBits/s)')
+  ax1.set_ylabel(f'Frequency (of {stamp_count})')
+
+  # get axis ticks to calculate equivalent bandwidths 
+  x_ticks = ax1.get_xticks()
+  ax1.set_xticks(x_ticks)
+  bandwidths = [((pck_len * 8) * (1e6 / x)) / 1e6 for x in x_ticks]
+  ticks = [] 
+
+  for i in range(len(x_ticks)):
+    print(i, x_ticks[i], bandwidths[i]) 
+    ticks.append(f"{x_ticks[i]:.0f} ({bandwidths[i]:.3f})")
+
+  ax1.set_xticklabels(ticks)
+
+  plt.title(f'Single-Source COBS Data Sink Deltas, pck_len={pck_len}')
+
+  plt.tight_layout()
+
+  plt.show()
\ No newline at end of file
diff --git a/code/serial_multi_sink_multiprocessing/serial_list.py b/code/serial_multi_sink_multiprocessing/serial_list.py
new file mode 100644
index 0000000000000000000000000000000000000000..5f25377227f77c79dc2db925a16f5a5a280f412b
--- /dev/null
+++ b/code/serial_multi_sink_multiprocessing/serial_list.py
@@ -0,0 +1,20 @@
+import serial.tools.list_ports
+
+def list_serial_ports():
+    ports = serial.tools.list_ports.comports()
+    for port in ports:
+        print(f"Port: {port.device}")
+        print(f" - Description: {port.description}")
+        if port.serial_number:
+            print(f" - Serial Number: {port.serial_number}")
+        if port.manufacturer:
+            print(f" - Manufacturer: {port.manufacturer}")
+        if port.product:
+            print(f" - Product: {port.product}")
+        if port.vid is not None:
+            print(f" - VID: {port.vid:04X}")
+        if port.pid is not None:
+            print(f" - PID: {port.pid:04X}")
+        print()
+
+list_serial_ports()
diff --git a/images/2023-12-27_multi-01.png b/images/2023-12-27_multi-01.png
new file mode 100644
index 0000000000000000000000000000000000000000..443ac82ce46b966565ffc93e3771ff5e1fefd872
Binary files /dev/null and b/images/2023-12-27_multi-01.png differ
diff --git a/images/2023-12-27_multi-02.png b/images/2023-12-27_multi-02.png
new file mode 100644
index 0000000000000000000000000000000000000000..f0db63f0407a8cc7487f49f4133f9c34cab5bbc8
Binary files /dev/null and b/images/2023-12-27_multi-02.png differ
diff --git a/images/2023-12-27_multi-03.png b/images/2023-12-27_multi-03.png
new file mode 100644
index 0000000000000000000000000000000000000000..996114ab9190ab70de576c9498cb66658494013e
Binary files /dev/null and b/images/2023-12-27_multi-03.png differ
diff --git a/images/2023-12-27_multi-04.png b/images/2023-12-27_multi-04.png
new file mode 100644
index 0000000000000000000000000000000000000000..46cbd848966e6948083386b452801462a2d697bb
Binary files /dev/null and b/images/2023-12-27_multi-04.png differ
diff --git a/images/2023-12-27_multi-test.png b/images/2023-12-27_multi-test.png
new file mode 100644
index 0000000000000000000000000000000000000000..88bd037ddda4bef1fc16622c1e4ef4c6cffc5744
Binary files /dev/null and b/images/2023-12-27_multi-test.png differ
diff --git a/images/2023-12-27_multi-unp-01.png b/images/2023-12-27_multi-unp-01.png
new file mode 100644
index 0000000000000000000000000000000000000000..8f2b573a44028c8f1a335495dbc83c56c36dd9f9
Binary files /dev/null and b/images/2023-12-27_multi-unp-01.png differ
diff --git a/images/2023-12-27_multi-unp-02.png b/images/2023-12-27_multi-unp-02.png
new file mode 100644
index 0000000000000000000000000000000000000000..45328c68efc51fe2dd44dded03c2dda6e31a7fe8
Binary files /dev/null and b/images/2023-12-27_multi-unp-02.png differ
diff --git a/images/2023-12-27_multi-unp-03.png b/images/2023-12-27_multi-unp-03.png
new file mode 100644
index 0000000000000000000000000000000000000000..5f0fa9c113526db1c88f30aab4514f5abf4e843a
Binary files /dev/null and b/images/2023-12-27_multi-unp-03.png differ
diff --git a/images/2023-12-27_multi-unp-04.png b/images/2023-12-27_multi-unp-04.png
new file mode 100644
index 0000000000000000000000000000000000000000..98f506ea8c8ad27e49c851b43435cbe511aa8828
Binary files /dev/null and b/images/2023-12-27_multi-unp-04.png differ