[object Object]

← back to Quadrille Showroom

Perf + PJ-accurate isolate: hero stays AMID the sliver arc (not hidden); only hero casts shadow; shared board geometry + cached placards + shadow-rebake budget → 2min FPS 57/68, 0 errors, specs intact

f75fcc78a34166b7ef0d76883eedb824efdd2bc9 · 2026-06-28 07:50:14 -0700 · Steve

Files touched

Diff

commit f75fcc78a34166b7ef0d76883eedb824efdd2bc9
Author: Steve <steve@designerwallcoverings.com>
Date:   Sun Jun 28 07:50:14 2026 -0700

    Perf + PJ-accurate isolate: hero stays AMID the sliver arc (not hidden); only hero casts shadow; shared board geometry + cached placards + shadow-rebake budget → 2min FPS 57/68, 0 errors, specs intact
---
 public/js/showroom.js | 31 ++++++++++++++++++++-----------
 public/js/versions.js |  6 ++++--
 2 files changed, 24 insertions(+), 13 deletions(-)

diff --git a/public/js/showroom.js b/public/js/showroom.js
index 3aaa444..32b64d7 100644
--- a/public/js/showroom.js
+++ b/public/js/showroom.js
@@ -752,7 +752,7 @@ function buildLighting() {
   key.position.set(2.4, CONFIG.room.height + 1.4, 2.2);
   key.target.position.set(0, 1.0, -CONFIG.room.depth / 2);
   key.castShadow = true;
-  key.shadow.mapSize.set(2048, 2048);            // 4× the texels → crisp-yet-soft contact shadows
+  key.shadow.mapSize.set(1536, 1536);            // crisp-yet-soft; smaller than 2048 for rebake headroom
   const sc = key.shadow.camera;
   sc.near = 0.5; sc.far = 14; sc.left = -5; sc.right = 5; sc.top = 4; sc.bottom = -3;
   key.shadow.bias = -0.0004;
@@ -1160,7 +1160,11 @@ function buildWingWall() {
     // raycast + loadWingBook plumbing keeps working with no per-leaf split.)
     const face = new THREE.Mesh(BG.face, poolMat);
     face.position.set(faceOffX, yC, 0);
-    face.castShadow = true; face.receiveShadow = true;
+    // Closed packed boards DON'T cast into the shadow map — their contact-shadow sprite
+    // grounds them, and casting 18 tightly-packed 6-ft boards doubles every shadow rebake
+    // (key+pic lights) for almost no visible gain. Only the OPEN hero casts (toggled on
+    // focus in isolateBoard). Big nav-FPS win; the rack still reads grounded.
+    face.castShadow = false; face.receiveShadow = true;
     face.userData = { isWingFace: true, parentPivot: pivot, leaf: 'F' };
     pivot.add(face);
     // Per-board light gradient + sheen overlay, tracking the single face.
@@ -1587,21 +1591,26 @@ function focusOnWing(pivot) {
   document.getElementById('info-text').textContent = `${product.pattern_name || product.name} · ${product.vendor}  |  ◀ Back / Next ▶ to flip through`;
 }
 
-// Isolate the focused board — hide the others so the open design is presented alone,
-// dead-centre. Restored on exit / window rebuild.
+// Isolate the focused board — the PJ way: the hero swings OPEN and presents its design,
+// while the rest of the arc STAYS VISIBLE as the packed sliver backdrop (NOT hidden) —
+// exactly the reference photo (open boards amid the fanned slivers). The hero is the
+// only board that casts into the shadow map (toggled here), so it reads as a real
+// lifted-forward panel without the cost of 18 cast-shadow boards.
 function isolateBoard(pivot) {
-  if (window._requestShadowUpdate) window._requestShadowUpdate(6); // visibility changed → rebake
+  if (window._requestShadowUpdate) window._requestShadowUpdate(6);
   wingBoards.forEach(p => {
-    p.visible = (p === pivot);
-    // Hide every wall drop-shadow in focus/hero mode: the other boards are hidden (their
-    // shadow would float), and the focused board swings forward off the wall (so it no
-    // longer casts directly behind itself). They come back in restoreBoards().
+    p.visible = true;                              // keep the sliver arc — do NOT hide
+    if (p.userData.face) p.userData.face.castShadow = (p === pivot);   // only the hero casts
     if (p.userData.wallDrop) p.userData.wallDrop.visible = false;
   });
 }
 function restoreBoards() {
-  wingBoards.forEach(p => { p.visible = true; if (p.userData.wallDrop) p.userData.wallDrop.visible = true; });
-  if (window._requestShadowUpdate) window._requestShadowUpdate(6); // visibility changed → rebake
+  wingBoards.forEach(p => {
+    p.visible = true;
+    if (p.userData.face) p.userData.face.castShadow = false;           // back to no-cast slivers
+    if (p.userData.wallDrop) p.userData.wallDrop.visible = false;      // arc has no flat wall behind
+  });
+  if (window._requestShadowUpdate) window._requestShadowUpdate(6);
 }
 
 // Next / Back — flip to the next/previous wingboard (pages window edges, endless).
diff --git a/public/js/versions.js b/public/js/versions.js
index 97e81ba..44161fe 100644
--- a/public/js/versions.js
+++ b/public/js/versions.js
@@ -643,8 +643,10 @@ function boot() {
   const safeBoot = { V1: 1, V6: 1, V7: 1, V8: 1, V9: 1, V10: 1 };
   const saved = localStorage.getItem('qh_version');
   const bootKey = (saved && VERSIONS[saved] && safeBoot[saved]) ? saved : 'V1';
-  // Defer the build a beat so viewmodes.js has finished its own boot (it sets Hero).
-  setTimeout(() => setVersion(bootKey, true), 900);
+  // Defer the build so BOTH the showroom's guided auto-focus (~700ms) AND viewmodes.js's
+  // boot ('hero', ~1200ms) have settled — then the version build OWNS the final scene
+  // state (otherwise the guided focus re-isolates the boards under V1's establishing shot).
+  setTimeout(() => setVersion(bootKey, true), 1800);
 
   // Overlay restore (default OFF — the locked simple boot).
   if (localStorage.getItem('qh_overlay') === '1') setTimeout(() => setOverlay(true), 1400);

← fecd0e8 auto-save: 2026-06-28T07:43:27 (4 files) — public/css/showro  ·  back to Quadrille Showroom  ·  V1 establishing arc (matches pj-dallas-showroom.png) + V2 st 5954106 →