fix(spatial): decorrelate spawn radius from angle to stop avatar overlap
Build and Publish / Metadata (push) Successful in 1s
Build and Publish / BuildAndDeployAmd64 (push) Successful in 51s

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 <noreply@anthropic.com>
This commit is contained in:
matsandClaude Sonnet 5 committed 2026-09-22 11:58:11 +02:00
1 parent f68e77472e
commit 8474a85ed2
1 file changed
+20 -10
+20 -10
View File
@@ -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) => {