From 870afc8b88e0862bdfe4e3621176b04ce12d5ca3 Mon Sep 17 00:00:00 2001 From: Jose Henrique Date: Mon, 12 Oct 2026 15:38:17 -0300 Subject: [PATCH] improving performance --- 404.html | 6 +- assets/lain.jpg | Bin 0 -> 63121 bytes css/main.css | 1 - css/matrix.css | 12 ++-- index.html | 8 +-- js/flowfield.js | 173 ++++++++++++++++++++++++++++++++++++++++-------- 6 files changed, 159 insertions(+), 41 deletions(-) create mode 100644 assets/lain.jpg diff --git a/404.html b/404.html index 47fc01b..dfb3188 100644 --- a/404.html +++ b/404.html @@ -15,8 +15,8 @@ 404 | ivanch - - + + @@ -45,7 +45,7 @@ - + diff --git a/assets/lain.jpg b/assets/lain.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e347a849654286face364d0358846a7b732a30a1 GIT binary patch literal 63121 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 literal 0 HcmV?d00001 diff --git a/css/main.css b/css/main.css index 50e8979..c61b0e5 100644 --- a/css/main.css +++ b/css/main.css @@ -59,7 +59,6 @@ img { border-top: 1px solid var(--glass-border); border-bottom: 1px solid var(--glass-border); box-shadow: var(--shadow-lg), var(--inset-shadow); - will-change: box-shadow; animation: glass-shadow 10s ease-in-out infinite; } diff --git a/css/matrix.css b/css/matrix.css index bff6f27..737f03f 100644 --- a/css/matrix.css +++ b/css/matrix.css @@ -8,10 +8,12 @@ pointer-events: none; overflow: hidden; background: rgba(0, 0, 0, 0.88); + /* Colors are pre-dimmed (x0.7) rather than using `filter: brightness()`, + which would re-filter the whole animated canvas every frame. The + particle colors get the same treatment in js/flowfield.js. */ background-image: - radial-gradient(120% 100% at 50% 0%, rgba(20, 24, 48, 0.55) 0%, rgba(0, 0, 0, 0) 60%), - linear-gradient(135deg, rgba(0, 0, 0, 0.9) 0%, rgba(18, 18, 38, 0.72) 100%); - filter: brightness(0.7) saturate(1.05); + radial-gradient(120% 100% at 50% 0%, rgba(14, 17, 34, 0.55) 0%, rgba(0, 0, 0, 0) 60%), + linear-gradient(135deg, rgba(0, 0, 0, 0.9) 0%, rgba(13, 13, 27, 0.72) 100%); } /* CSS only on phones/tablets and for reduced motion: no canvas work. */ @@ -37,10 +39,6 @@ animation: none; } - .main-container .glass { - will-change: auto; - } - /* Avoid blur compositing work while scrolling. */ body .main-container :is(.glass, .content, .projects-section, .homelab-section, .btn) { backdrop-filter: none; diff --git a/index.html b/index.html index 320a66b..0196082 100644 --- a/index.html +++ b/index.html @@ -14,14 +14,14 @@ ivanch - + - + @@ -34,7 +34,7 @@
- Avatar + Avatar
@@ -149,7 +149,7 @@
- + diff --git a/js/flowfield.js b/js/flowfield.js index 44f4f3d..555ae9c 100644 --- a/js/flowfield.js +++ b/js/flowfield.js @@ -10,6 +10,12 @@ * Trails are drawn by erasing the previous frame with a low-alpha * `destination-out` pass, so the CSS gradient painted behind the canvas * element keeps showing through instead of being flooded by an opaque fill. + * + * Performance notes: particles are bucketed by (tone, width, alpha) and each + * bucket is stroked as a single path, so a frame issues roughly a third of + * the draw calls of one-per-particle, with no per-frame string allocation. + * The loop is capped at ~60 fps so high-refresh displays don't do 2x the + * work (the simulation is frame-based, so this also keeps speed consistent). */ /* Deterministic PRNG so the noise permutation is stable within a page load. */ @@ -117,6 +123,8 @@ class FlowFieldBackground { this.mouse = { x: 0, y: 0, active: false }; this.rafId = null; this.resizeRaf = null; + this.lastFrame = 0; + this.frameInterval = 1000 / 60; this.events = new AbortController(); this.width = 0; @@ -151,9 +159,42 @@ class FlowFieldBackground { { weight: 0.13, rgb: '146, 103, 197', alpha: 0.16 } // Soft violet ]; + this.buildStyleCache(); this.init(); } + buildStyleCache() { + // Colors are dimmed here instead of with a CSS `filter: brightness()` + // on the canvas, which would cost a full-screen filter pass per frame. + // Additive blending is linear, so the result is nearly identical. + const dim = 0.7; + const dimRgb = (rgb) => rgb.split(',').map((c) => Math.round(c * dim)).join(', '); + for (const tone of this.palette) tone.drawRgb = dimRgb(tone.rgb); + const dotRgb = dimRgb('240, 248, 255'); + + // Alpha and line width are quantized so particles can share a path. + // Steps are small enough that the difference is not visible. + this.alphaStep = 0.02; + this.alphaLevels = Math.round(1 / this.alphaStep) + 1; + this.widthStep = 0.2; + this.widthLevels = 10; // covers the 0.4px - 1.8px particle range + + this.strokeStyles = []; + for (const tone of this.palette) { + for (let a = 0; a < this.alphaLevels; a++) { + this.strokeStyles.push(`rgba(${tone.drawRgb}, ${(a * this.alphaStep).toFixed(3)})`); + } + } + this.dotStyles = []; + for (let a = 0; a < this.alphaLevels; a++) { + this.dotStyles.push(`rgba(${dotRgb}, ${(a * this.alphaStep).toFixed(3)})`); + } + + const lineBuckets = this.palette.length * this.widthLevels * this.alphaLevels; + this.lineCounts = new Uint16Array(lineBuckets + 1); + this.dotCounts = new Uint16Array(this.alphaLevels + 1); + } + init() { this.bindEvents(); this.handleResize(); @@ -231,7 +272,9 @@ class FlowFieldBackground { this.width = w; this.height = h; - this.dpr = Math.min(window.devicePixelRatio || 1, 2); + // The strokes are faint and soft, so a 1x buffer looks nearly the same + // on HiDPI screens while using a quarter of the memory and fill cost. + this.dpr = 1; this.canvas.width = Math.round(w * this.dpr); this.canvas.height = Math.round(h * this.dpr); this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0); @@ -243,21 +286,28 @@ class FlowFieldBackground { this.particles.push(this.createParticle()); } + // Scratch buffers for bucketing particles at draw time. + this.lineKeys = new Int16Array(count); + this.lineOrder = new Uint16Array(count); + this.dotKeys = new Int16Array(count); + this.dotOrder = new Uint16Array(count); + // Repaint a short exposure so a resize never flashes an empty canvas. this.warmup(30); } pickTone() { let roll = Math.random(); - for (const tone of this.palette) { - roll -= tone.weight; - if (roll <= 0) return tone; + for (let i = 0; i < this.palette.length; i++) { + roll -= this.palette[i].weight; + if (roll <= 0) return i; } - return this.palette[0]; + return 0; } createParticle() { - const tone = this.pickTone(); + const toneIndex = this.pickTone(); + const tone = this.palette[toneIndex]; const { min, max } = this.config.life; // 3 Depth layers: 0 (background ambient), 1 (main stream), 2 (foreground highlight) @@ -289,7 +339,7 @@ class FlowFieldBackground { vy: 0, speed: this.config.baseSpeed * speedMult * (0.75 + Math.random() * 0.5), width: (0.7 + Math.random() * 0.5) * widthMult, - rgb: tone.rgb, + tone: toneIndex, baseAlpha: tone.alpha * (0.75 + Math.random() * 0.5) * alphaMult, alpha: 0, glow: 0, @@ -314,7 +364,7 @@ class FlowFieldBackground { for (let i = 0; i < count; i++) { const angle = Math.random() * Math.PI * 2; const speed = 1.0 + Math.random() * 2.2; - const tone = this.pickTone(); + const tone = this.palette[this.pickTone()]; this.sparks.push({ x, y, @@ -322,7 +372,7 @@ class FlowFieldBackground { py: y, vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed, - rgb: tone.rgb, + rgb: tone.drawRgb, size: 0.6 + Math.random() * 0.8, alpha: 0.6 + Math.random() * 0.25, life: 0, @@ -441,34 +491,99 @@ class FlowFieldBackground { this.ctx.fillRect(0, 0, this.width, this.height); } + /* Counting sort: groups particle indices in `order` by their bucket key + (keys < 0 are skipped). Afterwards counts[k] is the end offset of + bucket k, and bucket k starts where bucket k - 1 ends. */ + sortByKey(keys, n, counts, order) { + counts.fill(0); + for (let i = 0; i < n; i++) { + if (keys[i] >= 0) counts[keys[i] + 1]++; + } + for (let k = 1; k < counts.length; k++) counts[k] += counts[k - 1]; + for (let i = 0; i < n; i++) { + const k = keys[i]; + if (k >= 0) order[counts[k]++] = i; + } + } + drawParticles() { const ctx = this.ctx; - // Additive blending gives a soft glow where currents overlap. - ctx.globalCompositeOperation = 'lighter'; - ctx.lineCap = 'round'; + const particles = this.particles; + const n = particles.length; + const { alphaStep, alphaLevels, widthStep, widthLevels, lineKeys, dotKeys } = this; + const glowBoost = this.config.glowBoost; - for (const p of this.particles) { + for (let i = 0; i < n; i++) { + const p = particles[i]; + lineKeys[i] = -1; + dotKeys[i] = -1; if (p.fresh) { p.fresh = false; continue; } - const alpha = Math.min(1, p.alpha + p.glow * this.config.glowBoost); - if (alpha <= 0.005) continue; + const alpha = Math.min(1, p.alpha + p.glow * glowBoost); + const a = Math.round(alpha / alphaStep); + if (a === 0) continue; - ctx.strokeStyle = `rgba(${p.rgb}, ${alpha.toFixed(3)})`; - ctx.lineWidth = p.width; - ctx.beginPath(); - ctx.moveTo(p.px, p.py); - ctx.lineTo(p.x, p.y); - ctx.stroke(); + const w = Math.min(widthLevels - 1, Math.round(p.width / widthStep)); + lineKeys[i] = (p.tone * widthLevels + w) * alphaLevels + a; - // Draw a subtle bright dot at the head of foreground particles + // A subtle bright dot at the head of foreground particles if (p.depth === 2 && (p.glow > 0.1 || Math.random() < 0.12)) { - ctx.fillStyle = `rgba(240, 248, 255, ${(alpha * 0.75).toFixed(3)})`; + dotKeys[i] = Math.round((alpha * 0.75) / alphaStep); + } + } + + // Additive blending gives a soft glow where currents overlap. It is + // also order-independent, so batching does not change the result. + ctx.globalCompositeOperation = 'lighter'; + ctx.lineCap = 'round'; + + const lineCounts = this.lineCounts; + const lineOrder = this.lineOrder; + this.sortByKey(lineKeys, n, lineCounts, lineOrder); + let start = 0; + for (let k = 0; k < lineCounts.length - 1; k++) { + const end = lineCounts[k]; + if (end === start) continue; + + const a = k % alphaLevels; + const toneWidth = (k - a) / alphaLevels; + const w = toneWidth % widthLevels; + const tone = (toneWidth - w) / widthLevels; + + ctx.strokeStyle = this.strokeStyles[tone * alphaLevels + a]; + ctx.lineWidth = w * widthStep; + ctx.beginPath(); + for (let j = start; j < end; j++) { + const p = particles[lineOrder[j]]; + ctx.moveTo(p.px, p.py); + ctx.lineTo(p.x, p.y); + } + ctx.stroke(); + start = end; + } + + const dotCounts = this.dotCounts; + const dotOrder = this.dotOrder; + this.sortByKey(dotKeys, n, dotCounts, dotOrder); + start = 0; + for (let a = 0; a < dotCounts.length - 1; a++) { + const end = dotCounts[a]; + if (end === start) continue; + + if (a > 0) { + ctx.fillStyle = this.dotStyles[a]; ctx.beginPath(); - ctx.arc(p.x, p.y, p.width * 0.75, 0, Math.PI * 2); + for (let j = start; j < end; j++) { + const p = particles[dotOrder[j]]; + const r = p.width * 0.75; + ctx.moveTo(p.x + r, p.y); + ctx.arc(p.x, p.y, r, 0, Math.PI * 2); + } ctx.fill(); } + start = end; } } @@ -506,9 +621,15 @@ class FlowFieldBackground { } } - animate() { + animate(now = performance.now()) { + this.rafId = requestAnimationFrame((t) => this.animate(t)); + + // Cap at ~60 fps; keep the remainder so 120/144 Hz average out to 60. + const elapsed = now - this.lastFrame; + if (elapsed < this.frameInterval * 0.9) return; + this.lastFrame = now - (elapsed % this.frameInterval); + this.step(true); - this.rafId = requestAnimationFrame(() => this.animate()); } }