From 0f8d1ec9ee21f54ce1c0b43474f85a8675072015 Mon Sep 17 00:00:00 2001 From: jessikitty Date: Thu, 20 Aug 2026 10:39:04 +1000 Subject: [PATCH] =?UTF-8?q?tune:=20idle=20float=20scaled=20to=20the=20real?= =?UTF-8?q?=20display=20=E2=80=94=203mm=20bob=20(was=206cm),=20matching=20?= =?UTF-8?q?shimmer=20for=20wander=20and=20path=20modes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/js/ghosts/behavior.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/public/js/ghosts/behavior.js b/public/js/ghosts/behavior.js index 6adcf49..2256323 100644 --- a/public/js/ghosts/behavior.js +++ b/public/js/ghosts/behavior.js @@ -4,6 +4,14 @@ */ import * as THREE from 'three'; +/* Idle float amplitudes, in cm. The display is a ~25cm table with ~4cm ghosts, so + * these are deliberately tiny — a few millimetres reads as "hovering" at this + * scale, while anything larger looks like the ghost is bouncing. Per-ghost + * overrides: rec.behavior.bobAmp / bobHz. */ +const BOB_AMP_CM = 0.3; // 3mm +const BOB_HZ = 0.4; +const SHIMMER_CM = 0.3; // secondary float used by wander + path modes + function hashNoise(seed, k) { // cheap deterministic pseudo-noise in [-1, 1] const x = Math.sin(seed * 127.1 + k * 311.7) * 43758.5453; @@ -27,7 +35,7 @@ export function ghostTransform(rec, nowMs, out) { base.x + R * 0.9 * Math.sin(ph * 1.0 + hashNoise(s, 1) * 6.28) * 0.7 + R * 0.4 * Math.sin(ph * 2.3 + hashNoise(s, 2) * 6.28) * 0.3, base.y + (b.vertical ?? 10) * Math.sin(t * v * 1.3 + hashNoise(s, 3) * 6.28) - + 3 * Math.sin(t * 1.7 + hashNoise(s, 6) * 6.28), + + SHIMMER_CM * Math.sin(t * 1.7 + hashNoise(s, 6) * 6.28), base.z + R * 0.9 * Math.cos(ph * 0.8 + hashNoise(s, 4) * 6.28) * 0.7 + R * 0.4 * Math.cos(ph * 1.9 + hashNoise(s, 5) * 6.28) * 0.3 ); @@ -40,8 +48,8 @@ export function ghostTransform(rec, nowMs, out) { const dir = ahead(t + eps).sub(ahead(t)); out.rotationY = Math.atan2(dir.x, dir.z); } else { - const amp = b.bobAmp ?? 6; // cm - const hz = b.bobHz ?? 0.4; + const amp = b.bobAmp ?? BOB_AMP_CM; + const hz = b.bobHz ?? BOB_HZ; out.position.set(base.x, base.y + amp * Math.sin(t * hz * Math.PI * 2), base.z); out.rotationY = 0.25 * Math.sin(t * 0.3); // slow idle sway } @@ -91,6 +99,6 @@ function pathTransform(b, t, out) { const dx = ahead.x - out.position.x, dz = ahead.z - out.position.z; if (dx * dx + dz * dz > 1e-4) out.rotationY = Math.atan2(dx, dz); // gentle float so walking still reads ghostly - out.position.y += 2 * Math.sin(t * 1.9 + (b.seed || 0)); + out.position.y += SHIMMER_CM * Math.sin(t * 1.9 + (b.seed || 0)); } const _look = new THREE.Vector3();