diff --git a/deploy/site/ambient.js b/deploy/site/ambient.js index eb49625..e4f6613 100644 --- a/deploy/site/ambient.js +++ b/deploy/site/ambient.js @@ -78,7 +78,15 @@ for (let i = 0; i < message.length; i++) seed = Math.imul(seed ^ message.charCodeAt(i), 16777619); return seed >>> 0; } - if (typeof module !== 'undefined') module.exports = { createCurrent, position, receive, absorb, resizeCurrent }; + function grainFrom(seed) { + 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 }; + }); + } + if (typeof module !== 'undefined') module.exports = { createCurrent, position, receive, absorb, resizeCurrent, grainFrom }; if (typeof document === 'undefined') return; const canvas = document.querySelector('canvas[data-current]'); if (!canvas) return; @@ -88,6 +96,26 @@ 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(); + } + grain = context.createPattern(tile, 'repeat'); + grain.setTransform(new DOMMatrix().scale(.5)); + grainSeed = current.seed; + return grain; + } function connect() { clearTimeout(retry); if (document.hidden || socket) return; @@ -101,6 +129,7 @@ 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); for (const shard of current.shards) { const points = shard.points.map(([x, y]) => [x * width, y * height]); @@ -111,6 +140,10 @@ 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++) { diff --git a/deploy/site/index.html b/deploy/site/index.html index 6fbb558..6671193 100644 --- a/deploy/site/index.html +++ b/deploy/site/index.html @@ -81,7 +81,7 @@ footer .icon{width:11px;height:11px;vertical-align:-1px;margin-right:.6ch} source - +