the-one-button / index.html
acer
Claude Opus 5 (1M context)
81 and a nought
90a1397
Raw History Blame Contribute Delete
5.24 kB
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>81 and a nought</title>
<style>
html,body{margin:0;padding:0;background:#101010;color:#e8e6e3;overflow:hidden;
font:13px/1.5 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
canvas{display:block;width:100vw;height:100vh}
a{position:fixed;left:50%;bottom:1.1rem;transform:translateX(-50%);z-index:2;
color:#e8e6e3;text-decoration:none;border:1px solid #8467A9;border-radius:.4rem;
padding:.7rem 1.9rem;letter-spacing:.22em;font-size:.78rem;background:#10101088;
transition:background .25s,box-shadow .25s}
a:hover{background:#8467A9;color:#fff;box-shadow:0 0 2.5rem -.5rem #8467A9}
</style>
<canvas id="c"></canvas>
<a href="https://jessebrown1980.github.io/asolaria-multiverse/">MULTIVERSE</a>
<script>
// 81 and a nought. Integer arithmetic only: every position, velocity and force below is a
// fixed-point integer, S = 1024. No float touches a body. Float appears once, at the
// boundary, to hand pixels to the canvas.
var S = 1024, N = 81, HALF = 40;
var c = document.getElementById('c'), g = c.getContext('2d'), W = 0, H = 0, DPR = 1;
function fit(){ DPR = Math.min(2, window.devicePixelRatio||1);
W = c.width = (c.clientWidth*DPR)|0; H = c.height = (c.clientHeight*DPR)|0; }
addEventListener('resize', fit); fit();
// 27 red, 27 green, 27 blue -- 3 colour charges x 27 = 81 = 3^4 / 3.
var CH = [[224,64,56],[72,208,104],[80,128,232]];
var bx=[],by=[],bz=[],vx=[],vy=[],ch=[],tr=[];
var seed = 0x8467a937;
function rnd(){ seed ^= seed<<13; seed|=0; seed ^= seed>>>17; seed ^= seed<<5; seed|=0;
return (seed>>>0); }
for (var i=0;i<N;i++){
var k = i % 3, ring = 3 + ((i/3)|0) % 27;
var ang = (rnd() % 6283) - 3141; // milliradians, integer
var rad = (60 + ring*11) * S;
bx[i] = ((rad * Math.cos(ang/1000))|0); // laid out once, then integer forever
by[i] = ((rad * Math.sin(ang/1000))|0);
bz[i] = (rnd() % 27) - 13; // z is the squared wave: 27 bands
var sp = 900 + (rnd()%600);
vx[i] = ((-by[i]/ (rad/S||1) * sp / 900)|0)>>4;
vy[i] = (( bx[i]/ (rad/S||1) * sp / 900)|0)>>4;
ch[i] = k; tr[i] = [];
}
var G = 46, CORE = 1900, SOFT = 9*S, ZEB = 0;
function step(){
for (var i=0;i<N;i++){
var fx=0, fy=0;
// the nought at the centre: the shared centre, one body the other 81 all feel
var dx = -bx[i], dy = -by[i];
var d2 = ((dx>>5)*(dx>>5) + (dy>>5)*(dy>>5)) + 64;
var inv = (CORE*S)/d2|0;
fx += (dx>>5)*inv>>7; fy += (dy>>5)*inv>>7;
for (var j=0;j<N;j++){
if (j===i) continue;
var ex = bx[j]-bx[i], ey = by[j]-by[i];
var e2 = ((ex>>6)*(ex>>6) + (ey>>6)*(ey>>6)) + 36;
var f = (G*S)/e2|0;
// colour charge: like repels, unlike attracts -- the triplet stays mixed
if (ch[i]===ch[j]) f = -f;
fx += (ex>>6)*f>>9; fy += (ey>>6)*f>>9;
}
vx[i] += fx; vy[i] += fy;
if (vx[i]>9000) vx[i]=9000; if (vx[i]<-9000) vx[i]=-9000;
if (vy[i]>9000) vy[i]=9000; if (vy[i]<-9000) vy[i]=-9000;
}
for (var i=0;i<N;i++){
bx[i] += vx[i]>>4; by[i] += vy[i]>>4;
bz[i] = ((bz[i]+1+13)%27)-13; // the squared wave marches
var t = tr[i]; t.push(bx[i], by[i]);
if (t.length > 44) t.splice(0,2);
}
ZEB = (ZEB+1)%54;
}
function draw(){
var cx = W>>1, cy = H>>1, sc = Math.min(W,H)/ (HALF*S*2/1000) / 1000;
g.fillStyle = '#101010'; g.fillRect(0,0,W,H);
// the zebra: z is the squared wave, so depth is 27 hard bands, never a gradient
for (var b=0;b<27;b++){
if (((b + ((ZEB/2)|0)) % 2) === 0) continue;
var yy = (b*H/27)|0;
g.fillStyle = 'rgba(255,255,255,0.014)';
g.fillRect(0, yy, W, (H/27)|0);
}
for (var i=0;i<N;i++){
var col = CH[ch[i]], t = tr[i];
var band = (bz[i]+13); // 0..26, the squared wave
var lit = 0.30 + 0.70*((band%2)===0 ? 1 : 0.34);
g.strokeStyle = 'rgba('+col[0]+','+col[1]+','+col[2]+','+(0.16*lit).toFixed(3)+')';
g.lineWidth = DPR;
g.beginPath();
for (var k=0;k<t.length;k+=2){
var X = cx + t[k]*sc, Y = cy + t[k+1]*sc;
if (k===0) g.moveTo(X,Y); else g.lineTo(X,Y);
}
g.stroke();
var X = cx + bx[i]*sc, Y = cy + by[i]*sc;
var r = (1.6 + band*0.10) * DPR * lit;
g.fillStyle = 'rgba('+col[0]+','+col[1]+','+col[2]+','+lit.toFixed(3)+')';
g.beginPath(); g.arc(X,Y,r,0,6.2832); g.fill();
}
// the omega sphere, and the 1
var R = Math.min(W,H)*0.052;
var gr = g.createRadialGradient(cx-R*0.3, cy-R*0.35, R*0.1, cx, cy, R);
gr.addColorStop(0,'#b9a3d8'); gr.addColorStop(0.55,'#8467A9'); gr.addColorStop(1,'#2b2140');
g.fillStyle = gr; g.beginPath(); g.arc(cx,cy,R,0,6.2832); g.fill();
g.strokeStyle = 'rgba(240,240,16,0.55)'; g.lineWidth = DPR;
g.beginPath(); g.arc(cx,cy,R*1.34,0,6.2832); g.stroke();
g.fillStyle = '#f4f1ea'; g.textAlign='center'; g.textBaseline='middle';
g.font = (R*1.02|0)+'px ui-monospace,Menlo,Consolas,monospace';
g.fillText('Ω', cx, cy - R*0.06);
g.font = (R*0.52|0)+'px ui-monospace,Menlo,Consolas,monospace';
g.fillStyle = '#101010';
g.fillText('1', cx, cy + R*0.62);
}
(function loop(){ step(); draw(); requestAnimationFrame(loop); })();
</script>