diff --git a/src/app.js b/src/app.js
index 113f96e93883308fc4188b757b8e7b16e3a1adde..d23b052a5549939affeb9521c51635d0513bd24d 100644
--- a/src/app.js
+++ b/src/app.js
@@ -1,13 +1,34 @@
 // Room connection and synchronisation.
-// Translate local canvas input events to draw messages and send to the room.
+// Translate local canvas input events to draw messages in light of current tool
+// selections and send to the room.
 // Get back room updates and invoke the local canvas renderer.
 
 import * as canvas from "./canvas.js"
 import * as HTML from "./elements.js"
 import { connect } from "./room.js"
+import * as toolSelection from "./tool-selection.js"
 
 const TEST_ROOM = "imperial"
 
+const MIN_PRESSURE_FACTOR = 0.1
+const MAX_PRESSURE_FACTOR = 1.5
+
+// This is a quadratic such that:
+// - getPressureFactor(0.0) = MIN_PRESSURE_FACTOR
+// - getPressureFactor(0.5) = 1.0
+// - getPressureFactor(1.0) = MAX_PRESSURE_FACTOR
+// For sensible results, maintain that:
+// - 0.0 <= MIN_PRESSURE_FACTOR <= 1.0
+// - 1.0 <= MAX_PRESSURE_FACTOR
+// For intuitive results, maintain that:
+// - MAX_PRESSURE_FACTOR <= ~2.0
+const getPressureFactor = (pressure) => {
+  const a = 2 * (MAX_PRESSURE_FACTOR + MIN_PRESSURE_FACTOR) - 4
+  const b = -MAX_PRESSURE_FACTOR - 3 * MIN_PRESSURE_FACTOR + 4
+  const c = MIN_PRESSURE_FACTOR
+  return a * pressure ** 2 + b * pressure + c
+}
+
 let room = null
 
 const onRoomConnect = (room_) => {
@@ -89,102 +110,8 @@ const tryRoomConnect = async (roomID) => {
     .catch((err) => alert(`Error connecting to a room:\n${err}`))
 }
 
-const ERASER_RADIUS = 10
-const tools = {
-  PEN: Symbol("pen"),
-  ERASER: Symbol("eraser"),
-}
-let currentTool = tools.PEN
 const pathIDsByPointerID = new Map()
 
-HTML.penButton.addEventListener("click", () => {
-  if (currentTool == tools.PEN) {
-    showElement(HTML.penProperties)
-  } else {
-    currentTool = tools.PEN
-    HTML.penButton.classList.add("selected")
-    HTML.eraserButton.classList.remove("selected")
-  }
-})
-
-HTML.closeButton.forEach((element) => {
-  element.addEventListener("click", () => {
-    hideElement(element.parentNode.parentNode.parentNode)
-  })
-})
-
-window.addEventListener("click", (event) => {
-  if (event.target == HTML.penProperties) {
-    hideElement(HTML.penProperties)
-  } else if (event.target == HTML.palette) {
-    hideElement(HTML.palette)
-    hideElement(HTML.penProperties)
-  }
-})
-
-HTML.rectangle.addEventListener("click", () => {
-  showElement(HTML.palette)
-})
-
-const svg = HTML.wheel.children
-for (let i = 1; i < svg.length; i++) {
-  svg[i].addEventListener("click", (event) => {
-    const paletteColour = event.target.getAttribute("fill")
-    HTML.rectangle.style.backgroundColor = paletteColour
-    HTML.picker.value = paletteColour
-    HTML.labelColours.style.backgroundColor = paletteColour
-    canvas.setStrokeColour(paletteColour)
-    hideElement(HTML.palette)
-  })
-}
-
-function showElement(element) {
-  element.style.display = "block"
-}
-
-function hideElement(element) {
-  element.style.display = "none"
-}
-
-HTML.picker.addEventListener("change", () => {
-  const paletteColour = event.target.value
-  HTML.rectangle.style.backgroundColor = paletteColour
-  HTML.labelColours.style.backgroundColor = paletteColour
-  canvas.setStrokeColour(paletteColour)
-})
-
-HTML.output.innerHTML = HTML.slider.value
-
-HTML.slider.oninput = function() {
-  HTML.output.innerHTML = this.value
-  canvas.setStrokeRadius(this.value / 10)
-}
-
-const x = window.matchMedia(
-  "only screen and (orientation: landscape) and (max-width: 600px)",
-)
-x.addListener(() => {
-  if (x.matches) {
-    HTML.wheel.setAttribute("viewBox", "-50 10 200 100")
-    HTML.palette.setAttribute("style", "padding-top: 50px")
-  } else {
-    HTML.wheel.setAttribute("viewBox", "0 10 100 100")
-  }
-})
-
-HTML.picker.addEventListener("change", () => {
-  const paletteColour = event.target.value
-  HTML.rectangle.style.backgroundColor = paletteColour
-  HTML.labelColours.style.backgroundColor = paletteColour
-  canvas.setStrokeColour(paletteColour)
-})
-
-HTML.eraserButton.addEventListener("click", () => {
-  currentTool = tools.ERASER
-  HTML.penButton.classList.remove("selected")
-  HTML.eraserButton.classList.add("selected")
-})
-
 HTML.peerButton.addEventListener("click", () => {
   const peerID = HTML.peerIDElem.value
   if (room == null || peerID == "") {
@@ -293,7 +220,7 @@ const erasePoint = ([x, y]) => {
   }
   room.getPaths().forEach((points, pathID) => {
     points.forEach((point, i) => {
-      if (getDistance([x, y], point) <= ERASER_RADIUS) {
+      if (getDistance([x, y], point) <= toolSelection.getEraseRadius()) {
         room.erasePoint(pathID, i)
       }
     })
@@ -304,19 +231,18 @@ canvas.input.addEventListener("strokestart", ({ detail: e }) => {
   if (room == null) {
     return
   }
-
+  const currentTool = toolSelection.getTool()
   const mousePos = [e.offsetX, e.offsetY]
-
-  if (currentTool == tools.PEN) {
+  if (currentTool == toolSelection.Tools.PEN) {
     pathIDsByPointerID.set(
       e.pointerId,
       room.addPath([
         ...mousePos,
-        canvas.getStrokeRadius(e.pressure),
-        canvas.getStrokeColour(),
+        toolSelection.getStrokeRadius() * getPressureFactor(e.pressure),
+        toolSelection.getStrokeColour(),
       ]),
     )
-  } else if (currentTool == tools.ERASER) {
+  } else if (currentTool == toolSelection.Tools.ERASER) {
     erasePoint(mousePos)
   }
 })
@@ -329,16 +255,15 @@ canvas.input.addEventListener("strokemove", ({ detail: e }) => {
   if (room == null) {
     return
   }
-
+  const currentTool = toolSelection.getTool()
   const mousePos = [e.offsetX, e.offsetY]
-
-  if (currentTool == tools.PEN) {
+  if (currentTool == toolSelection.Tools.PEN) {
     room.extendPath(pathIDsByPointerID.get(e.pointerId), [
       ...mousePos,
-      canvas.getStrokeRadius(e.pressure),
-      canvas.getStrokeColour(),
+      toolSelection.getStrokeRadius() * getPressureFactor(e.pressure),
+      toolSelection.getStrokeColour(),
     ])
-  } else if (currentTool == tools.ERASER) {
+  } else if (currentTool == toolSelection.Tools.ERASER) {
     erasePoint(mousePos)
   }
 })
diff --git a/src/canvas.js b/src/canvas.js
index 5aceb59fdb0b37a8a7b7b7749204d627a87f3b16..d04e6fa2f7ef3d03a9fbc041f622618010c2df25 100644
--- a/src/canvas.js
+++ b/src/canvas.js
@@ -16,9 +16,6 @@ const lineFn = line()
 
 const pathGroupElems = new Map()
 
-let strokeColour = "#0000ff"
-let strokeRadius = 5
-
 const MAX_POINT_DISTANCE = 5
 const MAX_RADIUS_DELTA = 0.05
 
@@ -168,38 +165,3 @@ canvas.addEventListener("pointerup", dispatchPointerEvent("strokeend"))
 canvas.addEventListener("pointerleave", dispatchPointerEvent("strokeend"))
 canvas.addEventListener("pointermove", dispatchPointerEvent("strokemove"))
 canvas.addEventListener("touchmove", (e) => e.preventDefault())
-
-export function setStrokeColour(colour) {
-  strokeColour = colour
-}
-
-export function getStrokeColour() {
-  return strokeColour
-}
-
-const MIN_PRESSURE_FACTOR = 0.1
-const MAX_PRESSURE_FACTOR = 1.5
-
-// This is a quadratic such that:
-// - getPressureFactor(0.0) = MIN_PRESSURE_FACTOR
-// - getPressureFactor(0.5) = 1.0
-// - getPressureFactor(1.0) = MAX_PRESSURE_FACTOR
-// For sensible results, maintain that:
-// - 0.0 <= MIN_PRESSURE_FACTOR <= 1.0
-// - 1.0 <= MAX_PRESSURE_FACTOR
-// For intuitive results, maintain that:
-// - MAX_PRESSURE_FACTOR <= ~2.0
-const getPressureFactor = (pressure) => {
-  const a = 2 * (MAX_PRESSURE_FACTOR + MIN_PRESSURE_FACTOR) - 4
-  const b = -MAX_PRESSURE_FACTOR - 3 * MIN_PRESSURE_FACTOR + 4
-  const c = MIN_PRESSURE_FACTOR
-  return a * pressure ** 2 + b * pressure + c
-}
-
-export function getStrokeRadius(pressure) {
-  return strokeRadius * getPressureFactor(pressure)
-}
-
-export function setStrokeRadius(radius) {
-  strokeRadius = radius
-}
diff --git a/src/tool-selection.js b/src/tool-selection.js
new file mode 100644
index 0000000000000000000000000000000000000000..1d2be7cd3dd4c1eba9823d2fe234ee0714a77c82
--- /dev/null
+++ b/src/tool-selection.js
@@ -0,0 +1,98 @@
+import * as HTML from "./elements.js"
+
+export const Tools = Object.freeze({
+  PEN: Symbol("pen"),
+  ERASER: Symbol("eraser"),
+})
+
+let tool = Tools.PEN
+let strokeColour = "#0000ff"
+let strokeRadius = 5
+// TODO: The erase radius should also be selectable.
+const ERASE_RADIUS = 10
+
+export const getTool = () => tool
+export const getStrokeColour = () => strokeColour
+export const getStrokeRadius = () => strokeRadius
+export const getEraseRadius = () => ERASE_RADIUS
+
+const showElement = (element) => {
+  element.style.display = "block"
+}
+
+const hideElement = (element) => {
+  element.style.display = "none"
+}
+
+HTML.penButton.addEventListener("click", () => {
+  if (tool == Tools.PEN) {
+    showElement(HTML.penProperties)
+  } else {
+    tool = Tools.PEN
+    HTML.penButton.classList.add("selected")
+    HTML.eraserButton.classList.remove("selected")
+  }
+})
+
+HTML.eraserButton.addEventListener("click", () => {
+  tool = Tools.ERASER
+  HTML.penButton.classList.remove("selected")
+  HTML.eraserButton.classList.add("selected")
+})
+
+HTML.picker.addEventListener("change", () => {
+  const paletteColour = event.target.value
+  HTML.rectangle.style.backgroundColor = paletteColour
+  HTML.labelColours.style.backgroundColor = paletteColour
+  strokeColour = paletteColour
+})
+
+HTML.slider.oninput = function() {
+  HTML.output.innerHTML = this.value
+  strokeRadius = this.value / 10
+}
+
+HTML.output.innerHTML = HTML.slider.value
+
+const x = window.matchMedia(
+  "only screen and (orientation: landscape) and (max-width: 600px)",
+)
+x.addListener(() => {
+  if (x.matches) {
+    HTML.wheel.setAttribute("viewBox", "-50 10 200 100")
+    HTML.palette.setAttribute("style", "padding-top: 50px")
+  } else {
+    HTML.wheel.setAttribute("viewBox", "0 10 100 100")
+  }
+})
+
+HTML.closeButton.forEach((element) => {
+  element.addEventListener("click", () => {
+    hideElement(element.parentNode.parentNode.parentNode)
+  })
+})
+
+window.addEventListener("click", (event) => {
+  if (event.target == HTML.penProperties) {
+    hideElement(HTML.penProperties)
+  } else if (event.target == HTML.palette) {
+    hideElement(HTML.palette)
+    hideElement(HTML.penProperties)
+  }
+})
+
+HTML.rectangle.addEventListener("click", () => {
+  showElement(HTML.palette)
+})
+
+const svg = HTML.wheel.children
+for (let i = 1; i < svg.length; i++) {
+  svg[i].addEventListener("click", (event) => {
+    const paletteColour = event.target.getAttribute("fill")
+    HTML.rectangle.style.backgroundColor = paletteColour
+    HTML.picker.value = paletteColour
+    HTML.labelColours.style.backgroundColor = paletteColour
+    strokeColour = paletteColour
+    hideElement(HTML.palette)
+  })
+}