← back to Scarlet Riverboat Masquerade
Add Music Room: stream the Grateful Dead Live Music Archive (archive.org) — reuses the dead-agentabrams curated catalog; native transport, set list, seek, source attribution, embed fallback. Motion stays authored (no audio analysis).
85c6c771c12a1ba828ee548bde6e9d62f9ac3339 · 2026-09-01 17:06:22 -0700 · Steve Abrams
Files touched
Diff
commit 85c6c771c12a1ba828ee548bde6e9d62f9ac3339
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Sep 1 17:06:22 2026 -0700
Add Music Room: stream the Grateful Dead Live Music Archive (archive.org) — reuses the dead-agentabrams curated catalog; native transport, set list, seek, source attribution, embed fallback. Motion stays authored (no audio analysis).
---
index.html | 228 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 225 insertions(+), 3 deletions(-)
diff --git a/index.html b/index.html
index ea0d548..f543206 100644
--- a/index.html
+++ b/index.html
@@ -119,6 +119,48 @@
font-size:11px;line-height:1.7;letter-spacing:.02em}
.museum .keys b{display:inline-block;min-width:74px;color:var(--gold);font-family:var(--serif);font-style:italic;font-weight:600}
+ /* ---- Music Room console ---- */
+ .musicroom{
+ position:fixed;left:26px;top:158px;z-index:9;width:min(84vw,300px);
+ background:var(--panel);border:1px solid var(--panel-edge);border-radius:14px;
+ backdrop-filter:blur(9px);box-shadow:0 12px 40px rgba(0,0,0,.5);overflow:hidden;font-size:12px;
+ }
+ .musicroom > header{
+ display:flex;align-items:center;gap:9px;padding:11px 14px;cursor:pointer;
+ font-family:var(--serif);font-style:italic;color:var(--gold);font-size:14px;user-select:none;
+ }
+ .musicroom .live-dot{width:8px;height:8px;border-radius:50%;background:#5a2128;flex:none}
+ .musicroom.playing .live-dot{background:var(--gold);animation:pulse 2s infinite}
+ @keyframes pulse{0%{box-shadow:0 0 0 0 rgba(242,182,74,.5)}70%{box-shadow:0 0 0 8px rgba(242,182,74,0)}100%{box-shadow:0 0 0 0 rgba(242,182,74,0)}}
+ .musicroom .chev{margin-left:auto;transition:transform .3s;font-size:11px}
+ .musicroom[data-open="false"] .chev{transform:rotate(-90deg)}
+ .musicroom[data-open="false"] .mrbody{display:none}
+ .mrbody{padding:0 14px 14px}
+ .mr-show{width:100%;background:rgba(40,12,16,.7);color:var(--ink);border:1px solid var(--panel-edge);
+ border-radius:9px;padding:8px 10px;font-family:var(--serif);font-size:12.5px;cursor:pointer}
+ .mr-now{margin:12px 0 3px;color:var(--ink);font-family:var(--serif);font-style:italic;font-size:13px;min-height:18px;
+ overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
+ .mr-sub{color:var(--ink-dim);font-size:10.5px;letter-spacing:.03em;margin-bottom:9px}
+ .mr-transport{display:flex;align-items:center;gap:8px;margin:6px 0}
+ .mr-transport .btn{padding:7px 10px}
+ .mr-transport .play{min-width:46px;justify-content:center;font-size:13px}
+ .mr-tracks-toggle{margin-left:auto;font-size:10px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-dim);
+ background:none;border:0;cursor:pointer;padding:4px 2px}
+ .mr-tracks-toggle:hover{color:var(--gold)}
+ .mr-seek{display:flex;align-items:center;margin:9px 0 4px}
+ .mr-seek input[type=range]{width:100%}
+ .mr-time{font-variant-numeric:tabular-nums;color:var(--ink-dim);font-size:10px;text-align:right}
+ .mr-tracks{max-height:148px;overflow:auto;margin-top:9px;border-top:1px solid rgba(242,182,74,.16);padding-top:8px;display:none}
+ .musicroom.show-tracks .mr-tracks{display:block}
+ .mr-tracks button{display:flex;gap:9px;width:100%;text-align:left;background:none;border:0;color:var(--ink-dim);
+ font-family:var(--sans);font-size:11.5px;padding:5px 4px;cursor:pointer;border-radius:6px;line-height:1.3}
+ .mr-tracks button:hover{color:var(--ink);background:rgba(70,20,26,.5)}
+ .mr-tracks button[aria-current="true"]{color:var(--gold)}
+ .mr-tracks button .n{color:var(--ink-dim);min-width:20px;font-variant-numeric:tabular-nums;opacity:.7;flex:none}
+ .mr-foot{margin-top:11px;font-size:10px;line-height:1.55;color:var(--ink-dim)}
+ .mr-foot a{color:var(--gold);text-decoration:none;border-bottom:1px solid rgba(242,182,74,.4)}
+ .mr-msg{margin-top:9px;font-size:11px;line-height:1.5;color:var(--ink-dim)}
+
:focus-visible{outline:3px solid var(--focus);outline-offset:2px;border-radius:6px}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
@@ -132,6 +174,8 @@
.label-toggle{bottom:100px;right:12px}
.museum{bottom:148px;right:12px}
.chapters{top:auto;bottom:100px;right:12px;flex-direction:row}
+ .musicroom{top:auto;bottom:148px;left:12px;right:12px;width:auto}
+ .musicroom .mr-tracks{max-height:96px}
}
@media (prefers-reduced-motion: reduce){
.museum,.chapters button,.btn,.skip{transition:none}
@@ -146,11 +190,36 @@
<canvas id="scene" aria-hidden="true"></canvas>
<div class="titleplate">
- <p class="kicker">An Animated Gallery</p>
+ <p class="kicker">A Riverboat Listening Room</p>
<h1 aria-hidden="true">Scarlet Riverboat<br>Masquerade</h1>
<p class="chap" id="chapLabel">Chapter I · Departure</p>
</div>
+ <section class="musicroom" id="musicroom" data-open="true" aria-label="Music room — Grateful Dead Live Music Archive">
+ <header id="mrToggle" role="button" tabindex="0" aria-expanded="true" aria-controls="mrbody">
+ <span class="live-dot" aria-hidden="true"></span>
+ <span>Music Room</span>
+ <span class="chev" aria-hidden="true">▾</span>
+ </header>
+ <div class="mrbody" id="mrbody">
+ <select class="mr-show" id="mrShow" aria-label="Choose a recording from the Live Music Archive"></select>
+ <div class="mr-now" id="mrNow">Choose a night, then press play.</div>
+ <div class="mr-sub" id="mrSub"></div>
+ <div class="mr-transport">
+ <button class="btn" id="mrPrev" aria-label="Previous track">⏮</button>
+ <button class="btn play" id="mrPlay" aria-label="Play" aria-pressed="false">▶</button>
+ <button class="btn" id="mrNext" aria-label="Next track">⏭</button>
+ <button class="mr-tracks-toggle" id="mrTracksToggle" aria-expanded="false" aria-controls="mrTracks">Tracks</button>
+ </div>
+ <div class="mr-seek"><input type="range" id="mrSeek" min="0" max="1000" value="0" step="1" aria-label="Seek within track"></div>
+ <div class="mr-time"><span id="mrCur">0:00</span> / <span id="mrDur">0:00</span></div>
+ <div class="mr-tracks" id="mrTracks" role="list" aria-label="Set list"></div>
+ <div class="mr-foot" id="mrFoot"></div>
+ <div class="mr-msg" id="mrMsg" hidden></div>
+ </div>
+ <audio id="mrAudio" preload="none"></audio>
+ </section>
+
<nav class="chapters" aria-label="Chapters">
<button data-chapter="0" aria-current="true"><span class="dot" aria-hidden="true"></span>I · Departure</button>
<button data-chapter="1"><span class="dot" aria-hidden="true"></span>II · Moon Bend</button>
@@ -197,8 +266,10 @@
<button class="btn label-toggle" id="labelBtn" aria-expanded="false" aria-controls="museum">🏷 Museum Label</button>
<aside class="museum" id="museum" hidden aria-label="Museum label">
<h2>Scarlet Riverboat Masquerade</h2>
- <div class="meta">Generative Canvas · Authored Visual Rhythm · Single File</div>
- <p>A fantastical paddlewheel riverboat drifts through a hand-painted crimson canyon while masked revelers fill three decks. <strong>Every movement here is composed, not measured</strong> — the tempo you steer is an <em>authored visual cadence</em> written into the drawing loop itself, never an analysis of sound. There is no audio; the "rhythm" is pure choreography of light and line.</p>
+ <div class="meta">Generative Canvas · Live Music Archive · Authored Visual Rhythm</div>
+ <p>A fantastical paddlewheel riverboat drifts through a hand-painted crimson canyon while masked revelers fill three decks. This is a <strong>listening room</strong>: press play in the Music Room and a Grateful Dead concert streams from the Internet Archive's <strong>Live Music Archive</strong> while the boat turns around you.</p>
+ <p><strong>The motion is composed, not measured.</strong> The choreography is an <em>authored visual cadence</em> written into the drawing loop itself — it is deliberately <em>not</em> audio analysis and does not react to the waveform. Set the visual tempo to whatever feels right against the night's playing; the two rhythms — heard and drawn — keep company without one driving the other.</p>
+ <p><strong>The recordings are not copied or re-hosted here.</strong> They are community tapes made available through the Grateful Dead collection at the Internet Archive; playback is user-initiated and every set links back to its source item on archive.org.</p>
<p>The <strong>original visual language</strong> reduces each dancer to a weighted silhouette: couples pivot in shared arcs, a linked circle turns about the smokestack, soloists trail scarves, and musicians appear only as abstract shadow-forms — a communal, exploratory spirit rendered without any borrowed logo, emblem, or album art.</p>
<p>On the shore, four creatures each carry a distinct loop with believable weight — a <strong>trotting fox</strong>, a <strong>slow-stepping heron</strong>, a <strong>squash-and-stretch rabbit</strong>, and a <strong>bear conducting fireflies</strong>. Palette <em>and</em> choreography change across the three chapters as the boat travels from dusk (Departure) through moonrise (Moon Bend) into sunrise (Dawn Landing).</p>
<div class="keys">
@@ -981,5 +1052,156 @@ resize();
requestAnimationFrame(frame);
})();
</script>
+
+<script>
+/* ---------- Music Room: streams the Grateful Dead Live Music Archive (archive.org) ----------
+ Reuses the curated show catalog from the dead-agentabrams app. Listen-only:
+ audio streams straight from the Internet Archive, is user-initiated, and links
+ back to each source item. The visual scene stays authored — no audio analysis. */
+(() => {
+"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:'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 = 3; // Cornell '77
+
+const $ = id => document.getElementById(id);
+const audio = $('mrAudio'), room = $('musicroom'), sel = $('mrShow'), live = $('live');
+let showIdx=-1, tracks=[], trackIdx=-1, seeking=false;
+
+CATALOG.forEach((s,i)=>{ const o=document.createElement('option'); o.value=String(i); o.textContent=`${s.date} — ${s.title}`; sel.appendChild(o); });
+
+const fmt = t => { if(!isFinite(t)||t<0) t=0; const m=Math.floor(t/60); return m+':'+String(Math.floor(t%60)).padStart(2,'0'); };
+const announce = m => { if(live) live.textContent=m; };
+
+function setFoot(s){
+ const short = s.id.split('.')[0];
+ $('mrFoot').innerHTML =
+ `Source: <a href="https://archive.org/details/${s.id}" target="_blank" rel="noopener noreferrer">archive.org/details/${short}</a> — `+
+ `a community recording from the Grateful Dead Live Music Archive, streamed from the Internet Archive. Listen-only; nothing is copied or re-hosted here.`;
+}
+
+async function loadShow(i, opts){
+ opts=opts||{};
+ try{ audio.pause(); }catch(e){} // stop the current night before loading the next
+ showIdx=i; tracks=[]; trackIdx=-1;
+ const s=CATALOG[i];
+ sel.value=String(i);
+ $('mrNow').textContent=`${s.title} — loading set list…`;
+ $('mrSub').textContent=`${s.date} · ${s.sub}`;
+ $('mrTracks').innerHTML=''; $('mrMsg').hidden=true; $('mrMsg').innerHTML='';
+ setFoot(s);
+ try{
+ const r=await fetch(`https://archive.org/metadata/${s.id}`,{mode:'cors'});
+ if(!r.ok) throw new Error('metadata '+r.status);
+ const j=await r.json();
+ 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.sort((a,b)=> (a.name>b.name?1:-1));
+ tracks=mp3.map(f=>({
+ title:(f.title&&f.title.trim())?f.title.trim():f.name.replace(/\.mp3$/i,''),
+ url:`https://archive.org/download/${s.id}/${encodeURIComponent(f.name)}`
+ }));
+ 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)
+ $('mrNow').textContent=`♪ ${tracks[0].title}`;
+ highlight();
+ $('mrDur').textContent='0:00'; $('mrCur').textContent='0:00'; $('mrSeek').value='0';
+ announce(`Loaded ${s.title}, ${s.date}. ${tracks.length} tracks from the Live Music Archive.`);
+ if(opts.play) playTrack(0);
+ }catch(e){
+ fallbackEmbed(s);
+ }
+}
+
+function fallbackEmbed(s){
+ $('mrNow').textContent=s.title;
+ const msg=$('mrMsg'); msg.hidden=false;
+ const fileNote = location.protocol==='file:' ? ' Opening the file directly (file://) can block cross-site loading — serving it over http fixes it.' : '';
+ msg.innerHTML=`Couldn't load the set list here.${fileNote} `+
+ `<button class="btn" id="mrEmbedBtn" style="margin-top:8px">▶ Open the archive.org player</button>`;
+ $('mrEmbedBtn').onclick=()=>{
+ msg.innerHTML=`<iframe title="Internet Archive player for ${s.title}" src="https://archive.org/embed/${s.id}" width="100%" height="150" frameborder="0" style="border-radius:8px;border:1px solid var(--panel-edge)"></iframe>`;
+ room.classList.add('playing');
+ };
+}
+
+function renderTracks(){
+ const c=$('mrTracks'); c.innerHTML='';
+ tracks.forEach((t,i)=>{
+ const b=document.createElement('button'); b.type='button'; b.setAttribute('role','listitem');
+ b.innerHTML=`<span class="n">${String(i+1).padStart(2,'0')}</span><span>${t.title}</span>`;
+ b.addEventListener('click',()=>playTrack(i));
+ c.appendChild(b);
+ });
+}
+function highlight(){ [...$('mrTracks').children].forEach((b,i)=>b.setAttribute('aria-current', String(i===trackIdx))); }
+
+function playTrack(i){
+ if(i<0||i>=tracks.length) return;
+ if(i!==trackIdx || !audio.src){ trackIdx=i; audio.src=tracks[i].url; }
+ audio.play().catch(()=>setPlay(false));
+ $('mrNow').textContent=`♪ ${tracks[i].title}`;
+ highlight();
+ announce(`Now playing ${tracks[i].title}.`);
+}
+function setPlay(on){
+ room.classList.toggle('playing',on);
+ const b=$('mrPlay'); b.textContent=on?'❚❚':'▶';
+ b.setAttribute('aria-pressed',String(on)); b.setAttribute('aria-label',on?'Pause':'Play');
+}
+
+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',()=>{
+ const d=audio.duration||0, c=audio.currentTime||0;
+ $('mrCur').textContent=fmt(c); $('mrDur').textContent=fmt(d);
+ if(d>0 && !seeking) $('mrSeek').value=String(Math.round(c/d*1000));
+});
+audio.addEventListener('error',()=>{ if(showIdx>=0 && !tracks.length) fallbackEmbed(CATALOG[showIdx]); });
+
+$('mrPlay').addEventListener('click',()=>{
+ if(!tracks.length){ loadShow(showIdx<0?DEFAULT_SHOW:showIdx,{play:true}); return; }
+ if(trackIdx<0){ playTrack(0); return; }
+ if(audio.paused) audio.play().catch(()=>{}); else audio.pause();
+});
+$('mrPrev').addEventListener('click',()=>{ if(tracks.length) playTrack(Math.max(0,trackIdx-1)); });
+$('mrNext').addEventListener('click',()=>{ if(tracks.length) playTrack(Math.min(tracks.length-1,trackIdx+1)); });
+
+const seek=$('mrSeek');
+seek.addEventListener('input',()=>{ seeking=true; });
+seek.addEventListener('change',()=>{ const d=audio.duration||0; if(d>0) audio.currentTime=(+seek.value/1000)*d; seeking=false; });
+
+sel.addEventListener('change',()=>loadShow(+sel.value,{play:true}));
+
+$('mrTracksToggle').addEventListener('click',()=>{
+ const on=room.classList.toggle('show-tracks');
+ $('mrTracksToggle').setAttribute('aria-expanded',String(on));
+});
+
+const mrToggle=$('mrToggle');
+function toggleRoom(){
+ const open=room.getAttribute('data-open')!=='false';
+ room.setAttribute('data-open',String(!open));
+ mrToggle.setAttribute('aria-expanded',String(!open));
+}
+mrToggle.addEventListener('click',toggleRoom);
+mrToggle.addEventListener('keydown',e=>{ if(e.key==='Enter'||e.key===' '){ e.preventDefault(); toggleRoom(); } });
+
+// small screens: start collapsed so the art leads
+if(window.matchMedia && matchMedia('(max-width:820px)').matches){
+ room.setAttribute('data-open','false'); mrToggle.setAttribute('aria-expanded','false');
+}
+
+// cue the default night (no autoplay — the source policy is user-initiated playback)
+loadShow(DEFAULT_SHOW);
+})();
+</script>
</body>
</html>
← 8187692 Harden shade(): clamp t to [0,1] and channels to [0,255] (co
·
back to Scarlet Riverboat Masquerade
·
Add conditional DEAD back-link (shown only when served as /r 2b38b3c →