[object Object]

← back to Dead Agentabrams

Sync ROOM 01: venue backdrops + song-motif items

8a0b439bd1d8eb901c718362a217f8e60dbc7b2b · 2026-09-01 22:24:06 -0700 · Steve Abrams

Files touched

Diff

commit 8a0b439bd1d8eb901c718362a217f8e60dbc7b2b
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Sep 1 22:24:06 2026 -0700

    Sync ROOM 01: venue backdrops + song-motif items
---
 room/index.html | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 97 insertions(+)

diff --git a/room/index.html b/room/index.html
index 0df967b..b9dda3c 100644
--- a/room/index.html
+++ b/room/index.html
@@ -272,6 +272,7 @@
         <button class="btn" id="trailBtn" aria-pressed="true">✧ Motion Trails</button>
         <button class="btn" id="reactBtn" aria-pressed="false">♫ React to Music</button>
         <button class="btn" id="pteroBtn">🦖 Fly Over</button>
+        <button class="btn" id="venueBtn" aria-pressed="true">🏛 Venue</button>
       </div>
     </div>
     <div class="group">
@@ -411,6 +412,89 @@ function pal(key,i){ // blended palette between integer chapters
 // Choreography snaps to the nearest chapter (palette blends, steps change crisply)
 function CH(){ return CHAPTERS[clamp(Math.round(S.chapterMix),0,2)].choreo; }
 
+// ---- Concert venue backdrops (swap with the streaming show) ----
+const VENUES=[
+  {name:'Fillmore West · San Francisco · 1969',    lights:['#ffcf6e','#ff7a4a','#ffd06e']},
+  {name:'Veneta · Oregon · 1972',                  lights:['#ffe0a0','#ffbf6a','#fff0c0']},
+  {name:'Jai-Alai Fronton · Miami · 1974',         lights:['#a8c8ff','#ffd0f0','#8fe0ff']},
+  {name:'Barton Hall · Cornell · 1977',            lights:['#ffd0f0','#c9a2ff','#ffe08a']},
+  {name:'Nassau Coliseum · Uniondale · 1990',      lights:['#80e0ff','#ff8ad0','#ffd66e']}
+];
+let venueOn=true;
+
+// ---- Song-motif layer: floats items drawn from the current track's title ----
+const MOTIFS=[
+  [/fire|flame|mountain|burn/,           ['🔥','⛰️','✨']],
+  [/sugar|magnolia|sugaree|begonia|rose|flower|daffodil|lily|garden/, ['🌺','🌸','🌼']],
+  [/rain|snow|cold|storm|hail|winter/,   ['❄️','🌧️','☁️']],
+  [/sun|sunshine|sunflower|shine|summer/,['🌞','🌻','☀️']],
+  [/moon|dark star|midnight|star/,       ['🌙','⭐','✨']],
+  [/water|river|ripple|ocean|wave|sea|rowing/, ['💧','🌊']],
+  [/bird|eagle|dove|sparrow|crow/,       ['🐦','🕊️']],
+  [/cat|china cat|tiger|lion/,           ['🐱','🐾']],
+  [/train|casey|railroad|jones|engine/,  ['🚂','🛤️']],
+  [/truck|road|highway|ramble|wheel|turnin/, ['🚚','🛣️','🎡']],
+  [/devil|hell|deal|gambl|dice|card/,    ['🃏','🎲']],
+  [/eyes|world/,                          ['👁️','🌍']],
+  [/wine|whiskey|beer|jug|drink/,        ['🍷','🥃']],
+  [/love|heart|sweet|darling|honey/,     ['💗','💘']],
+  [/gold|silver|diamond|jewel/,          ['✨','💎']],
+  [/rat|mouse|wharf/,                    ['🐀']],
+  [/candy|man/,                          ['🍬']]
+];
+let motifParticles=[], motifClock=0, motifSet=null, motifKey=null;
+function currentMotifs(){
+  const tr=((window.SRM&&window.SRM.track)||'').toLowerCase();
+  if(tr===motifKey) return motifSet;
+  motifKey=tr; motifSet=null;
+  for(const m of MOTIFS){ if(m[0].test(tr)){ motifSet=m[1]; break; } }
+  return motifSet;
+}
+function drawSongMotifs(dt){
+  const set=currentMotifs();
+  if(set && S.playing && !S.reduced){
+    motifClock+=dt;
+    if(motifClock>0.75 && motifParticles.length<40){ motifClock=0;
+      motifParticles.push({x:S.W*(0.08+Math.random()*0.84), y:S.H*0.9, vy:-(18+Math.random()*26), drift:(Math.random()-0.5)*14, life:1, size:20+Math.random()*20, g:set[(Math.random()*set.length)|0]});
+    }
+  }
+  if(!motifParticles.length) return;
+  ctx.save(); ctx.textAlign='center'; ctx.textBaseline='middle';
+  for(let i=motifParticles.length-1;i>=0;i--){ const p=motifParticles[i];
+    p.y+=p.vy*dt; p.x+=p.drift*dt; p.life-=dt*0.16;
+    if(p.life<=0 || p.y<S.H*0.14){ motifParticles.splice(i,1); continue; }
+    ctx.globalAlpha=Math.max(0,Math.min(0.9,p.life));
+    ctx.font=`${p.size}px "Apple Color Emoji","Segoe UI Emoji",serif`;
+    ctx.fillText(p.g, p.x, p.y);
+  }
+  ctx.restore(); ctx.globalAlpha=1;
+}
+
+function drawVenue(){
+  if(!venueOn) return;
+  const W=S.W,H=S.H;
+  const idx=clamp((window.SRM&&window.SRM.showIdx!=null)?window.SRM.showIdx:3,0,VENUES.length-1);
+  const V=VENUES[idx];
+  // stage-light beams fanning down from the rig, gently sweeping, brightening on the beat
+  ctx.save(); ctx.globalCompositeOperation='lighter';
+  for(let i=0;i<4;i++){
+    const bx=W*(0.16+0.22*i), sweep=Math.sin(S.t*0.5+i*1.3)*W*0.06, tx=bx+sweep;
+    ctx.globalAlpha=0.045+(S.amp||0)*0.06+(S.pulse||0)*0.05;
+    ctx.fillStyle=V.lights[i%V.lights.length];
+    ctx.beginPath(); ctx.moveTo(bx-16,0); ctx.lineTo(bx+16,0); ctx.lineTo(tx+78,H*0.62); ctx.lineTo(tx-78,H*0.62); ctx.closePath(); ctx.fill();
+  }
+  ctx.restore(); ctx.globalAlpha=1;
+  // venue marquee
+  ctx.save();
+  const fs=Math.max(13,Math.min(21,W/60));
+  ctx.font=`italic 600 ${fs}px "Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif`;
+  ctx.textAlign='center'; ctx.textBaseline='middle';
+  ctx.shadowColor='rgba(0,0,0,0.65)'; ctx.shadowBlur=14;
+  ctx.globalAlpha=0.92; ctx.fillStyle=pal('accent',0);
+  ctx.fillText(V.name, W*0.5, H*0.14);
+  ctx.restore();
+}
+
 // cutout ("South Park") styling: flat fill + a bold dark outline, round joins
 const OUTLINE='rgba(8,3,5,0.92)';
 function cut(fill,w){ ctx.fillStyle=fill; ctx.strokeStyle=OUTLINE; ctx.lineWidth=w||2.4; ctx.lineJoin='round'; ctx.lineCap='round'; }
@@ -1130,6 +1214,8 @@ function frame(now){
   drawRiver();
   drawAnimals();
   drawBoat();
+  drawVenue();
+  drawSongMotifs(dt);
   drawPtero(dt);
   drawFireworks(dt);
 
@@ -1253,6 +1339,13 @@ function toggleChrome(){
 }
 chromeBtn.addEventListener('click', toggleChrome);
 
+// Venue backdrop toggle
+const venueBtn=document.getElementById('venueBtn');
+venueBtn.addEventListener('click',()=>{
+  venueOn=!venueOn; venueBtn.setAttribute('aria-pressed',String(venueOn));
+  announce(venueOn?'Venue backdrop on.':'Venue backdrop off.');
+});
+
 // keyboard shortcuts
 window.addEventListener('keydown',e=>{
   const t=e.target;
@@ -1268,6 +1361,7 @@ window.addEventListener('keydown',e=>{
     case 'a': { const rb=document.getElementById('reactBtn'); if(rb) rb.click(); } break;
     case 'p': launchPtero(); break;
     case 'c': toggleChrome(); break;
+    case 'v': venueBtn.click(); break;
     case '1': document.querySelector('[data-cam="wide"]').click(); break;
     case '2': document.querySelector('[data-cam="deck"]').click(); break;
     case '3': document.querySelector('[data-cam="over"]').click(); break;
@@ -1331,6 +1425,7 @@ async function loadShow(i, opts){
   const myToken=++loadToken;               // guard against a stale fetch overwriting a newer selection
   try{ audio.pause(); }catch(e){}          // stop the current night before loading the next
   showIdx=i; tracks=[]; trackIdx=-1;
+  if(window.SRM) window.SRM.showIdx=i;     // tell the visuals which venue to render
   const s=CATALOG[i];
   sel.value=String(i);
   $('mrNow').textContent=`${s.title} — loading set list…`;
@@ -1352,6 +1447,7 @@ async function loadShow(i, opts){
     if(!tracks.length) throw new Error('no audio files');
     renderTracks();
     trackIdx=0; audio.src=tracks[0].url;                 // cue the opener, paused (playback stays user-initiated)
+    if(window.SRM) window.SRM.track=tracks[0].title;
     $('mrNow').textContent=`♪ ${tracks[0].title}`;
     highlight();
     $('mrDur').textContent='0:00'; $('mrCur').textContent='0:00'; $('mrSeek').value='0';
@@ -1388,6 +1484,7 @@ function highlight(){ [...$('mrTracks').children].forEach((b,i)=>b.setAttribute(
 
 function playTrack(i){
   if(i<0||i>=tracks.length) return;
+  if(window.SRM) window.SRM.track=tracks[i].title;   // tell the visuals which song is playing
   if(i!==trackIdx || !audio.src){ trackIdx=i; audio.src=tracks[i].url; }
   audio.play().catch(()=>setPlay(false));
   $('mrNow').textContent=`♪ ${tracks[i].title}`;

← c6068c0 Sync ROOM 01: livelier beat-synced bear dance  ·  back to Dead Agentabrams  ·  Sync ROOM 01: drawn animated song-motif items 5b3f52d →