Performance Regression Analysis — micr-dev/quarzite
Automated by Nightshift v3 (GLM 5.1).
Analyzed 20 JS files (~2,082 lines), 2 CSS files (~17KB), and 56MB of assets.
P1 — Massive unoptimized image assets (10MB+)
Files: assets/crossover.png (10.2MB), assets/farhan.png (6.2MB), assets/brainrotcat.png (4.4MB), assets/nana.png (3MB)
27 gallery images total, many exceeding 1MB as uncompressed PNG. The largest (crossover.png) is 10MB — loading this single image on a mobile connection takes ~30s.
Impact: 30MB+ of images fetched on a gallery page. Even with loading=lazy, visible images still block initial paint.
Recommendation:
- Convert gallery images to WebP (typically 60-80% smaller) or AVIF
- Use responsive
<picture> / srcset to serve smaller variants for mobile
- Run
sharp or squosh to resize images beyond their display size
- Consider a CDN with automatic image optimization (Netlify supports this natively)
P2 — cache: "no-store" prevents caching of gallery data
File: js/gallery-shared.js (line 158)
response = await fetch(url, { cache: "no-store" });
Forces a full network round-trip for data/gallery.json on every page load, even though this file changes rarely. The browser cache is bypassed entirely.
Impact: Unnecessary network latency on revisits.
Recommendation: Use default caching (remove cache: "no-store"), or use cache: "default" with a cache-busting query param when the data actually changes.
P2 — Background SVG generated at runtime on every page load
File: js/background.js (lines 102-151)
A complex SVG with 4 linear gradients, a drop-shadow filter, and 4 polygons is constructed in JavaScript on every page load. The theme (#8621E7) and tile size (10px) are constants — this SVG never changes.
Impact: Unnecessary JS execution + string concatenation + URI encoding on every load.
Recommendation: Pre-generate the SVG as a static file and reference it via CSS background-image: url(pattern.svg). If runtime theming is needed, cache the data URL after first generation.
P2 — New Audio() object created on every sound play
File: js/sounds.js (lines 12-18)
function play(name) {
const a = new Audio(base + f);
a.play().catch(() => {});
}
Creates a fresh Audio element per play call. This prevents the browser from preloading audio, adds GC pressure, and introduces playback latency (the audio file must be fetched/decoded each time).
Impact: 100-300ms delay before sounds play, audio files re-fetched repeatedly.
Recommendation: Create Audio objects once and reuse them. Preload on first interaction:
const cache = {};
function play(name) {
if (!cache[name]) cache[name] = new Audio(base + files[name]);
cache[name].currentTime = 0;
cache[name].volume = (window.AppVolume ?? 100) / 100;
cache[name].play().catch(() => {});
}
P2 — Layout thrashing in drag handler
File: js/drag.js (line 20)
const style = getComputedStyle(el);
startLeft = px(style.left);
startTop = px(style.top);
getComputedStyle forces the browser to flush the style calculation queue. Called on every mousedown start, it triggers a synchronous style recalculation. During active drags, move() reads el.offsetWidth / container.clientWidth (line 49-50) which also force layout.
Impact: Jank during window drag, especially with many DOM elements.
Recommendation: Cache left/top from inline style or a data attribute instead of reading computed style. Throttle layout reads in move().
P2 — Triple getComputedStyle in image viewer resize
File: js/gallery.js (lines 158-165)
adjustToImageWidth() calls getComputedStyle three times for different elements (window body, figure). Each call may trigger a separate style recalculation.
Recommendation: Batch all style reads before any style writes to avoid forced reflows. Use requestAnimationFrame (already used for the call, but not inside the function).
P3 — Duplicate z-index tracking across modules
Files: js/gallery.js (line 25-32) and js/app.js (line 61)
Both files track z-index independently — gallery.js scans all windows with getComputedStyle on every click, while app.js increments a shared counter. The gallery's bringToFront() is slower (O(n) with n = window count) and doesn't coordinate with app.js's counter.
Recommendation: Use a single shared z-index counter (e.g., window.AppZIndex) and increment it from both modules.
P3 — Date objects created during sort
File: js/gallery-shared.js (lines 147-153)
sortImagesByDate creates a new Date object for every comparison. For 27 items, this means ~130 Date object allocations during sort.
Impact: Minor, but unnecessary allocation.
Recommendation: Parse dates once and sort by pre-computed timestamps, or compare ISO date strings directly if format permits.
Summary
| Severity |
Count |
Key Issues |
| P1 |
1 |
30MB+ of unoptimized PNG images |
| P2 |
5 |
No-store caching, runtime SVG, Audio GC, layout thrashing |
| P3 |
2 |
Duplicate z-index, sort allocations |
| Total |
8 |
|
Highest impact fix: Image optimization (P1) would reduce total asset size by ~70-80% with WebP conversion alone.
Performance Regression Analysis — micr-dev/quarzite
Automated by Nightshift v3 (GLM 5.1).
Analyzed 20 JS files (~2,082 lines), 2 CSS files (~17KB), and 56MB of assets.
P1 — Massive unoptimized image assets (10MB+)
Files:
assets/crossover.png(10.2MB),assets/farhan.png(6.2MB),assets/brainrotcat.png(4.4MB),assets/nana.png(3MB)27 gallery images total, many exceeding 1MB as uncompressed PNG. The largest (
crossover.png) is 10MB — loading this single image on a mobile connection takes ~30s.Impact: 30MB+ of images fetched on a gallery page. Even with
loading=lazy, visible images still block initial paint.Recommendation:
<picture>/srcsetto serve smaller variants for mobilesharporsquoshto resize images beyond their display sizeP2 —
cache: "no-store"prevents caching of gallery dataFile:
js/gallery-shared.js(line 158)Forces a full network round-trip for
data/gallery.jsonon every page load, even though this file changes rarely. The browser cache is bypassed entirely.Impact: Unnecessary network latency on revisits.
Recommendation: Use default caching (remove
cache: "no-store"), or usecache: "default"with a cache-busting query param when the data actually changes.P2 — Background SVG generated at runtime on every page load
File:
js/background.js(lines 102-151)A complex SVG with 4 linear gradients, a drop-shadow filter, and 4 polygons is constructed in JavaScript on every page load. The theme (
#8621E7) and tile size (10px) are constants — this SVG never changes.Impact: Unnecessary JS execution + string concatenation + URI encoding on every load.
Recommendation: Pre-generate the SVG as a static file and reference it via CSS
background-image: url(pattern.svg). If runtime theming is needed, cache the data URL after first generation.P2 — New Audio() object created on every sound play
File:
js/sounds.js(lines 12-18)Creates a fresh
Audioelement per play call. This prevents the browser from preloading audio, adds GC pressure, and introduces playback latency (the audio file must be fetched/decoded each time).Impact: 100-300ms delay before sounds play, audio files re-fetched repeatedly.
Recommendation: Create Audio objects once and reuse them. Preload on first interaction:
P2 — Layout thrashing in drag handler
File:
js/drag.js(line 20)getComputedStyleforces the browser to flush the style calculation queue. Called on everymousedownstart, it triggers a synchronous style recalculation. During active drags,move()readsel.offsetWidth/container.clientWidth(line 49-50) which also force layout.Impact: Jank during window drag, especially with many DOM elements.
Recommendation: Cache
left/topfrom inline style or a data attribute instead of reading computed style. Throttle layout reads inmove().P2 — Triple
getComputedStylein image viewer resizeFile:
js/gallery.js(lines 158-165)adjustToImageWidth()callsgetComputedStylethree times for different elements (window body, figure). Each call may trigger a separate style recalculation.Recommendation: Batch all style reads before any style writes to avoid forced reflows. Use
requestAnimationFrame(already used for the call, but not inside the function).P3 — Duplicate z-index tracking across modules
Files:
js/gallery.js(line 25-32) andjs/app.js(line 61)Both files track z-index independently —
gallery.jsscans all windows withgetComputedStyleon every click, whileapp.jsincrements a shared counter. The gallery'sbringToFront()is slower (O(n) with n = window count) and doesn't coordinate with app.js's counter.Recommendation: Use a single shared z-index counter (e.g.,
window.AppZIndex) and increment it from both modules.P3 — Date objects created during sort
File:
js/gallery-shared.js(lines 147-153)sortImagesByDatecreates a newDateobject for every comparison. For 27 items, this means ~130 Date object allocations during sort.Impact: Minor, but unnecessary allocation.
Recommendation: Parse dates once and sort by pre-computed timestamps, or compare ISO date strings directly if format permits.
Summary
Highest impact fix: Image optimization (P1) would reduce total asset size by ~70-80% with WebP conversion alone.