From 42bd388293d68045f4e305c7fb52144b57b6f81e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 8 May 2026 14:00:07 +0000 Subject: [PATCH] Fix accumulating canvas/window event listeners on each run createEngine() is called on every code run (engine is disposed to null between runs), causing touchend, keydown, keyup, blur, and pointerup handlers to stack up indefinitely with no way to remove them. Guard registration with a one-time flag; the handlers are safe to reuse across runs since they access flock.scene lazily at call time. https://claude.ai/code/session_01QYFwXKPZrjm2RxVFKcKNvA --- flock.js | 90 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/flock.js b/flock.js index 23032946..06bb499f 100644 --- a/flock.js +++ b/flock.js @@ -1273,55 +1273,59 @@ export const flock = { console.error("Error initializing CSG2:", error); } - flock.canvas.addEventListener( - "touchend", - (event) => { - if (event.touches.length === 0) { - const input = flock.scene.activeCamera.inputs?.attached?.pointers; - // Add null check for input itself - if ( - input && - (input._pointA !== null || - input._pointB !== null || - input._isMultiTouch === true) - ) { - flock._hardResetCameraControls(flock.scene.activeCamera, { - reattachDelayMs: 100, - noPreventDefault: true, - }); + if (!flock._canvasListenersRegistered) { + flock._canvasListenersRegistered = true; + + flock.canvas.addEventListener( + "touchend", + (event) => { + if (event.touches.length === 0) { + const input = flock.scene.activeCamera.inputs?.attached?.pointers; + // Add null check for input itself + if ( + input && + (input._pointA !== null || + input._pointB !== null || + input._isMultiTouch === true) + ) { + flock._hardResetCameraControls(flock.scene.activeCamera, { + reattachDelayMs: 100, + noPreventDefault: true, + }); + } } - } - }, - { passive: false }, - ); + }, + { passive: false }, + ); - flock.canvas.addEventListener("keydown", function (event) { - flock.canvas.currentKeyPressed = event.key; - flock.canvas.pressedKeys.add(event.key); - }); + flock.canvas.addEventListener("keydown", function (event) { + flock.canvas.currentKeyPressed = event.key; + flock.canvas.pressedKeys.add(event.key); + }); - flock.canvas.addEventListener("keyup", function (event) { - flock.canvas.pressedKeys.delete(event.key); - }); + flock.canvas.addEventListener("keyup", function (event) { + flock.canvas.pressedKeys.delete(event.key); + }); - flock.canvas.addEventListener("blur", () => { - // Clear all pressed keys when window loses focus - flock.canvas.pressedKeys.clear(); - flock.canvas.pressedButtons.clear(); - flock._hardResetCameraControls(flock.scene?.activeCamera); - }); + flock.canvas.addEventListener("blur", () => { + // Clear all pressed keys when window loses focus + flock.canvas.pressedKeys.clear(); + flock.canvas.pressedButtons.clear(); + flock._hardResetCameraControls(flock.scene?.activeCamera); + }); - // Hardening for rare pointer/input desync where camera keeps moving - // after input ends or browser focus changes. - window.addEventListener("blur", () => { - flock.canvas.pressedKeys.clear(); - flock.canvas.pressedButtons.clear(); - flock._hardResetCameraControls(flock.scene?.activeCamera); - }); + // Hardening for rare pointer/input desync where camera keeps moving + // after input ends or browser focus changes. + window.addEventListener("blur", () => { + flock.canvas.pressedKeys.clear(); + flock.canvas.pressedButtons.clear(); + flock._hardResetCameraControls(flock.scene?.activeCamera); + }); - window.addEventListener("pointerup", () => { - flock._hardResetCameraControls(flock.scene?.activeCamera); - }); + window.addEventListener("pointerup", () => { + flock._hardResetCameraControls(flock.scene?.activeCamera); + }); + } flock.engineReady = true; },