[object Object]

← back to Dead Agentabrams

room: cancel stale archive fetches on source-switch (contrarian hardening)

ac0f021961964ba1a6aa0c7c91e9473842db15fa · 2026-09-02 09:36:17 -0700 · Steve Abrams

Files touched

Diff

commit ac0f021961964ba1a6aa0c7c91e9473842db15fa
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 2 09:36:17 2026 -0700

    room: cancel stale archive fetches on source-switch (contrarian hardening)
---
 room/index.html | 152 ++++++++++----------------------------------------------
 1 file changed, 27 insertions(+), 125 deletions(-)

diff --git a/room/index.html b/room/index.html
index 1e48d0b..ac0394c 100644
--- a/room/index.html
+++ b/room/index.html
@@ -237,10 +237,6 @@
   }
   @media (prefers-reduced-motion: reduce){
     .museum,.chapters button,.btn,.skip{transition:none}
-    /* also silence the always-on keyframe loops (live-dot pulse, loading tiles) */
-    *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important}
-    .musicroom.playing .live-dot{animation:none}
-    .mr-yr.loading{animation:none}
   }
 </style>
 </head>
@@ -270,9 +266,9 @@
     </header>
     <div class="mrbody" id="mrbody">
       <input class="mr-search" id="mrSearch" type="search" placeholder="🔍 Search the archive — date, city, song…" aria-label="Search the Grateful Dead archive">
-      <div class="mr-src" id="mrSrc" role="group" aria-label="Choose a music source"></div>
+      <div class="mr-src" id="mrSrc" role="tablist" aria-label="Choose a music source"></div>
       <div class="mr-map-head"><span id="mrMapLabel">Tap a year</span><button type="button" class="mr-allbtn on" id="mrAll">All years</button></div>
-      <div class="mr-map" id="mrMap" role="group" aria-label="Browse shows by year"></div>
+      <div class="mr-map" id="mrMap" role="listbox" aria-label="Browse shows by year"></div>
       <input type="hidden" id="mrYear" value="">
       <div class="mr-results" id="mrResults" role="list"></div>
       <div class="mr-picks">Curated nights:</div>
@@ -405,18 +401,6 @@ const CHAPTER_SECONDS = 55; // dwell before the boat drifts to the next chapter
 if (window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches){
   S.reduced = true;
 }
-// ...and keep honoring it if the visitor flips the OS setting mid-session
-if (window.matchMedia){
-  const _rmq = matchMedia('(prefers-reduced-motion: reduce)');
-  const _sync = e => {
-    S.reduced = e.matches;
-    const rb = document.getElementById('reduceBtn');
-    if (rb) rb.setAttribute('aria-pressed', String(S.reduced));
-    if (S.reduced) ctx.clearRect(0,0,S.W,S.H);
-  };
-  if (_rmq.addEventListener) _rmq.addEventListener('change', _sync);
-  else if (_rmq.addListener) _rmq.addListener(_sync);
-}
 
 // ---------- Palettes + per-chapter choreography ----------
 const CHAPTERS = [
@@ -966,7 +950,7 @@ function drawAnimals(){
   heron(W*0.40, baseY-4, 1*scaleA, t, accent);
   rabbit(W*0.63, baseY-4, 1*scaleA, t, accent);
   bearAndFireflies(W*0.85, baseY-2, 1.1*scaleA, t, accent);
-  if(!S.playing && !S.reduced){ drawIdleBackflipBear(scaleA, baseY); } else { drawBearCubs(t, scaleA, baseY); }
+  drawBearCubs(t, scaleA, baseY);
   drawSongbirds(t, scaleA, baseY);
 
   ctx.restore();
@@ -1103,21 +1087,6 @@ function bearCub(x,y,s,t,ph,FUR){
   ctx.fillStyle=OUTLINE; ctx.beginPath(); ctx.arc(0,-29,1.1,0,7); ctx.fill();      // nose
   ctx.restore();
 }
-function drawIdleBackflipBear(scaleA, baseY){
-  const W=S.W;
-  const cross=8.5, gap=1.4, span=cross+gap;
-  const tt=S.idleT||0; const idx=Math.floor(tt/span); const local=tt%span;
-  if(local>cross) return;
-  const prog=local/cross;
-  const FUR=BEAR_COLORS[idx%BEAR_COLORS.length];
-  const size=1.55*scaleA, margin=64;
-  const x=-margin + prog*(W+margin*2);
-  const flips=3, fp=(prog*flips)%1;
-  const lift=Math.sin(fp*Math.PI)*54*scaleA;
-  const spin=-(prog*flips)*Math.PI*2;
-  ctx.save(); ctx.translate(x,(baseY+8)-lift); ctx.rotate(spin);
-  bearCub(0,0,size,(S.idleT||0)*0.8,idx*1.3,FUR); ctx.restore();
-}
 // a user-sized colorful dancing-bear troupe (1..10), auto-distributed + scaled across the shore (our own design, not the GD trademark)
 const BEAR_COLORS=['rgba(206,66,116,0.98)','rgba(232,160,52,0.98)','rgba(72,172,150,0.98)','rgba(124,110,224,0.98)','rgba(226,96,60,0.98)','rgba(96,182,96,0.98)','rgba(232,120,182,0.98)','rgba(82,158,224,0.98)','rgba(214,182,60,0.98)','rgba(154,92,204,0.98)'];
 function drawBearCubs(t,scaleA,baseY){
@@ -1266,21 +1235,18 @@ function drawPtero(dt){
 let last=performance.now();
 function frame(now){
   const dt=Math.min(0.05,(now-last)/1000); last=now;
-  S.idleT=(S.idleT||0)+dt;
 
   // live audio energy (0..1) when "React to Music" is on — otherwise motion stays authored
   const reacting = !!(window.SRM && window.SRM.react);
   S.amp = reacting ? (window.SRM.level||0) : 0;
   S.pulse = reacting ? (window.SRM.pulse||0) : 0;   // sharp per-beat throb for lamps
-  // bridge visual state to the separate WebGL orb loop so it can honor reduced motion + pause
-  if(window.SRM){ window.SRM.reduced = S.reduced; window.SRM.playing = S.playing; }
   // fire a burst on each new detected beat
   if(reacting && !S.reduced && window.SRM.beat>_seenBeat){ _seenBeat=window.SRM.beat; spawnFirework(); }
   else if(!reacting){ _seenBeat=window.SRM?(window.SRM.beat||0):0; }   // let fireworks decay naturally
 
   if(S.playing){
     const spd = S.reduced ? 0.28 : 1.0;
-    S.t += dt * S.tempo * spd * 0.85;   // slowed jazzy/groovy
+    S.t += dt * S.tempo * spd * 1.4;
 
     // Gentle auto-journey through the three chapters (until the visitor takes over)
     if(S.autoJourney && !S.reduced){
@@ -1497,26 +1463,13 @@ requestAnimationFrame(frame);
 (() => {
 "use strict";
 const CATALOG = [
-  {id:"gd69-02-27.sbd.16track.kaplan.6315.sbeok.shnf", date:"February 27, 1969", title:"Fillmore West", sub:"San Francisco · primal psychedelic era"},
-  {id:"gd70-05-24.sbd.hanno.6481.sbeok.shnf", date:"May 24, 1970", title:"Newcastle-Under-Lyme", sub:"Newcastle · acoustic-and-electric era"},
-  {id:"gd71-04-14.sbd.lai.6250.sbefail.shnf", date:"April 14, 1971", title:"Christy Mathewson Stadium, Bucknell U.", sub:"Lewisburg · Skull & Roses era"},
-  {id:"gd72-08-27.sbd.orf.3328.sbeok.shnf", date:"August 27, 1972", title:"Veneta", sub:"Oregon · field, heat, and open sky"},
-  {id:"gd73-02-15.sbd.hall.1580.sbeok.shnf", date:"February 15, 1973", title:"Dane County Coliseum", sub:"Madison · jazz-loose / Wake of the Flood era"},
-  {id:"gd74-06-23.sbd.cribbs.16780.sbeok.shnf", date:"June 23, 1974", title:"Jai-Alai Fronton", sub:"Miami · Wall of Sound era"},
-  {id:"gd76-06-10.sbd.bertha-ashley.23370.sbeok.shnf", date:"June 10, 1976", title:"Boston Music Hall", sub:"Boston · return-from-hiatus era"},
-  {id:"gd77-05-08.sbd.hicks.4982.sbeok.shnf", date:"May 8, 1977", title:"Cornell", sub:"Ithaca · Barton Hall"},
-  {id:"gd78-02-01.sbd.parillo.1017.sbeok.shnf", date:"February 1, 1978", title:"Uptown Theater", sub:"Chicago · tight & funky late-’70s era"},
-  {id:"gd81-03-21.sbd.clugston.6666.sbeok.shnf", date:"March 21, 1981", title:"Rainbow Theatre", sub:"London · European-tour era"},
-  {id:"gd85-04-27.sbd.jerugim.359.sbeok.shnf", date:"April 27, 1985", title:"Frost Amphitheatre", sub:"Palo Alto · 20th-anniversary / Frost era"},
-  {id:"gd1987-01-28.sbd.walker-scotton.miller.83286.flac16", date:"January 28, 1987", title:"S.F Civic Auditorium", sub:"San Francisco · In the Dark comeback era"},
-  {id:"gd89-12-31.sbd.bertha-ashley.18674.sbeok.shnf", date:"December 31, 1989", title:"Oakland-Alameda County Coliseum", sub:"Oakland · late-peak / Built to Last era"},
-  {id:"gd90-03-29.aud-fob.set2.unknown.1317.sbeok.shnf", date:"March 29, 1990", title:"Nassau", sub:"Uniondale · late-era expansion"},
-  {id:"gd91-10-31.sbd.gardner.2897.sbeok.shnf", date:"October 31, 1991", title:"Oakland-Alameda County Coliseum", sub:"Oakland · post-Brent / Bruce Hornsby era"},
-  {id:"gd93-03-24.sbd.keyser.25664.sbeok.flacf", date:"March 24, 1993", title:"Dean Smith Center", sub:"Chapel Hill · Vince era / stadium years"},
-  {id:"gd94-10-05.sbd.unknown.8030.sbeok.shnf", date:"October 5, 1994", title:"The Spectrum", sub:"Philadelphia · final-years era"},
-  {id:"gd95-07-09.sbd.7233.sbeok.shnf", date:"July 9, 1995", title:"Soldier Field", sub:"Chicago · the final show"}
+  {id:'gd69-02-27.sbd.16track.kaplan.6315.sbeok.shnf', date:'February 27, 1969', title:'Fillmore West',    sub:'San Francisco · primal psychedelic era'},
+  {id:'gd72-08-27.sbd.orf.3328.sbeok.shnf',            date:'August 27, 1972',   title:'Veneta',           sub:'Oregon · field, heat, and open sky'},
+  {id:'gd74-06-23.sbd.cribbs.16780.sbeok.shnf',        date:'June 23, 1974',     title:'Jai-Alai Fronton', sub:'Miami · Wall of Sound era'},
+  {id:'gd77-05-08.sbd.hicks.4982.sbeok.shnf',          date:'May 8, 1977',       title:'Cornell',          sub:'Ithaca · Barton Hall'},
+  {id:'gd90-03-29.aud-fob.set2.unknown.1317.sbeok.shnf',date:'March 29, 1990',   title:'Nassau',           sub:'Uniondale · late-era expansion'}
 ];
-const DEFAULT_SHOW = 7; // Cornell '77
+const DEFAULT_SHOW = 3; // Cornell '77
 
 const $ = id => document.getElementById(id);
 const audio = $('mrAudio'), room = $('musicroom'), sel = $('mrShow'), live = $('live');
@@ -1577,9 +1530,13 @@ async function loadShow(i, opts){ if(sel) sel.value=String(i); return loadShowRe
 let searchTimer=null;
 // archive.org is a flaky nonprofit — abort a hung request so the UI fails gracefully
 // into the error state instead of pulsing "…" forever (5x sweep 2 fix).
-function fetchT(url, ms=9000){
+function fetchT(url, ms=9000, extSignal){
   const ctl = (typeof AbortController!=='undefined') ? new AbortController() : null;
   const t = ctl ? setTimeout(()=>ctl.abort(), ms) : null;
+  if(ctl && extSignal){                                   // batch-cancel (e.g. source switch)
+    if(extSignal.aborted) ctl.abort();
+    else extSignal.addEventListener('abort', ()=>ctl.abort(), {once:true});
+  }
   return fetch(url, {mode:'cors', signal: ctl?ctl.signal:undefined})
     .finally(()=>{ if(t) clearTimeout(t); });
 }
@@ -1592,7 +1549,7 @@ async function searchArchive(q){
   const filters=[]; if(q) filters.push('('+q+')'); if(yr) filters.push('year:'+yr);
   const query = filters.length ? coll+' AND '+filters.join(' AND ') : coll;
   res.innerHTML='<div class="mr-hint">'+(filters.length?'Searching the archive…':'Loading the most-played shows…')+'</div>';
-  const sort = (q||yr) ? 'downloads+desc' : 'downloads+desc';
+  const sort = 'downloads+desc';
   try{
     const url='https://archive.org/advancedsearch.php?q='+encodeURIComponent(query)+
       '&fl[]=identifier&fl[]=title&fl[]=date&fl[]=venue&fl[]=coverage&fl[]=downloads&sort[]='+sort+'&rows=40&output=json';
@@ -1666,8 +1623,6 @@ function ensureGraph(){
 }
 let _avg=0, _clock=0, _lastBeat=-99;
 function sampleLevel(){
-  // stop the rAF loop when it has nothing to analyze; reactBtn / audio 'play' restart it
-  if(!window.SRM.react || audio.paused){ _looping=false; return; }
   if(_analyser){
     _analyser.getByteFrequencyData(_freq);
     let sum=0, n=Math.max(1,Math.floor(_freq.length/6));  // low-end (bass) energy
@@ -1696,9 +1651,7 @@ if(reactBtn) reactBtn.addEventListener('click',()=>{
   announce(on?'Reacting to the music — lanterns, fireflies, and dancers now move to the live audio.':'Back to authored motion.');
 });
 
-audio.addEventListener('play',()=>{ setPlay(true);
-  if(window.SRM.react && _actx){ if(_actx.state==='suspended') _actx.resume(); if(!_looping){ _looping=true; sampleLevel(); } }
-});
+audio.addEventListener('play',()=>setPlay(true));
 audio.addEventListener('pause',()=>setPlay(false));
 audio.addEventListener('ended',()=>{ if(trackIdx<tracks.length-1) playTrack(trackIdx+1); else setPlay(false); });
 audio.addEventListener('timeupdate',()=>{
@@ -1752,13 +1705,17 @@ function setYear(y){
   searchArchive(searchEl?searchEl.value:'');
 }
 
+let buildCtl=null;   // cancels the previous batch of year-count fetches on a source switch
 function buildMap(){
   const src=window.MR_SOURCES[window.MR_SRC], mySrc=window.MR_SRC;
+  if(buildCtl) buildCtl.abort();                            // kill in-flight fetches from the prior build
+  buildCtl=(typeof AbortController!=='undefined')?new AbortController():null;
+  const sig=buildCtl?buildCtl.signal:undefined;
   const YEARS=[]; for(let y=src.y0;y<=src.y1;y++) YEARS.push(y);
   mapEl.innerHTML='';
   YEARS.forEach(y=>{
     const b=document.createElement('button'); b.type='button'; b.className='mr-yr loading';
-    b.dataset.y=String(y); b.title=y+' — counting…';
+    b.dataset.y=String(y); b.setAttribute('role','option'); b.title=y+' — counting…';
     b.innerHTML=`<span class="yl">${y}</span><span class="yc">…</span>`;
     b.addEventListener('click',()=>setYear(y));
     b.addEventListener('mouseenter',()=>{ const c=counts[ck(window.MR_SRC,y)]; if(c!=null) mapLabel.textContent=`${y} · ${c.toLocaleString()} shows`; });
@@ -1766,7 +1723,7 @@ function buildMap(){
   });
   // per-year show counts → heat-color the tiles (sqrt to tame the '77/'89 spikes)
   Promise.all(YEARS.map(y=>
-    fetchT('https://archive.org/advancedsearch.php?q='+encodeURIComponent('collection:'+src.id+' AND year:'+y)+'&rows=0&output=json')
+    fetchT('https://archive.org/advancedsearch.php?q='+encodeURIComponent('collection:'+src.id+' AND year:'+y)+'&rows=0&output=json', 9000, sig)
       .then(r=>r.json()).then(j=>({y,n:(j.response&&j.response.numFound)||0})).catch(()=>({y,n:-1}))
   )).then(rows=>{
     if(mySrc!==window.MR_SRC) return;                          // source switched mid-flight
@@ -1785,11 +1742,11 @@ function buildMap(){
 
 if(srcEl){
   srcEl.innerHTML=Object.entries(window.MR_SOURCES).map(([k,s])=>
-    `<button type="button" data-src="${k}" class="${k===window.MR_SRC?'on':''}" aria-pressed="${k===window.MR_SRC}"><b>${s.label}</b><span class="yr">${s.years}</span></button>`).join('');
+    `<button type="button" data-src="${k}" class="${k===window.MR_SRC?'on':''}" role="tab" aria-selected="${k===window.MR_SRC}"><b>${s.label}</b><span class="yr">${s.years}</span></button>`).join('');
   srcEl.addEventListener('click',e=>{
     const btn=e.target.closest('button[data-src]'); if(!btn||btn.dataset.src===window.MR_SRC) return;
     window.MR_SRC=btn.dataset.src;
-    [...srcEl.children].forEach(c=>{ const on=c.dataset.src===window.MR_SRC; c.classList.toggle('on',on); c.setAttribute('aria-pressed',String(on)); });
+    [...srcEl.children].forEach(c=>{ const on=c.dataset.src===window.MR_SRC; c.classList.toggle('on',on); c.setAttribute('aria-selected',String(on)); });
     yearEl.value=''; allBtn.classList.add('on'); mapLabel.textContent='Tap a year';
     buildMap(); searchArchive(searchEl?searchEl.value:'');
   });
@@ -1824,55 +1781,8 @@ mrToggle.addEventListener('keydown',e=>{ if(e.key==='Enter'||e.key===' '){ e.pre
 // Music Room stays EXPANDED (incl. small screens) so revealing the chrome shows a
 // working player in one tap — no hidden second collapse (CTA/screenrecord CRITICAL #1).
 
-// ---- Autostart on load: queue "Me and My Uncle → Bertha → Deal" and play ----
-const WISHLIST=['Me and My Uncle','Bertha','Deal'];
-function _norm(t){ return (t||'').toLowerCase().replace(/&/g,' and ').replace(/[^a-z0-9 ]+/g,' ').replace(/\bthe\b/g,' ').replace(/\s+/g,' ').trim(); }
-function _matches(title,want){ const n=_norm(title), w=_norm(want); if(!n||!w) return false;
-  return new RegExp('\\b'+w.replace(/[.*+?^${}()|[\]\\]/g,'\\$&').replace(/\s+/g,'\\s+')+'\\b').test(n); }
-// fan out to the curated shows in parallel, pool every mp3 track (title + direct url)
-async function _poolFromShows(){
-  const results=await Promise.allSettled(CATALOG.map(s=>
-    fetch(`https://archive.org/metadata/${s.id}`,{mode:'cors'}).then(r=>r.ok?r.json():null).then(j=>({s,j}))));
-  const pool=[];
-  for(const res of results){
-    if(res.status!=='fulfilled' || !res.value || !res.value.j) continue;
-    const s=res.value.s, j=res.value.j;
-    let mp3=(j.files||[]).filter(f=>/vbr mp3/i.test(f.format||'')); if(!mp3.length) mp3=(j.files||[]).filter(f=>/mp3/i.test(f.format||''));
-    mp3.forEach(f=>pool.push({ title:(f.title&&f.title.trim())?f.title.trim():f.name.replace(/\.mp3$/i,''),
-      url:`https://archive.org/download/${s.id}/${encodeURIComponent(f.name)}`, show:s }));
-  }
-  return pool;
-}
-// browsers block silent autoplay — try, and if blocked, start on the first tap/keypress
-function _tryAutoplay(){
-  const go=()=>audio.play().then(()=>setPlay(true)).catch(()=>{});
-  audio.play().then(()=>setPlay(true)).catch(()=>{
-    const unlock=()=>{ go(); document.removeEventListener('pointerdown',unlock,true); document.removeEventListener('keydown',unlock,true); };
-    document.addEventListener('pointerdown',unlock,true);
-    document.addEventListener('keydown',unlock,true);
-    announce('Tap or press any key to start the music — Me and My Uncle, Bertha, Deal.');
-  });
-}
-async function bootMusic(){
-  $('mrNow').textContent='Cueing the set…';
-  try{
-    const pool=await _poolFromShows();
-    const list=[]; for(const w of WISHLIST){ const hit=pool.find(p=>_matches(p.title,w)); if(hit) list.push(hit); }
-    if(list.length){
-      tracks=list; trackIdx=0; showIdx=-1; renderTracks(); highlight();
-      audio.src=tracks[0].url; if(window.SRM) window.SRM.track=tracks[0].title;
-      $('mrNow').textContent=`♪ ${tracks[0].title}`;
-      $('mrSub').textContent='Autostart — '+list.map(t=>t.title).join(' · ');
-      setFoot(list[0].show);
-      announce('Autostarting: '+list.map(t=>t.title).join(', ')+'.');
-      _tryAutoplay();
-      return;
-    }
-  }catch(e){}
-  await loadShow(DEFAULT_SHOW);   // fallback: archive slow/down → cue the default night, still try to start
-  _tryAutoplay();
-}
-bootMusic();
+// cue the default night (no autoplay — the source policy is user-initiated playback)
+loadShow(DEFAULT_SHOW);
 
 // reveal the DEAD back-link only when this room is served as part of the exhibition
 const _p=location.pathname.replace(/\/(index\.html)?$/,'');
@@ -1902,16 +1812,8 @@ if(_p.endsWith('/room') || /dead\.agentabrams/i.test(location.hostname)){
   scene.add(new THREE.AmbientLight(0x402028,0.9));
   const col=new THREE.Color();
   addEventListener('resize',()=>{ W=innerWidth;H=innerHeight; camera.aspect=W/H; camera.updateProjectionMatrix(); renderer.setSize(W,H,false); });
-  let _orbStatic=false;
   function loop(){ requestAnimationFrame(loop);
     const s=window.SRM||{}, t=performance.now()/1000;
-    // Honor reduced motion (WCAG 2.3.3): draw one calm static frame, then stop spinning + re-rendering.
-    if(s.reduced){
-      if(!_orbStatic){ group.rotation.set(Math.sin(t*0.3)*0.18,0,0); group.scale.setScalar(1);
-        core.material.emissiveIntensity=0.7; renderer.render(scene,camera); _orbStatic=true; }
-      return;
-    }
-    _orbStatic=false;
     const pulse=s.pulse||0, level=(s.react?(s.level||0):0);
     const idle=0.5+0.5*Math.sin(t*1.2);                 // gentle breathing when no music
     const energy=s.react ? (level*0.6+pulse*0.7) : idle*0.12;

← 70fd187 auto-data-snapshot: 2026-09-02T09:35:20 (56 data files) — .p  ·  back to Dead Agentabrams  ·  auto-data-snapshot: 2026-09-02T10:09:29 (43 data files) — .p 7a653cc →