From f513355cbbffa55496c3a7e689aecefcca89143f Mon Sep 17 00:00:00 2001 From: zzstoatzz Date: Sun, 20 Sep 2026 03:05:06 -0500 Subject: [PATCH] site: form crisp colored splinters within the live glass --- deploy/site/ambient.js | 115 +++++++++++++++-------------------- deploy/site/brand.css | 4 +- deploy/site/index.html | 6 +- deploy/site/stats.html | 6 +- deploy/site/statuspage.html | 6 +- tests/frontend-layout.py | 2 + tests/glass-current.test.cjs | 14 +++++ 7 files changed, 75 insertions(+), 78 deletions(-) diff --git a/deploy/site/ambient.js b/deploy/site/ambient.js index b6ddaea..463c8c8 100644 --- a/deploy/site/ambient.js +++ b/deploy/site/ambient.js @@ -78,12 +78,19 @@ for (let i = 0; i < message.length; i++) seed = Math.imul(seed ^ message.charCodeAt(i), 16777619); return seed >>> 0; } - function grainFrom(seed) { + function grainFrom(seed, points = [[0,0],[384,0],[0,384]], tint = 0) { + const [a,b,c] = points; + const area = Math.abs((b[0]-a[0])*(c[1]-a[1])-(c[0]-a[0])*(b[1]-a[1]))/2; const random = randomFrom(seed); - return Array.from({ length: 4200 }, () => { - const x = random() * 384, y = random() * 384; - const direction = Math.sin(x / 47) * .4 + Math.cos(y / 61) * .3; - return { x, y, length: .6 + random() ** 3 * 2.8, direction, alpha: .12 + random() ** 2 * .25 }; + const angle = Math.atan2(b[1]-a[1],b[0]-a[0])*.12 + (tint-.5)*.8; + return Array.from({length:Math.ceil(area/90)},()=>{ + let u=random(), v=random(); if(u+v>1){u=1-u;v=1-v;} + const x=a[0]+u*(b[0]-a[0])+v*(c[0]-a[0]); + const y=a[1]+u*(b[1]-a[1])+v*(c[1]-a[1]); + const theta=angle+Math.sin(x/190+y/260)*.35+(random()-.5)*.22; + const length=.9+random()**3*6, breadth=.25+random()*.7; + return {x,y,dx:Math.cos(theta)*length,dy:Math.sin(theta)*length, + nx:-Math.sin(theta)*breadth,ny:Math.cos(theta)*breadth}; }); } if (typeof module !== 'undefined') module.exports = { createCurrent, position, receive, absorb, resizeCurrent, grainFrom }; @@ -101,25 +108,21 @@ const reduced = matchMedia('(prefers-reduced-motion: reduce)'); let width = 0, height = 0, frame = 0, last = 0, entropy = 0; let socket, retry; - let grain, grainSeed; - function texture() { - if (grain && grainSeed === current.seed) return grain; - const tile = document.createElement('canvas'); - tile.width = tile.height = 768; - const surface = tile.getContext('2d'); - surface.scale(2, 2); - for (const fleck of grainFrom(current.seed)) { - surface.strokeStyle = `rgba(145,168,161,${fleck.alpha})`; - surface.lineWidth = .65; - surface.beginPath(); - surface.moveTo(fleck.x, fleck.y); - surface.lineTo(fleck.x + fleck.length, fleck.y + fleck.direction * fleck.length); - surface.stroke(); + const material = new WeakMap(); + const pigments = ['65,163,133','74,163,192','131,113,185','184,158,101']; + function texture(shard) { + if (material.has(shard)) return material.get(shard); + const points = shard.points.map(([x,y]) => [x*width,y*height]); + const [a,b,c] = points; + const faces = new Path2D(), edges = new Path2D(), shadows = new Path2D(); + const flecks=grainFrom(current.seed+BigInt(Math.round(shard.tint*1e9)),points,shard.tint); + for (const {x,y,dx,dy,nx,ny} of flecks) { + faces.moveTo(x-dx*.5,y-dy*.5);faces.lineTo(x+nx,y+ny);faces.lineTo(x+dx*.5,y+dy*.5);faces.closePath(); + shadows.moveTo(x-dx*.5,y-dy*.5);shadows.lineTo(x-nx,y-ny);shadows.lineTo(x+dx*.5,y+dy*.5);shadows.closePath(); + edges.moveTo(x-dx*.5,y-dy*.5);edges.lineTo(x+dx*.5,y+dy*.5); } - grain = context.createPattern(tile, 'repeat'); - grain.setTransform(new DOMMatrix().scale(.5)); - grainSeed = current.seed; - return grain; + const result={faces,edges,shadows,cx:(a[0]+b[0]+c[0])/3,cy:(a[1]+b[1]+c[1])/3}; + material.set(shard,result);return result; } function connect() { clearTimeout(retry); @@ -134,53 +137,30 @@ function paint(now) { const phase = reduced.matches ? current.to : position(current, now); const t = reduced.matches ? 1 : progress(current, now); - const surface = texture(); - context.clearRect(0, 0, width, height); - context.fillStyle = '#111f2a'; - context.fillRect(0, 0, width, height); - const water = context.createLinearGradient(0, height, width, 0); - water.addColorStop(0, 'rgba(30,65,64,.32)'); - water.addColorStop(.48, 'rgba(44,56,80,.28)'); - water.addColorStop(1, 'rgba(62,72,86,.32)'); - context.fillStyle = water; - context.fillRect(0, 0, width, height); + context.fillStyle = '#040708'; + context.fillRect(0,0,width,height); for (const shard of current.shards) { - const points = shard.points.map(([x, y]) => [x * width, y * height]); - const light = lightAt(shard, t); - const pigment = shard.tint < .18 ? '127,121,91' : shard.tint < .5 ? '71,108,99' : '79,107,128'; - const [a, b, c] = points; - context.beginPath(); - context.moveTo(...a); context.lineTo(...b); context.lineTo(...c); context.closePath(); - context.fillStyle = `rgba(${pigment},${.012 + light * .025})`; - context.fill(); - context.fillStyle = surface; - context.globalAlpha = .65 + light * .35; - context.fill(); - context.globalAlpha = 1; - context.save(); context.clip(); - const offset = (shard.tint - .5) * 14; - for (let channel = 0; channel < 4; channel++) { - context.beginPath(); - for (let step = 0; step <= 24; step++) { - const x = step / 24 * width; - const y = height * (.15 + channel * .23) + - Math.sin(x / 430 - phase * 5 + channel * 1.7) * 35 + - Math.sin(x / 190 + channel - phase * 2) * 8 + offset; - if (step === 0) context.moveTo(x, y); else context.lineTo(x, y); - } - context.strokeStyle = `rgba(${pigment},${.018 + light * .08})`; - context.lineWidth = 10 + shard.tint * 14; - context.stroke(); - context.strokeStyle = `rgba(147,179,171,${.015 + light * .06})`; - context.lineWidth = 1.1; - context.stroke(); - } + const surface=texture(shard); + const light=lightAt(shard,t); + const bend=Math.sin(surface.cx/340-phase*5)*65+Math.sin(surface.cx/150-phase*2)*18; + const stream=.5+.5*Math.sin((surface.cy-bend)/130+phase*3); + const illumination=.10+stream**5*(.12+light*.55); + const pigment= pigments[shard.tint<.38?0:shard.tint<.72?1:shard.tint<.93?2:3]; + const [a,b,c]=shard.points.map(([x,y])=>[x*width,y*height]); + context.beginPath();context.moveTo(...a);context.lineTo(...b);context.lineTo(...c);context.closePath(); + context.fillStyle=`rgba(${pigment},${illumination*.022})`;context.fill(); + context.save();context.clip(); + context.fillStyle=`rgba(${pigment},${illumination*.75})`;context.fill(surface.faces); + context.fillStyle=`rgba(${pigment},${illumination*.2})`;context.fill(surface.shadows); + context.strokeStyle=`rgba(${pigment},${illumination})`; + context.lineWidth=.32;context.stroke(surface.edges); context.restore(); - context.beginPath(); context.moveTo(...a); context.lineTo(...b); - context.strokeStyle = `rgba(155,183,175,${.012 + light * .075})`; - context.lineWidth = .45; context.stroke(); + context.beginPath();context.moveTo(...a);context.lineTo(...b); + context.strokeStyle=`rgba(${pigment},${illumination*.09})`; + context.lineWidth=.35;context.stroke(); } } + function animate(now) { frame = 0; if (document.hidden) return; @@ -192,7 +172,7 @@ if (!frame && !document.hidden) frame = requestAnimationFrame(animate); } function resize() { - const ratio = Math.min(devicePixelRatio || 1, 2); + const ratio = Math.min(devicePixelRatio || 1, 3); width = canvas.clientWidth; height = canvas.clientHeight; resizeCurrent(current, width, height); canvas.width = Math.round(width * ratio); canvas.height = Math.round(height * ratio); @@ -203,6 +183,7 @@ if (receive(current, BigInt(event.detail), performance.now(), BigInt(entropy))) wake(); }); addEventListener('resize', resize); + new ResizeObserver(resize).observe(canvas); document.addEventListener('visibilitychange', () => { if (document.hidden) { clearTimeout(retry); if (socket) socket.close(); } else { connect(); wake(); } diff --git a/deploy/site/brand.css b/deploy/site/brand.css index d198d7b..2e8cf0a 100644 --- a/deploy/site/brand.css +++ b/deploy/site/brand.css @@ -1,4 +1,4 @@ -:root{color-scheme:dark;--bg:#1b2b38;--ink:#c1ccd2;--dim:#98aab5;--faint:#465a68;--line:#465a68;--hot:#e2ebf0;--water:#9bc4cf;-webkit-text-size-adjust:100%;text-size-adjust:100%} +:root{color-scheme:dark;--bg:#040708;--ink:#c1ccd2;--dim:#98aab5;--faint:#465a68;--line:#465a68;--hot:#e2ebf0;--water:#9bc4cf;-webkit-text-size-adjust:100%;text-size-adjust:100%} *{box-sizing:border-box} html{background:var(--bg)} body{margin:0;isolation:isolate;min-height:100svh;background:transparent;color:var(--ink);font:14px/1.65 ui-monospace,"SF Mono",Menlo,Consolas,monospace} @@ -33,7 +33,7 @@ body[data-page=stats] .grid{display:grid;grid-template-columns:repeat(3,minmax(0 body[data-page=strata]>header{padding-inline:18px} body[data-page=strata]>main{padding-top:0} body[data-page=strata] #map-dialog{background:var(--bg)} -body[data-page=strata] :is(button,select,input,#map-panel,.camera-controls,#hover,pre){background-color:#192a36} +body[data-page=strata] :is(button,select,input,#map-panel,.camera-controls,#hover,pre){background-color:#080e11} @media(max-width:640px){body[data-page=stats] .grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:24px 14px}.report dl{font-size:12px;gap:10px 12px}} body[data-page=strata] canvas#map{touch-action:pan-y pinch-zoom} diff --git a/deploy/site/index.html b/deploy/site/index.html index 3fba0dd..537a52f 100644 --- a/deploy/site/index.html +++ b/deploy/site/index.html @@ -5,7 +5,7 @@ stream - + {{$s := httpInclude "/status"}}{{$seq := regexFind "[0-9,]+" (regexFind "upstream seq[^\n]+" $s)}}{{$segs := regexFind "[0-9,]+" (regexFind "sealed segs[^\n]+" $s)}}{{$desc := "jetstream v2 firehose + full-network atproto archive"}}{{if $seq}}{{$desc = printf "%s · network seq %s · %s sealed segments" $desc $seq $segs}}{{end}} @@ -37,7 +37,7 @@ footer .icon{width:11px;height:11px;vertical-align:-1px;margin-right:.6ch} .wrap{position:relative} - + @@ -66,7 +66,7 @@ footer .icon{width:11px;height:11px;vertical-align:-1px;margin-right:.6ch} source - + + +