From 8474a85ed2af846d6f020520bcc36422331bdc06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mats=20T=C3=B6rnberg?= Date: Tue, 22 Sep 2026 11:58:11 +0200 Subject: [PATCH] fix(spatial): decorrelate spawn radius from angle to stop avatar overlap The previous spawn fix (radius 20 -> 6) traded one problem for another: hashing only the angle onto a small fixed-radius ring means two names whose hashes land near the same angle bucket (only 360 of them) now separate by just a few pixels of arc instead of dozens, since arc length scales with radius. Confirmed against the actual guest names from a live session (guest-pxj6/guest-rd7o): old algorithm placed them ~11 room units apart, easily close enough for their ~34px graph nodes to visually merge into what looked like a single dot. Now the radius is hashed independently from the angle (8-19 units), so an angle collision no longer implies a full positional overlap. Same test names now land ~27.6 units apart. Still well within the range that keeps audio at full gain (refDistance=4). Co-Authored-By: Claude Sonnet 5 --- public/app.js | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/public/app.js b/public/app.js index 8b0d292..8738743 100644 --- a/public/app.js +++ b/public/app.js @@ -1524,17 +1524,27 @@ setInterval(() => { } }, 5000); -// Spawn me slightly off-center so two people don't land exactly on top of -// each other — small radius on purpose: at refDistance=4/rolloffFactor=1.4, -// the old radius-20 ring put name-hash-unlucky pairs up to 40 units apart -// (diametrically opposite), which is already down to ~7% audio gain before -// anyone's said a word. +// Spawn me slightly off-center so people don't land on top of each other. +// A single hashed angle on a *fixed* radius ring (the previous approach) +// puts anyone whose name hashes to a nearby angle almost exactly on top of +// each other too — on a small ring, "nearby angle" is only a few pixels of +// arc, and with just 360 angle buckets that's common (especially with the +// same recurring guest-xxxx test names). Hashing the radius independently +// from the angle means an angle collision no longer implies a full overlap. +// Both stay small enough that at refDistance=4/rolloffFactor=1.4 (see +// updatePanner) nobody starts out attenuated — this is purely about the +// dots being visually distinguishable, not audio range. (() => { - let h = 0; - for (const c of S.myId || S.name) h = (h * 33 + c.charCodeAt(0)) >>> 0; - const a = (h % 360) * (Math.PI / 180); - S.me.x = 50 + 6 * Math.cos(a); - S.me.y = 50 + 6 * Math.sin(a); + const key = S.myId || S.name; + let hAngle = 0, hRadius = 0; + for (const c of key) { + hAngle = (hAngle * 33 + c.charCodeAt(0)) >>> 0; + hRadius = (hRadius * 17 + c.charCodeAt(0) + 7) >>> 0; + } + const a = (hAngle % 360) * (Math.PI / 180); + const r = 8 + (hRadius % 12); // 8..19 room units + S.me.x = 50 + r * Math.cos(a); + S.me.y = 50 + r * Math.sin(a); })(); window.addEventListener('keydown', (ev) => {