diff --git a/deploy/site/ambient.js b/deploy/site/ambient.js index 790f879..eb49625 100644 --- a/deploy/site/ambient.js +++ b/deploy/site/ambient.js @@ -9,7 +9,7 @@ }; } function createCurrent() { - return { sequence: 0n, shards: [], from: 0, to: 0, at: 0 }; + return { sequence: 0n, seed: 0n, entropy: 0n, width: 390, height: 900, shards: [], from: 0, to: 0, at: 0 }; } function progress(current, now) { return Math.max(0, Math.min(1, (now - current.at) / 5000)); @@ -20,15 +20,16 @@ function lightAt(shard, t) { return shard.light + (shard.target - shard.light) * t; } - function makeShards(sequence) { + function makeShards(sequence, width, height) { const random = randomFrom(sequence); const points = []; - const columns = 5, rows = 9; + const columns = Math.max(3, Math.min(24, Math.ceil(width / 120))); + const rows = Math.max(4, Math.min(16, Math.ceil(height / 115))); for (let row = 0; row <= rows; row++) { for (let col = 0; col <= columns; col++) { points.push([ - (col + (col && col < columns ? (random() - .5) * .85 : 0)) / columns, - (row + (row && row < rows ? (random() - .5) * .85 : 0)) / rows, + (col + (col && col < columns ? (random() - .5) * .45 : 0)) / columns, + (row + (row && row < rows ? (random() - .5) * .45 : 0)) / rows, ]); } } @@ -45,12 +46,23 @@ } return shards; } + function resizeCurrent(current, width, height) { + if (current.width === width && current.height === height) return; + current.width = width; current.height = height; + if (current.sequence === 0n) return; + current.shards = makeShards(current.seed, width, height); + const random = randomFrom(current.entropy); + for (const shard of current.shards) { + shard.light = shard.target = current.entropy === 0n ? 0 : random() ** 4; + } + } function receive(current, sequence, now, entropy = 0n) { if (sequence <= 0n || sequence === current.sequence) return false; const fresh = current.sequence === 0n || sequence < current.sequence; const t = progress(current, now); const delta = fresh ? 0 : Number(sequence - current.sequence > 5000n ? 5000n : sequence - current.sequence); - if (fresh) current.shards = makeShards(sequence); + if (fresh) { current.seed = sequence; current.shards = makeShards(sequence, current.width, current.height); } + current.entropy = entropy; const random = randomFrom(entropy); for (const shard of current.shards) { shard.light = lightAt(shard, t); @@ -66,7 +78,7 @@ 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 }; + if (typeof module !== 'undefined') module.exports = { createCurrent, position, receive, absorb, resizeCurrent }; if (typeof document === 'undefined') return; const canvas = document.querySelector('canvas[data-current]'); if (!canvas) return; @@ -97,20 +109,30 @@ const [a, b, c] = points; context.beginPath(); context.moveTo(...a); context.lineTo(...b); context.lineTo(...c); context.closePath(); - const centerX = (a[0] + b[0] + c[0]) / 3, centerY = (a[1] + b[1] + c[1]) / 3; - const angle = shard.tint * Math.PI + phase * 2; - const reach = Math.min(width, height) * .22; - const dx = Math.cos(angle) * reach, dy = Math.sin(angle) * reach; - const glass = context.createLinearGradient(centerX - dx, centerY - dy, centerX + dx, centerY + dy); - glass.addColorStop(0, `rgba(${pigment},.015)`); - glass.addColorStop(.48, `rgba(${pigment},${.035 + light * .23})`); - glass.addColorStop(.52, `rgba(${pigment},${.018 + light * .06})`); - glass.addColorStop(1, `rgba(${pigment},.008)`); - context.fillStyle = glass; context.fill(); - context.strokeStyle = 'rgba(74,105,112,.09)'; context.lineWidth = .5; context.stroke(); + context.fillStyle = `rgba(${pigment},${.012 + light * .025})`; + context.fill(); + 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(); + } + context.restore(); context.beginPath(); context.moveTo(...a); context.lineTo(...b); - context.strokeStyle = `rgba(155,183,175,${.025 + light * .2})`; - context.lineWidth = .65; context.stroke(); + context.strokeStyle = `rgba(155,183,175,${.012 + light * .075})`; + context.lineWidth = .45; context.stroke(); } } function animate(now) { @@ -126,6 +148,7 @@ function resize() { const ratio = Math.min(devicePixelRatio || 1, 2); width = canvas.clientWidth; height = canvas.clientHeight; + resizeCurrent(current, width, height); canvas.width = Math.round(width * ratio); canvas.height = Math.round(height * ratio); context.setTransform(ratio, 0, 0, ratio, 0, 0); paint(performance.now()); diff --git a/deploy/site/index.html b/deploy/site/index.html index 08309b2..6fbb558 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 - +