File size: 13,021 Bytes
0a8d25d
3cb1e08
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0a8d25d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>QUANTCLASSIC</title>
<meta name="description" content="A quantum radio for classical music. Every great public-domain theme plays at once, quietly β€” a superposition, all at once and none. Click one to measure it: its wavefunction collapses, it rings out clear, the rest hush.">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;1,400&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root{--bg:#0b0a12;--ink:#efe7d6;--dim:#8f86a6;--gold:#e6c874;--cyan:#66e0ff;--line:#241f38;
  --serif:'Cormorant Garamond',Georgia,serif;--mono:'JetBrains Mono',monospace}
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent;user-select:none}
html,body{height:100%;overflow:hidden;background:var(--bg);color:var(--ink);font-family:var(--serif)}
#stage{position:fixed;inset:0;display:flex;flex-direction:column}
canvas#bg{position:absolute;inset:0;z-index:0;opacity:.5}
header{position:relative;z-index:2;text-align:center;padding:22px 16px 6px}
h1{font-family:var(--mono);font-weight:500;font-size:clamp(20px,4.5vw,30px);letter-spacing:.34em;color:var(--gold)}
.sub{font-style:italic;font-size:clamp(16px,3vw,21px);color:var(--dim);margin-top:2px}
#field{position:relative;z-index:2;flex:1;overflow:hidden}
.orb{position:absolute;transform:translate(-50%,-50%);width:var(--s);height:var(--s);border-radius:50%;
  display:grid;place-items:center;text-align:center;cursor:pointer;transition:width .5s cubic-bezier(.2,.8,.2,1),height .5s cubic-bezier(.2,.8,.2,1),opacity .5s;
  border:1px solid rgba(255,255,255,.08)}
.orb .c{font-family:var(--mono);font-size:10px;letter-spacing:.12em;color:rgba(255,255,255,.85);text-transform:uppercase}
.orb .p{font-style:italic;font-size:13px;line-height:1.15;color:rgba(255,255,255,.7);padding:0 6px;margin-top:1px}
.orb.focus{box-shadow:0 0 50px var(--glow),0 0 12px #fff inset;border-color:rgba(255,255,255,.5);z-index:3}
.orb.focus .p{color:#fff}
.orb.dim{opacity:.28}
#now{position:relative;z-index:2;text-align:center;padding:6px 16px 4px;min-height:2.2em}
#now .l{font-family:var(--mono);font-size:9px;letter-spacing:.3em;color:var(--dim);text-transform:uppercase}
#now .t{font-style:italic;font-size:clamp(19px,4vw,27px);color:var(--gold);transition:color .4s}
#now.super .t{color:var(--dim)}
footer{position:relative;z-index:2;display:flex;gap:12px;justify-content:center;align-items:center;padding:12px 16px 20px;flex-wrap:wrap}
button{font-family:var(--mono);font-size:12px;letter-spacing:.06em;cursor:pointer;color:var(--ink);background:rgba(40,36,64,.6);border:1px solid var(--line);border-radius:22px;padding:9px 18px;transition:.2s}
button:hover{border-color:var(--cyan);color:var(--cyan)}
button.on{background:rgba(102,224,255,.14);border-color:var(--cyan);color:var(--cyan)}
.hint{font-family:var(--mono);font-size:9.5px;letter-spacing:.14em;color:#544c6a;text-transform:uppercase;width:100%;text-align:center;margin-top:2px}.hint .f{color:#a08a4a}
#gate{position:absolute;inset:0;z-index:20;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:24px;background:radial-gradient(120% 90% at 50% 35%,rgba(230,200,116,.06),transparent),rgba(11,10,18,.92)}
#gate.hide{display:none}
#gate .tag{font-size:clamp(20px,4vw,28px);font-style:italic;max-width:32rem;line-height:1.4;color:var(--ink)}
#gate .tag em{color:var(--gold)}
#gate .small{font-family:var(--mono);font-size:11px;color:var(--dim);letter-spacing:.1em;margin-top:14px;line-height:1.7}
#gate button{font-family:var(--serif);font-style:italic;font-size:24px;color:#140f04;background:linear-gradient(180deg,var(--gold),#c8a24a);border:0;border-radius:30px;padding:12px 40px;margin-top:26px}
@media(prefers-reduced-motion:reduce){*{transition:none!important}}
</style>
</head>
<body>
<div id="stage">
  <canvas id="bg"></canvas>
  <header>
    <h1>QUANTCLASSIC</h1>
    <div class="sub">a quantum radio Β· every great theme at once, and none</div>
  </header>
  <div id="field"></div>
  <div id="now" class="super"><div class="l">state</div><div class="t" id="now-t">superposition β€” all at once, and none</div></div>
  <footer>
    <button id="super-btn">β–š superpose all</button>
    <button id="shuffle-btn">⟳ quantshuffle</button>
    <div class="hint">πŸœ‚ measurement collapses the chord to one voice &middot; <span class="f">ahogy a dolgok vannak</span></div>
  </footer>
  <div id="gate">
    <p class="tag">Every great theme is playing right now β€” <em>Beethoven, Bach, Mozart, Vivaldi</em> β€” all at once, so quietly it is almost nothing. A <em>superposition</em> of the whole canon.</p>
    <p class="small">Click a voice to <em style="color:var(--cyan)">measure</em> it β€” its wavefunction collapses and it rings out alone.<br>All public domain. All synthesized live, no recordings.</p>
    <button id="gate-btn">β–Ά raise the baton</button>
  </div>
</div>

<script>
(function(){
"use strict";
// ── the canon (public domain), as playable themes ───────────────────────────
// notes: [midi, beats]. Each voice loops at its own tempo, so superposed they blur.
function mtof(m){ return 440*Math.pow(2,(m-69)/12); }
var THEMES=[
 {c:'Beethoven', p:'Symphony No. 5', hue:8,  wave:'triangle', bpm:132, gain:1.0,
  n:[[67,.25],[67,.25],[67,.25],[63,1.1],[65,.25],[65,.25],[65,.25],[62,1.1],[0,.4]]},
 {c:'Beethoven', p:'Ode to Joy', hue:44, wave:'triangle', bpm:118, gain:.9,
  n:[[64,.5],[64,.5],[65,.5],[67,.5],[67,.5],[65,.5],[64,.5],[62,.5],[60,.5],[60,.5],[62,.5],[64,.5],[64,.75],[62,.25],[62,1]]},
 {c:'Beethoven', p:'FΓΌr Elise', hue:300, wave:'sine', bpm:76, gain:.85,
  n:[[76,.25],[75,.25],[76,.25],[75,.25],[76,.25],[71,.25],[74,.25],[72,.25],[69,.6],[0,.3]]},
 {c:'Mozart', p:'Eine kleine Nachtmusik', hue:120, wave:'triangle', bpm:130, gain:.85,
  n:[[67,.25],[62,.25],[67,.25],[62,.25],[67,.25],[71,.25],[74,.5],[62,.25],[57,.25],[62,.25],[57,.25],[62,.25],[66,.25],[69,.5]]},
 {c:'Bach', p:'Toccata in D minor', hue:264, wave:'sawtooth', bpm:84, gain:.7,
  n:[[69,.5],[67,.25],[69,.75],[0,.25],[64,.5],[62,.5],[61,.5],[57,1.2],[0,.4]]},
 {c:'Pachelbel', p:'Canon in D', hue:190, wave:'sine', bpm:100, gain:.8,
  n:[[78,.5],[76,.5],[74,.5],[73,.5],[71,.5],[69,.5],[71,.5],[73,.5]]},
 {c:'Vivaldi', p:'Spring', hue:96, wave:'triangle', bpm:120, gain:.8,
  n:[[76,.4],[76,.4],[76,.4],[71,.8],[71,.8],[73,.4],[73,.4],[71,.9],[0,.3]]},
 {c:'Mozart', p:'Symphony No. 40', hue:220, wave:'sine', bpm:108, gain:.8,
  n:[[75,.25],[75,.25],[77,.5],[75,.25],[75,.25],[77,.5],[75,.5],[82,.5],[81,1],[0,.3]]}
];

var actx=null, master=null, voices=[], sched=null, started=false, focusIdx=-1, shuffleOn=false, shuffleT=null;

function buildAudio(){
  actx=new (window.AudioContext||window.webkitAudioContext)();
  master=actx.createGain(); master.gain.value=0.9;
  var lp=actx.createBiquadFilter(); lp.type='lowpass'; lp.frequency.value=5200;
  master.connect(lp); lp.connect(actx.destination);
  voices=THEMES.map(function(th){
    var g=actx.createGain(); g.gain.value=0.06*th.gain; g.connect(master);
    return { th:th, g:g, i:0, next:actx.currentTime+0.1 };
  });
}
function playNote(v, m, dur, when){
  if(m===0) return; // rest
  var o=actx.createOscillator(), e=actx.createGain();
  o.type=v.th.wave; o.frequency.value=mtof(m);
  var a=0.012, rel=Math.min(0.25,dur*0.4), peak=0.5;
  e.gain.setValueAtTime(0,when); e.gain.linearRampToValueAtTime(peak,when+a);
  e.gain.setValueAtTime(peak,when+Math.max(a,dur-rel)); e.gain.exponentialRampToValueAtTime(0.0006,when+dur);
  o.connect(e); e.connect(v.g); o.start(when); o.stop(when+dur+0.02);
}
function scheduler(){
  var ahead=actx.currentTime+0.12;
  voices.forEach(function(v){
    var spb=60/v.th.bpm;
    while(v.next < ahead){
      var note=v.th.n[v.i], dur=note[1]*spb;
      playNote(v, note[0], dur*0.96, v.next);
      v.next += dur; v.i=(v.i+1)%v.th.n.length;
    }
  });
}
function setMix(){
  voices.forEach(function(v,i){
    var target = focusIdx<0 ? 0.06*v.th.gain : (i===focusIdx ? 0.55 : 0.012);
    v.g.gain.setTargetAtTime(target, actx.currentTime, 0.25);
  });
}

// ── orbs ─────────────────────────────────────────────────────────────────────
var field=document.getElementById('field'), orbEls=[];
function layout(){
  var fw=field.clientWidth, fh=field.clientHeight, n=THEMES.length;
  var cx=fw/2, cy=fh/2, rad=Math.min(fw,fh)*0.36;
  THEMES.forEach(function(th,i){
    var ang=(i/n)*Math.PI*2 - Math.PI/2;
    var el=orbEls[i];
    var x=cx+Math.cos(ang)*rad, y=cy+Math.sin(ang)*rad;
    el.style.left=x+'px'; el.style.top=y+'px';
  });
}
THEMES.forEach(function(th,i){
  var el=document.createElement('div'); el.className='orb'; el.dataset.i=i;
  var base=Math.min(innerWidth,innerHeight)*0.16;
  el.style.setProperty('--s', Math.max(78,base)+'px');
  el.style.setProperty('--glow','hsl('+th.hue+',80%,55%)');
  el.style.background='radial-gradient(circle at 38% 32%, hsla('+th.hue+',85%,68%,.5), hsla('+th.hue+',80%,42%,.28) 60%, hsla('+th.hue+',60%,20%,.15))';
  el.innerHTML='<div><div class="c">'+th.c+'</div><div class="p">'+th.p+'</div></div>';
  el.addEventListener('click',function(){ if(!started)return; measure(i); });
  field.appendChild(el); orbEls.push(el);
});
addEventListener('resize', layout);

function measure(i){
  focusIdx=i; setMix();
  orbEls.forEach(function(el,k){
    var base=Math.min(innerWidth,innerHeight)*0.16;
    el.classList.toggle('focus', k===i); el.classList.toggle('dim', k!==i);
    el.style.setProperty('--s', (k===i? Math.max(120,base*1.5): Math.max(66,base*0.85))+'px');
  });
  var d=document.getElementById('now'); d.classList.remove('super');
  document.getElementById('now-t').textContent=THEMES[i].c+' β€” '+THEMES[i].p;
}
function superpose(){
  focusIdx=-1; setMix();
  orbEls.forEach(function(el){ var base=Math.min(innerWidth,innerHeight)*0.16;
    el.classList.remove('focus','dim'); el.style.setProperty('--s', Math.max(78,base)+'px'); });
  var d=document.getElementById('now'); d.classList.add('super');
  document.getElementById('now-t').textContent='superposition β€” all at once, and none';
}

function startAll(){
  if(started) return; started=true;
  buildAudio(); layout();
  sched=setInterval(scheduler, 25);
  document.getElementById('gate').classList.add('hide');
  superpose();
}
document.getElementById('gate-btn').addEventListener('click', startAll);
document.getElementById('super-btn').addEventListener('click', function(){ if(started){ if(shuffleOn)toggleShuffle(); superpose(); } });
document.getElementById('shuffle-btn').addEventListener('click', function(){ if(started) toggleShuffle(); });
function toggleShuffle(){
  shuffleOn=!shuffleOn; document.getElementById('shuffle-btn').classList.toggle('on',shuffleOn);
  if(shuffleOn){ measure(Math.floor(Math.random()*THEMES.length));
    shuffleT=setInterval(function(){ measure(Math.floor(Math.random()*THEMES.length)); }, 9000); }
  else { clearInterval(shuffleT); }
}

// ── quantum interference background (canvas, low-res) ────────────────────────
(function(){
  var c=document.getElementById('bg'), ctx=c.getContext('2d');
  var reduce=matchMedia('(prefers-reduced-motion: reduce)').matches;
  var LW=100,LH=60, off=document.createElement('canvas'); off.width=LW;off.height=LH;
  var octx=off.getContext('2d'), img=octx.createImageData(LW,LH);
  function rs(){ c.width=innerWidth;c.height=innerHeight; } rs(); addEventListener('resize',rs);
  var t=0;
  function fr(){ t+=0.01; var d=img.data,p=0;
    for(var y=0;y<LH;y++)for(var x=0;x<LW;x++){
      var ux=(x/LW-0.5)*(LW/LH)*4, uy=(y/LH-0.5)*4, re=0,im=0;
      for(var i=0;i<4;i++){var a=1.2+i*1.5,kx=Math.cos(a)*(1.3+i*.5),ky=Math.sin(a)*(1.3+i*.5),w=.4+i*.15,ph=kx*ux+ky*uy-w*t;re+=Math.cos(ph);im+=Math.sin(ph);}
      re/=4;im/=4;var mag=Math.sqrt(re*re+im*im),phase=Math.atan2(im,re);
      var h=(0.55+(phase/6.283+0.5)*0.3)%1, r=Math.max(0,Math.abs(h*6-3)-1),g=Math.max(0,2-Math.abs(h*6-2)),b=Math.max(0,2-Math.abs(h*6-4)),br=mag*mag*0.32+0.01;
      d[p++]=Math.min(1,r)*br*255;d[p++]=Math.min(1,g)*br*255;d[p++]=Math.min(1,b)*br*255;d[p++]=255;
    }
    octx.putImageData(img,0,0); ctx.clearRect(0,0,c.width,c.height); ctx.imageSmoothingEnabled=true; ctx.drawImage(off,0,0,c.width,c.height);
    if(!reduce) requestAnimationFrame(fr);
  }
  fr();
})();

window.__qc={ start:startAll, measure:measure, superpose:superpose,
  state:function(){return{started:started,focus:focusIdx,voices:voices.length,ctx:actx?actx.state:'none'};} };
})();
</script>
</body>
</html>