← back to Aurora Drift
All lights go to the beat: gentle rhythmic tremolo on the ambient pad + a live audio analyser drives aurora brightness, cabin glow, and the 3D orb (soft authored breathing when muted)
608ae8ad1b834f6feb33f0932463d7b7686d2b54 · 2026-09-02 07:40:06 -0700 · Steve Abrams
Files touched
Diff
commit 608ae8ad1b834f6feb33f0932463d7b7686d2b54
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Sep 2 07:40:06 2026 -0700
All lights go to the beat: gentle rhythmic tremolo on the ambient pad + a live audio analyser drives aurora brightness, cabin glow, and the 3D orb (soft authored breathing when muted)
---
index.html | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/index.html b/index.html
index 206a2ad..cd7d2f4 100644
--- a/index.html
+++ b/index.html
@@ -152,9 +152,11 @@ const MOODS={
violet: { name:'Violet Veil', cols:[[140,120,255],[200,120,255],[120,160,255],[170,110,230]], root:65 }
};
// ---- ambient synth pad (original, no audio files) ----
-let actx=null, master=null, padOsc=[], padGain=null, padLfo=null;
-function ensureAudio(){ if(actx) return true; try{ actx=new (window.AudioContext||window.webkitAudioContext)(); master=actx.createGain(); master.gain.value=S.volume; master.connect(actx.destination); return true; }catch(e){ return false; } }
-function stopPad(){ padOsc.forEach(o=>{try{o.stop();}catch(e){}}); padOsc=[]; if(padLfo){try{padLfo.stop();}catch(e){}} padLfo=null; padGain=null; }
+let actx=null, master=null, padOsc=[], padGain=null, padLfo=null, padTrem=null, analyser=null, freqData=null;
+function ensureAudio(){ if(actx) return true; try{ actx=new (window.AudioContext||window.webkitAudioContext)(); master=actx.createGain(); master.gain.value=S.volume;
+ analyser=actx.createAnalyser(); analyser.fftSize=64; analyser.smoothingTimeConstant=0.8; freqData=new Uint8Array(analyser.frequencyBinCount);
+ master.connect(analyser); master.connect(actx.destination); return true; }catch(e){ return false; } }
+function stopPad(){ padOsc.forEach(o=>{try{o.stop();}catch(e){}}); padOsc=[]; [padLfo,padTrem].forEach(o=>{if(o){try{o.stop();}catch(e){}}}); padLfo=padTrem=null; padGain=null; }
function startPad(){
if(!actx) return; stopPad();
const root=MOODS[S.mood].root, t=actx.currentTime;
@@ -164,6 +166,8 @@ function startPad(){
[1,1.5,2,3].forEach((mult,i)=>{ const o=actx.createOscillator(); o.type=i>2?'triangle':'sine'; o.frequency.value=root*mult*(1+(Math.random()-0.5)*0.004); o.connect(pg); o.start(); padOsc.push(o); });
pg.gain.linearRampToValueAtTime(0.14, t+2.5); // slow swell in
padGain=pg;
+ // gentle rhythmic tremolo so the lights have a beat to follow (~54 bpm pulse)
+ padTrem=actx.createOscillator(); const tg=actx.createGain(); padTrem.frequency.value=0.9; tg.gain.value=0.05; padTrem.connect(tg); tg.connect(pg.gain); padTrem.start();
}
if(window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches) S.reduced=true;
const announce=m=>{ live.textContent=m; };
@@ -211,7 +215,7 @@ function auroraPath(g, yTop, band, seed, camScale){
}
function drawAuroraInto(c, flip){
if(S.aurora<=0.02) return;
- const W=S.W,H=S.H, hz=HORIZON(), intensity=S.aurora;
+ const W=S.W,H=S.H, hz=HORIZON(), intensity=S.aurora*(0.72+(S.pulse||0)*0.55); // aurora brightens to the beat
c.save(); c.globalCompositeOperation='lighter';
const bands=4;
for(let b=0;b<bands;b++){
@@ -259,7 +263,7 @@ function drawCabin(){
ctx.fillRect(x,y-22,40,22);
ctx.beginPath(); ctx.moveTo(x-4,y-22); ctx.lineTo(x+20,y-36); ctx.lineTo(x+44,y-22); ctx.closePath(); ctx.fill();
// warm window
- const wx=x+15, wy=y-14, glow=0.7+0.3*Math.sin(S.t*2);
+ const wx=x+15, wy=y-14, glow=(0.6+0.4*Math.sin(S.t*2))*(0.8+(S.pulse||0)*0.7); // cabin window pulses to the beat
const g=ctx.createRadialGradient(wx,wy,1,wx,wy,20); g.addColorStop(0,`rgba(255,207,110,${0.5*glow})`); g.addColorStop(1,'rgba(0,0,0,0)');
ctx.fillStyle=g; ctx.beginPath(); ctx.arc(wx,wy,20,0,7); ctx.fill();
ctx.fillStyle=`rgba(255,207,110,${0.9*glow})`; ctx.fillRect(wx-4,wy-4,10,10);
@@ -315,6 +319,9 @@ let last=performance.now();
function frame(now){
const dt=Math.min(0.05,(now-last)/1000); last=now;
if(S.playing){ S.t += dt*S.tempo*(S.reduced?0.3:1); }
+ // beat pulse: from the live audio when sound is on, else a soft authored breathing
+ if(analyser && S.sound){ analyser.getByteFrequencyData(freqData); let sum=0,n=Math.max(1,freqData.length>>1); for(let i=0;i<n;i++) sum+=freqData[i]; const raw=(sum/n)/255; S.pulse=(S.pulse||0)*0.7+raw*0.3; }
+ else { S.pulse=0.30+0.30*Math.sin(S.t*1.4); }
if(skyDirty) drawSky();
const W=S.W,H=S.H;
if(S.trails && !S.reduced && S.playing){ ctx.fillStyle='rgba(3,8,16,0.16)'; ctx.fillRect(0,0,W,H); } else ctx.clearRect(0,0,W,H);
@@ -397,9 +404,10 @@ resize(); requestAnimationFrame(frame);
const col=new THREE.Color();
addEventListener('resize',()=>{ W=innerWidth;H=innerHeight; camera.aspect=W/H; camera.updateProjectionMatrix(); renderer.setSize(W,H,false); });
function loop(){ requestAnimationFrame(loop);
- const s=window.__auroraS||{}, t=performance.now()/1000, paused=(s.playing===false)||s.reduced;
+ const s=window.__auroraS||{}, t=performance.now()/1000, paused=(s.playing===false)||s.reduced, pulse=s.pulse||0;
if(!paused) group.rotation.y+=0.005; group.rotation.x=Math.sin(t*0.2)*0.15;
- group.scale.setScalar(1+0.06*Math.sin(t*1.1));
+ group.scale.setScalar(1+0.06*Math.sin(t*1.1)+pulse*0.22);
+ core.material.emissiveIntensity=0.4+pulse*1.3;
const hue=(160+Math.sin(t*0.25)*40)/360; col.setHSL(hue,0.7,0.55); core.material.emissive.copy(col); wire.material.color.setHSL((hue+0.08)%1,0.7,0.7);
renderer.render(scene,camera);
}
← 1c90e32 Add sound + selection controller to Aurora: original ambient
·
back to Aurora Drift
·
(newest)