← back to Rose City Rooftop Solstice
Add optional live Grateful Dead music streamed from the Internet Archive (keyless public API); honest note updated
632cd635166b6373c7016115e4eafe3831d1cfd2 · 2026-09-01 17:11:36 -0700 · Steve Abrams
Files touched
Diff
commit 632cd635166b6373c7016115e4eafe3831d1cfd2
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Sep 1 17:11:36 2026 -0700
Add optional live Grateful Dead music streamed from the Internet Archive (keyless public API); honest note updated
---
README.md | 17 +++++++++
index.html | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 140 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index ec0b831..d20b500 100644
--- a/README.md
+++ b/README.md
@@ -40,6 +40,23 @@ or you can pin one.
Keyboard: `Space` pause · `1 2 3` cameras · `R` restart · `N` names ·
`T` trails · `D` diagrams · `M` reduced motion · `C` hide panel.
+## Music (optional, from the archive)
+
+The control dock has a **Music · from the archive** section that streams live
+**Grateful Dead** recordings from the [Internet Archive](https://archive.org)'s
+`GratefulDead` collection — a public, keyless API. The band was famously
+taping-friendly, and these recordings are shared with their blessing; each
+now-playing show links back to its archive.org item for credit.
+
+- **Play music** starts a curated, verified show (Cornell '77 by default).
+- **Shuffle show** queries the archive live and loads a random popular show.
+- **⏮ / ⏭** step tracks; the volume slider is independent of visual tempo.
+
+This is the *only* part of the piece that touches the network, and it's **audio
+only** — it starts on a click (respecting autoplay rules), and if you're offline
+it simply says so. The visuals stay fully self-contained and, per the honest
+note, **never** react to the audio.
+
## An honest note
The piece keeps its own time. Its rhythm, swells and blooms are **authored** — a
diff --git a/index.html b/index.html
index e85ac7a..9d79b0a 100644
--- a/index.html
+++ b/index.html
@@ -229,6 +229,21 @@
<div class="row"><label for="tgDiag">Route diagrams</label><input type="checkbox" id="tgDiag" /></div>
</div>
+ <div class="grp" role="group" aria-label="Music from the Internet Archive">
+ <h2 style="margin-top:0">Music · from the archive</h2>
+ <p class="hint" id="npLine">Optional: live Grateful Dead recordings streamed from the Internet Archive. Even while it plays, the visuals never sync to the audio — their rhythm stays authored.</p>
+ <div class="actions">
+ <button class="btn" id="mPlay" aria-pressed="false">Play music</button>
+ <button class="btn" id="mShuffle">Shuffle show</button>
+ </div>
+ <div class="row" style="margin-top:8px;gap:8px">
+ <button class="btn" id="mPrev" aria-label="Previous track">⏮</button>
+ <button class="btn" id="mNext" aria-label="Next track">⏭</button>
+ <input type="range" id="mVol" min="0" max="1" step="0.05" value="0.55" aria-label="Music volume" style="flex:1" />
+ </div>
+ <p class="hint" id="mCredit" style="margin-bottom:0"></p>
+ </div>
+
<div class="grp" role="group" aria-label="Accessibility & playback">
<div class="row"><label for="tgReduce">Reduced motion (still composition)</label><input type="checkbox" id="tgReduce" /></div>
<div class="actions">
@@ -248,7 +263,7 @@
<button class="btn dock-toggle" id="dockToggle" aria-expanded="true" aria-controls="controls">Hide panel</button>
<div class="toolbar">
- <p class="note"><b>An honest note.</b> This piece keeps time on its own. Its rhythm, swells and blooms are <b>authored</b> — a composed visual tempo — <em>not</em> reactive to any music or microphone. Turn on <b>Reduced motion</b> for a calm, still composition.</p>
+ <p class="note"><b>An honest note.</b> This piece keeps time on its own. Its rhythm, swells and blooms are <b>authored</b> — a composed visual tempo — <em>not</em> reactive to any music or microphone. Optional music streams live from the <b>Internet Archive</b>’s Grateful Dead collection; even with it playing, the visuals never sync to the audio. Turn on <b>Reduced motion</b> for a calm, still composition.</p>
</div>
<script>
@@ -1258,6 +1273,113 @@ window.addEventListener('keydown',e=>{
}
});
+/* ===================================================================
+ Optional live music — Grateful Dead recordings streamed from the
+ Internet Archive (archive.org). Public, keyless API. The band was
+ famously taping-friendly; these are shared with their blessing.
+ NOTE: this adds a runtime network dependency for AUDIO ONLY. The
+ visuals stay fully self-contained/offline and never react to sound.
+ =================================================================== */
+const MUSIC = {
+ // curated, verified-real item ids — filenames are always fetched at runtime, never hardcoded
+ shows:[
+ {id:'gd77-05-08.sbd.hicks.4982.sbeok.shnf', label:'Barton Hall, Cornell · May 8, 1977'},
+ {id:'gd77-05-07.sbd.eaton.wizard.26085.sbeok.shnf',label:'Boston Garden · May 7, 1977'},
+ {id:'gd77-05-09.sbd.connor.8304.sbeok.shnf', label:'Buffalo Memorial Aud. · May 9, 1977'},
+ {id:'gd73-06-10.sbd.hollister.174.sbeok.shnf', label:'RFK Stadium · June 10, 1973'},
+ {id:'gd73-02-15.sbd.hall.1580.sbeok.shnf', label:'Dane County Coliseum · Feb 15, 1973'},
+ {id:'gd71-08-06.aud.bertrando.yerys.129.sbeok.shnf',label:'Hollywood Palladium · Aug 6, 1971'},
+ ],
+ audio:new Audio(), playlist:[], idx:0, showId:'', showLabel:'', loading:false, wantPlay:false,
+};
+MUSIC.audio.preload='none';
+MUSIC.audio.volume=0.55;
+
+const iaItem = id => 'https://archive.org/details/'+id;
+const iaFile = (id,name) => 'https://archive.org/download/'+id+'/'+encodeURIComponent(name);
+const npEl=()=>document.getElementById('npLine');
+function setNP(t){ const e=npEl(); if(e) e.textContent=t; }
+function setCredit(id){
+ const c=document.getElementById('mCredit'); if(!c) return;
+ if(!id){ c.textContent=''; return; }
+ c.innerHTML='Source: <a href="'+iaItem(id)+'" target="_blank" rel="noopener noreferrer">archive.org ↗</a> · taping-friendly recording, shared with the band’s blessing.';
+}
+function updateMusicBtn(playing){
+ const b=document.getElementById('mPlay'); if(!b) return;
+ b.textContent=playing?'Pause music':'Play music'; b.setAttribute('aria-pressed',String(playing));
+}
+
+async function loadShow(show, autoplay, silent){
+ MUSIC.wantPlay = MUSIC.wantPlay || !!autoplay;
+ if(MUSIC.loading) return;
+ MUSIC.loading=true;
+ if(!silent) setNP('Loading '+show.label+' …');
+ try{
+ const r=await fetch('https://archive.org/metadata/'+show.id);
+ const d=await r.json();
+ let files=(d.files||[]).filter(f=>/\.mp3$/i.test(f.name||''));
+ const vbr=files.filter(f=>String(f.format||'').toUpperCase().indexOf('VBR')>=0);
+ if(vbr.length) files=vbr; // one derivative per track
+ files.sort((a,b)=> (a.name<b.name?-1:a.name>b.name?1:0));
+ if(!files.length) throw new Error('no mp3 files');
+ MUSIC.showId=show.id; MUSIC.showLabel=show.label; MUSIC.idx=0;
+ MUSIC.playlist=files.map(f=>({url:iaFile(show.id,f.name), title:(f.title||f.name.replace(/\.mp3$/i,'')).replace(/^gd[\d\-]+[a-z]*-?/i,'').trim()||f.name}));
+ MUSIC.loading=false;
+ setCredit(show.id);
+ if(MUSIC.wantPlay) playTrack(0);
+ else if(!silent) setNP('Ready — '+show.label);
+ }catch(e){
+ MUSIC.loading=false;
+ if(!silent) setNP('Couldn’t reach the Internet Archive — check your connection, then try again.');
+ }
+}
+function playTrack(i){
+ if(!MUSIC.playlist.length){ MUSIC.wantPlay=true; loadShow(MUSIC.shows[0],true); return; }
+ MUSIC.idx=((i%MUSIC.playlist.length)+MUSIC.playlist.length)%MUSIC.playlist.length;
+ const t=MUSIC.playlist[MUSIC.idx];
+ MUSIC.wantPlay=true;
+ MUSIC.audio.src=t.url;
+ setNP('♪ '+t.title+' — '+MUSIC.showLabel);
+ const p=MUSIC.audio.play();
+ if(p&&p.catch) p.catch(()=>{ updateMusicBtn(false); setNP('Tap “Play music” once more to allow audio.'); });
+}
+MUSIC.audio.addEventListener('ended', ()=>playTrack(MUSIC.idx+1));
+MUSIC.audio.addEventListener('error', ()=>{ if(MUSIC.playlist.length && MUSIC.idx<MUSIC.playlist.length-1) playTrack(MUSIC.idx+1); });
+MUSIC.audio.addEventListener('play', ()=>updateMusicBtn(true));
+MUSIC.audio.addEventListener('pause', ()=>updateMusicBtn(false));
+
+async function liveShuffle(){
+ setNP('Finding a show in the archive …');
+ MUSIC.wantPlay=true;
+ try{
+ const q=encodeURIComponent('collection:GratefulDead AND format:(VBR MP3)');
+ const r=await fetch('https://archive.org/advancedsearch.php?q='+q+'&fl[]=identifier&fl[]=title&sort[]=downloads+desc&rows=60&output=json');
+ const d=await r.json();
+ const docs=(d.response&&d.response.docs)||[];
+ if(!docs.length) throw new Error('empty');
+ const pick=docs[Math.floor(rnd()*docs.length)];
+ const label=String(pick.title||'Grateful Dead').replace(/^Grateful Dead Live at\s*/i,'').replace(/\s+on\s+/,' · ');
+ MUSIC.loading=false; loadShow({id:pick.identifier,label}, true);
+ }catch(e){
+ MUSIC.loading=false;
+ loadShow(MUSIC.shows[Math.floor(rnd()*MUSIC.shows.length)], true); // fallback: curated
+ }
+}
+
+// wiring
+document.getElementById('mPlay').addEventListener('click',()=>{
+ if(MUSIC.audio.src && !MUSIC.audio.paused){ MUSIC.audio.pause(); return; }
+ if(MUSIC.audio.src){ const p=MUSIC.audio.play(); if(p&&p.catch)p.catch(()=>{}); return; }
+ if(MUSIC.playlist.length) playTrack(MUSIC.idx);
+ else { MUSIC.wantPlay=true; loadShow(MUSIC.shows[0], true); }
+});
+document.getElementById('mNext').addEventListener('click',()=>playTrack(MUSIC.idx+1));
+document.getElementById('mPrev').addEventListener('click',()=>playTrack(MUSIC.idx-1));
+document.getElementById('mShuffle').addEventListener('click',liveShuffle);
+document.getElementById('mVol').addEventListener('input',e=>{ MUSIC.audio.volume=+e.target.value; });
+// silent metadata prefetch so the first Play is instant (no audio, no autoplay, leaves the note text intact)
+setTimeout(()=>{ if(!MUSIC.playlist.length && !MUSIC.loading) loadShow(MUSIC.shows[0], false, true); }, 1800);
+
/* ============================ BOOT ============================ */
resize();
syncSceneUI();
← 77a9df8 Rose City Rooftop Solstice — self-contained animated rooftop
·
back to Rose City Rooftop Solstice
·
Make music LIVE AUDIO-REACTIVE: decode archive.org tracks th 9c4c7bf →