From 39c0e3de2fd7ec2bdc9894fadd4b241901d2d0b3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 15 Aug 2026 17:36:41 +0000 Subject: [PATCH 1/2] Replace page-field CSS gradient with animated canvas spotlights Co-authored-by: dauble <4305144+dauble@users.noreply.github.com> --- _layouts/default.html | 2 +- assets/css/styles.scss | 10 ++-- assets/js/site.js | 110 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 7 deletions(-) diff --git a/_layouts/default.html b/_layouts/default.html index 15ddaaa..b5808c4 100755 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -58,7 +58,7 @@
{% else %} - + {% endif %} {% if page.layout == 'post' %} diff --git a/assets/css/styles.scss b/assets/css/styles.scss index 2800b9d..f23948b 100644 --- a/assets/css/styles.scss +++ b/assets/css/styles.scss @@ -69,15 +69,13 @@ body { } /* Ambient page background */ -.page-field { +#page-canvas { position: fixed; inset: 0; z-index: -2; - background: - radial-gradient(60% 55% at 12% 8%, rgba(29, 107, 176, 0.22), transparent 60%), - radial-gradient(50% 50% at 92% 0%, rgba(153, 204, 51, 0.10), transparent 55%), - radial-gradient(70% 60% at 80% 100%, rgba(0, 51, 102, 0.55), transparent 62%), - linear-gradient(180deg, var(--navy-950), var(--navy-900) 40%, var(--navy-950)); + width: 100%; + height: 100%; + display: block; } .page-grain { position: fixed; diff --git a/assets/js/site.js b/assets/js/site.js index fbc342a..bdff32d 100644 --- a/assets/js/site.js +++ b/assets/js/site.js @@ -6,6 +6,116 @@ "use strict"; var reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches; + /* ---- Animated gradient background canvas ---- */ + var canvas = document.getElementById("page-canvas"); + if (canvas) { + var ctx = canvas.getContext("2d"); + + var spots = [ + { + // blue — upper-left + cx: 0.12, cy: 0.08, + rx: 0.60, ry: 0.55, + color: "29,107,176", alpha: 0.22, + dx: 0.00007, dy: 0.00005, + drx: 0.00004, dry: 0.00003, + phase: 0 + }, + { + // lime — upper-right + cx: 0.92, cy: 0.00, + rx: 0.50, ry: 0.50, + color: "153,204,51", alpha: 0.10, + dx: -0.00006, dy: 0.00008, + drx: 0.00003, dry: 0.00004, + phase: 1.5 + }, + { + // deep-navy — lower-right + cx: 0.80, cy: 1.00, + rx: 0.70, ry: 0.60, + color: "0,51,102", alpha: 0.55, + dx: -0.00005, dy: -0.00006, + drx: 0.00005, dry: 0.00003, + phase: 3.0 + } + ]; + + // Travel bounds for center position (fraction of viewport) + var DRIFT = 0.12; + // Pulse amplitude (fraction of base radius) + var PULSE = 0.18; + + function resize() { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + } + resize(); + window.addEventListener("resize", resize, { passive: true }); + + var then = 0; + function draw(now) { + var dt = now - then; + then = now; + + var W = canvas.width; + var H = canvas.height; + + // Navy base fill + ctx.fillStyle = "#000d18"; + ctx.fillRect(0, 0, W, H); + + for (var i = 0; i < spots.length; i++) { + var s = spots[i]; + + if (!reduce) { + s.phase += dt * 0.0004; + // Drift center — oscillate around original cx/cy + s.cx += s.dx; + s.cy += s.dy; + // Bounce drift within range (keep roughly in quadrant) + var ox = [0.12, 0.92, 0.80][i]; + var oy = [0.08, 0.00, 1.00][i]; + if (Math.abs(s.cx - ox) > DRIFT) s.dx *= -1; + if (Math.abs(s.cy - oy) > DRIFT) s.dy *= -1; + // Pulse radius + s.rx += s.drx; + s.ry += s.dry; + var brx = [0.60, 0.50, 0.70][i]; + var bry = [0.55, 0.50, 0.60][i]; + if (Math.abs(s.rx - brx) > PULSE * brx) s.drx *= -1; + if (Math.abs(s.ry - bry) > PULSE * bry) s.dry *= -1; + } + + var px = s.cx * W; + var py = s.cy * H; + var rw = s.rx * W; + var rh = s.ry * H; + var r = Math.max(rw, rh); + + // Draw as ellipse via scale transform + ctx.save(); + ctx.translate(px, py); + ctx.scale(rw / r, rh / r); + var grad = ctx.createRadialGradient(0, 0, 0, 0, 0, r); + grad.addColorStop(0, "rgba(" + s.color + "," + s.alpha + ")"); + grad.addColorStop(1, "rgba(" + s.color + ",0)"); + ctx.fillStyle = grad; + ctx.beginPath(); + ctx.arc(0, 0, r, 0, Math.PI * 2); + ctx.fill(); + ctx.restore(); + } + + requestAnimationFrame(draw); + } + + requestAnimationFrame(function (now) { + then = now; + requestAnimationFrame(draw); + }); + } + /* ---- Nav: frost on scroll ---- */ var nav = document.querySelector("[data-nav]"); if (nav) { From 82abe080b2fe4c28714581bb218aacd59f245af7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 15 Aug 2026 17:38:03 +0000 Subject: [PATCH 2/2] Address code review: store origin values on spots, guard r<=0, pause on tab hide Co-authored-by: dauble <4305144+dauble@users.noreply.github.com> --- assets/js/site.js | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/assets/js/site.js b/assets/js/site.js index bdff32d..d107533 100644 --- a/assets/js/site.js +++ b/assets/js/site.js @@ -41,6 +41,14 @@ } ]; + // Store origin values on each spot for bounce reference + for (var si = 0; si < spots.length; si++) { + spots[si].ocx = spots[si].cx; + spots[si].ocy = spots[si].cy; + spots[si].orx = spots[si].rx; + spots[si].ory = spots[si].ry; + } + // Travel bounds for center position (fraction of viewport) var DRIFT = 0.12; // Pulse amplitude (fraction of base radius) @@ -54,6 +62,7 @@ window.addEventListener("resize", resize, { passive: true }); var then = 0; + var raf = null; function draw(now) { var dt = now - then; then = now; @@ -73,18 +82,14 @@ // Drift center — oscillate around original cx/cy s.cx += s.dx; s.cy += s.dy; - // Bounce drift within range (keep roughly in quadrant) - var ox = [0.12, 0.92, 0.80][i]; - var oy = [0.08, 0.00, 1.00][i]; - if (Math.abs(s.cx - ox) > DRIFT) s.dx *= -1; - if (Math.abs(s.cy - oy) > DRIFT) s.dy *= -1; + // Bounce drift within range + if (Math.abs(s.cx - s.ocx) > DRIFT) s.dx *= -1; + if (Math.abs(s.cy - s.ocy) > DRIFT) s.dy *= -1; // Pulse radius s.rx += s.drx; s.ry += s.dry; - var brx = [0.60, 0.50, 0.70][i]; - var bry = [0.55, 0.50, 0.60][i]; - if (Math.abs(s.rx - brx) > PULSE * brx) s.drx *= -1; - if (Math.abs(s.ry - bry) > PULSE * bry) s.dry *= -1; + if (Math.abs(s.rx - s.orx) > PULSE * s.orx) s.drx *= -1; + if (Math.abs(s.ry - s.ory) > PULSE * s.ory) s.dry *= -1; } var px = s.cx * W; @@ -93,6 +98,8 @@ var rh = s.ry * H; var r = Math.max(rw, rh); + if (r <= 0) continue; + // Draw as ellipse via scale transform ctx.save(); ctx.translate(px, py); @@ -107,13 +114,21 @@ ctx.restore(); } - requestAnimationFrame(draw); + raf = requestAnimationFrame(draw); } - requestAnimationFrame(function (now) { - then = now; - requestAnimationFrame(draw); + document.addEventListener("visibilitychange", function () { + if (document.hidden) { + cancelAnimationFrame(raf); + raf = null; + } else if (!raf) { + then = performance.now(); + raf = requestAnimationFrame(draw); + } }); + + then = performance.now(); + raf = requestAnimationFrame(draw); } /* ---- Nav: frost on scroll ---- */