[object Object]

← back to Dead Agentabrams

Backdrop: confine to the stage-riser area with live X/Y/W/H controls

f5a3143bc451e456990816b0bf80a0e8f5001788 · 2026-09-03 11:57:02 -0700 · Steve Abrams

Steve: "put behind my graphics!! on the bg on stage only" / "the area
in back of where singers would sing" / "allow me to expand to show
how it's done."

Two fixes:
1. playConcertManual() was unconditionally entering yt-hero (the
   full-screen "video IS the show" mode reserved for the real
   archive.org-down fallback) for every Videos-panel pick too, which
   is why "behind the art" never actually looked behind anything —
   archive.org is still down right now, so every pick went full-hero.
   Manual picks now stay muted + confined to the small backdrop box;
   yt-hero stays exclusive to enterYouTubeFallback's real outage path.
2. Replaced the single "Backdrop size" scale slider with 4 controls
   (Left/Top/Width/Height, --vr-bd-x/-y/-w/-h) plus a reset button, so
   Steve can position the box himself instead of me guessing canvas
   coordinates. Default widened (46vw x32vh) so the video peeks out
   around the opaque stage-riser graphic rather than being fully
   hidden behind it edge-to-edge.

Files touched

Diff

commit f5a3143bc451e456990816b0bf80a0e8f5001788
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 3 11:57:02 2026 -0700

    Backdrop: confine to the stage-riser area with live X/Y/W/H controls
    
    Steve: "put behind my graphics!! on the bg on stage only" / "the area
    in back of where singers would sing" / "allow me to expand to show
    how it's done."
    
    Two fixes:
    1. playConcertManual() was unconditionally entering yt-hero (the
       full-screen "video IS the show" mode reserved for the real
       archive.org-down fallback) for every Videos-panel pick too, which
       is why "behind the art" never actually looked behind anything —
       archive.org is still down right now, so every pick went full-hero.
       Manual picks now stay muted + confined to the small backdrop box;
       yt-hero stays exclusive to enterYouTubeFallback's real outage path.
    2. Replaced the single "Backdrop size" scale slider with 4 controls
       (Left/Top/Width/Height, --vr-bd-x/-y/-w/-h) plus a reset button, so
       Steve can position the box himself instead of me guessing canvas
       coordinates. Default widened (46vw x32vh) so the video peeks out
       around the opaque stage-riser graphic rather than being fully
       hidden behind it edge-to-edge.
---
 room/index.html | 109 ++++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 74 insertions(+), 35 deletions(-)

diff --git a/room/index.html b/room/index.html
index f1884e2..4776bb4 100644
--- a/room/index.html
+++ b/room/index.html
@@ -24,20 +24,24 @@
   #stage{position:fixed;inset:0;display:block}
   canvas{position:absolute;inset:0;width:100%;height:100%}
   #orb{z-index:4;pointer-events:none}
-  /* ---- Concert-video backdrop (TK-11178): official GD footage behind the generative scene ---- */
-  #venueVideo{position:absolute;inset:0;overflow:hidden;z-index:0;pointer-events:none;background:#000;opacity:0;transition:opacity 1.4s ease}
-  #venueVideo iframe,#venueVideo>div{position:absolute;top:50%;left:50%;
-    transform:translate(-50%,-50%) scale(var(--vr-bd-scale,1));   /* size dialable from the Videos panel (TK-11178) */
-    width:100vw;height:56.25vw;min-height:100vh;min-width:177.78vh;border:0;pointer-events:none}
+  /* ---- Concert-video backdrop (TK-11178): official GD footage behind the generative scene.
+     Confined to a positioned box (Steve: "the area in back of where singers would sing") —
+     defaults roughly over the stage riser, fully dialable via the Videos panel's X/Y/W/H
+     sliders (--vr-bd-x/-y/-w/-h). Contain-fit inside that box — never cropped into a close-up. ---- */
+  #venueVideo{position:absolute;
+    left:var(--vr-bd-x, 50%); top:var(--vr-bd-y, 37%);
+    width:var(--vr-bd-w, 46vw); height:var(--vr-bd-h, 32vh);
+    transform:translate(-50%,-50%);
+    overflow:hidden;z-index:0;pointer-events:none;background:#000;opacity:0;
+    transition:opacity 1.4s ease,left .15s,top .15s,width .15s,height .15s;border-radius:3px}
+  #venueVideo iframe,#venueVideo>div{position:absolute;inset:0;width:100%;height:100%;border:0;pointer-events:none}
   #stage.video-bg #venueVideo{opacity:1}
-  #stage.video-bg #sky{opacity:.12}
-  #stage.video-bg #scene{opacity:.42}
-  /* YouTube fallback = the video IS the show → let the concert footage dominate the frame */
+  #stage.video-bg #sky{opacity:.20}
+  #stage.video-bg #scene{opacity:.85}
+  /* YouTube fallback = the video IS the show → let it fill the frame, not just the riser box */
+  #stage.video-bg.yt-hero #venueVideo{left:50%;top:50%;width:100vw;height:56.25vw;min-height:100vh;min-width:177.78vh}
   #stage.video-bg.yt-hero #sky{opacity:.04}
   #stage.video-bg.yt-hero #scene{opacity:.16}
-  /* (TK-11178 consolidation) The separate #vrBackdrop/.vr-bg layer was removed — there is now
-     ONE backdrop (#venueVideo), driven by both the automatic era-match failover AND the manual
-     Videos-panel pick. Its size is dialable via --vr-bd-scale (Videos panel "Backdrop size"). */
   #stage.video-bg.yt-hero #orb{opacity:.5}
   .btn:disabled,button:disabled{opacity:.42;cursor:not-allowed}
   /* ---- Controls in a collapsed RIGHT drawer, opened by the top-right hamburger (TK-11178 UI) ---- */
@@ -281,7 +285,7 @@
   .vr-size{margin-bottom:9px}
   .vr-size label{display:block;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-dim);margin-bottom:4px}
   .vr-size .val{font-family:var(--serif);font-style:italic;color:var(--gold);font-size:12px;text-transform:none;letter-spacing:0}
-  .vr-size input[type=range]{width:100%}
+  .vr-size input[type=range]{width:100%;margin-bottom:8px}
   .vr-list{max-height:220px;overflow:auto;display:flex;flex-direction:column;gap:2px}
   .vr-row{display:flex;gap:9px;align-items:baseline;width:100%;text-align:left;background:none;border:0;
     color:var(--ink-dim);font-family:var(--sans);font-size:11.5px;padding:6px 6px;cursor:pointer;border-radius:6px}
@@ -1696,22 +1700,57 @@ const MR_VIDEOS=[
   vrToggle.addEventListener('keydown', e => {
     if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); vrToggle.click(); }
   });
-  // Backdrop size — dials the ONE #venueVideo backdrop via --vr-bd-scale (wrapped for private-mode).
-  const SCALE_KEY = 'vr-bd-scale';
-  const savedScale = Math.min(1.5, Math.max(.4, parseFloat((()=>{try{return localStorage.getItem(SCALE_KEY);}catch(e){return null;}})()) || 1));
-  try{ document.documentElement.style.setProperty('--vr-bd-scale', savedScale); }catch(e){}
-  const sizeWrap = document.createElement('div');
-  sizeWrap.className = 'vr-size';
-  sizeWrap.innerHTML = `<label for="vrSize">Backdrop size <span class="val" id="vrSizeV">${Math.round(savedScale*100)}%</span></label>
-    <input type="range" id="vrSize" min="40" max="150" step="5" value="${Math.round(savedScale*100)}" aria-label="Backdrop video size">`;
-  vrList.insertAdjacentElement('beforebegin', sizeWrap);
-  const sizeInput = sizeWrap.querySelector('#vrSize');
-  const sizeVal = sizeWrap.querySelector('#vrSizeV');
-  sizeInput.addEventListener('input', () => {
-    const pct = +sizeInput.value;
-    try{ document.documentElement.style.setProperty('--vr-bd-scale', pct / 100); localStorage.setItem(SCALE_KEY, pct / 100); }catch(e){}
-    sizeVal.textContent = pct + '%';
+  // Backdrop position + size — dials the ONE #venueVideo box (--vr-bd-x/-y/-w/-h) so Steve can
+  // drag it right onto "the area in back of where singers would sing" himself, live.
+  const POS_KEY = 'vr-bd-pos';
+  function readJSON(k){ try{ return JSON.parse(localStorage.getItem(k)); }catch(e){ return null; } }
+  function writeJSON(k,v){ try{ localStorage.setItem(k, JSON.stringify(v)); }catch(e){} }
+  const DEFAULT_POS = { x:50, y:37, w:46, h:32 };   // %,%,vw,vh — wide enough to show around the (opaque) stage riser, not just its exact footprint
+  const pos = Object.assign({}, DEFAULT_POS, readJSON(POS_KEY) || {});
+  function applyPos(){
+    const root = document.documentElement.style;
+    root.setProperty('--vr-bd-x', pos.x + '%');
+    root.setProperty('--vr-bd-y', pos.y + '%');
+    root.setProperty('--vr-bd-w', pos.w + 'vw');
+    root.setProperty('--vr-bd-h', pos.h + 'vh');
+  }
+  applyPos();
+  const posWrap = document.createElement('div');
+  posWrap.className = 'vr-size';
+  posWrap.innerHTML = [
+    ['x','Left','%',5,95,DEFAULT_POS.x],
+    ['y','Top','%',5,95,DEFAULT_POS.y],
+    ['w','Width','vw',8,100,DEFAULT_POS.w],
+    ['h','Height','vh',6,100,DEFAULT_POS.h],
+  ].map(([k,label,unit])=>
+    `<label for="vrBd_${k}">Backdrop ${label} <span class="val" id="vrBd_${k}V">${pos[k]}${unit}</span></label>
+     <input type="range" id="vrBd_${k}" min="${k==='x'||k==='y'?5:(k==='w'?8:6)}" max="${k==='x'||k==='y'?95:100}" step="1" value="${pos[k]}" aria-label="Backdrop ${label}">`
+  ).join('');
+  vrList.insertAdjacentElement('beforebegin', posWrap);
+  ['x','y','w','h'].forEach(k=>{
+    const input = posWrap.querySelector(`#vrBd_${k}`);
+    const val = posWrap.querySelector(`#vrBd_${k}V`);
+    const unit = (k==='x'||k==='y') ? '%' : (k==='w' ? 'vw' : 'vh');
+    input.addEventListener('input', () => {
+      pos[k] = +input.value;
+      val.textContent = pos[k] + unit;
+      applyPos();
+      writeJSON(POS_KEY, pos);
+    });
+  });
+  const resetBtn = document.createElement('button');
+  resetBtn.type = 'button'; resetBtn.className = 'vr-bd-toggle'; resetBtn.textContent = '↺ Reset backdrop position';
+  resetBtn.addEventListener('click', () => {
+    Object.assign(pos, DEFAULT_POS);
+    ['x','y','w','h'].forEach(k=>{
+      const unit = (k==='x'||k==='y') ? '%' : (k==='w' ? 'vw' : 'vh');
+      posWrap.querySelector(`#vrBd_${k}`).value = pos[k];
+      posWrap.querySelector(`#vrBd_${k}V`).textContent = pos[k] + unit;
+    });
+    applyPos();
+    writeJSON(POS_KEY, pos);
   });
+  posWrap.insertAdjacentElement('afterend', resetBtn);
   MR_VIDEOS.forEach(v => {
     const row = document.createElement('button');
     row.type = 'button';
@@ -1848,23 +1887,23 @@ function enterYouTubeFallback(show, hadGesture){
   announce('archive.org is unreachable — playing the nearest official Grateful Dead concert from YouTube.');
   saveResume();                                            // persist the selected show even in fallback (audio timeupdate won't fire here)
 }
-// MANUAL trigger from the Videos panel → play a chosen concert through the ONE backdrop controller (TK-11178).
-// Same engine as the automatic failover, different entry point → one backdrop layer, one audio owner.
+// MANUAL trigger from the Videos panel → a MUTED decoration in the small backdrop box behind
+// the stage (Steve: "put behind my graphics... the area in back of where singers would sing"),
+// NOT the full-screen yt-hero "now watching" mode — that stays reserved for the real
+// archive.org-is-down fallback (enterYouTubeFallback), where the video IS the whole show.
 function playConcertManual(id, title){
   VB.mode='youtube';
   try{ audio.pause(); }catch(e){}
   try{ audio.removeAttribute('src'); audio.load(); }catch(e){}   // one audio owner — kill archive audio
-  const hasAct=!!(navigator.userActivation ? navigator.userActivation.isActive : false);
-  VB.wantId=id; VB.wantPlay=true; VB.wantMuted=!hasAct; VB._tried=new Set(); VB._hops=0;
+  VB.wantId=id; VB.wantPlay=true; VB.wantMuted=true; VB._tried=new Set(); VB._hops=0;
   _loadYTApi();
   if(VB.player && VB.player.loadVideoById) _applyVideo();
-  _vbShow(); _stageEl.classList.add('yt-hero');
-  if(!hasAct) _primeYTUnlock();
+  _vbShow(); _stageEl.classList.remove('yt-hero');
   $('mrNow').textContent='▶ Grateful Dead — '+(title||'concert');
   $('mrSub').textContent='Playing your pick from the Videos panel — official concert footage on YouTube.';
   const msg=$('mrMsg'); if(msg){ msg.hidden=false;
-    msg.innerHTML='Now playing the concert you chose.'+(hasAct?'':' Tap or press any key for sound.')+
-      ' <a href="https://www.youtube.com/watch?v='+id+'" target="_blank" rel="noopener noreferrer">Watch on YouTube ↗</a>'; }
+    msg.innerHTML='Now showing behind the stage (muted).'+
+      ' <a href="https://www.youtube.com/watch?v='+id+'" target="_blank" rel="noopener noreferrer">Watch with sound on YouTube ↗</a>'; }
   setPlay(true);
   if(typeof reactBtn!=='undefined' && reactBtn){ reactBtn.disabled=true; reactBtn.title='Live audio analysis isn’t available on the YouTube stream.'; }
   saveResume();

← 8f1b9bf Music room: CONSOLIDATE the two video-backdrop systems into  ·  back to Dead Agentabrams  ·  Lock in Steve's dialed-in backdrop position as the default ff54235 →