Performance Analysis Report - forscher.com

Date: November 21, 2025 Analysis Type: Comprehensive Performance Audit

Executive Summary

The forscher.com Jekyll site has undergone significant performance optimizations including jQuery removal, WebP image conversion, and vanilla JavaScript migration. While these improvements are commendable, several critical performance bottlenecks remain that impact Core Web Vitals and user experience.

Key Findings

  • Strengths: Successful jQuery removal, partial WebP adoption, efficient CSS bundle (12KB)
  • Critical Issues: Blocking preloader, inefficient animation loops, missing image dimensions, suboptimal font loading
  • Performance Impact: Estimated 40-60% improvement potential in Core Web Vitals with recommended optimizations

1. Recent Performance Changes Assessment

Positive Changes Implemented ✓

  1. jQuery Removal - Successfully migrated to vanilla JavaScript
    • Eliminated 85KB dependency
    • All modules (masonry-init.js, info-page.js, preloader.js) now use native APIs
  2. Image Optimization Progress
    • 189 of 213 images converted to WebP (89% coverage)
    • Lazy loading implemented on most images
    • Picture element with WebP fallback in img.html include
  3. Script Loading Strategy
    • All scripts use defer attribute for non-blocking loading
    • Proper execution order maintained
    • SRI hashes implemented for security

Areas Requiring Attention ⚠️

  1. Incomplete WebP conversion - 24 images still in JPG/PNG only
  2. No AVIF support - Missing next-gen format
  3. Large unoptimized libraries - GSAP (64KB), Masonry (24KB)

2. Core Web Vitals Analysis

Largest Contentful Paint (LCP) - POOR

Current Issues:

  • Preloader blocks rendering for 950ms minimum
  • No critical CSS extraction
  • Font loading not optimized
  • Hero images lack priority hints

Impact: LCP likely >2.5s (poor threshold)

First Input Delay (FID) - MODERATE

Current Issues:

  • Multiple window.load event handlers compete for main thread
  • GSAP animation runs continuously (setInterval every 300ms)
  • No code splitting or progressive enhancement

Impact: FID likely 100-300ms (needs improvement)

Cumulative Layout Shift (CLS) - POOR

Current Issues:

  • Grid items have no explicit dimensions
  • Images missing width/height attributes
  • Masonry layout causes reflow after image load
  • Staggered animation causes 40ms * n shifts

Impact: CLS likely >0.25 (poor threshold)

First Contentful Paint (FCP) - POOR

Current Issues:

  • Preloader covers entire viewport
  • No critical CSS inlining
  • External font requests block rendering

Impact: FCP likely >1.8s


3. Critical Performance Bottlenecks

Severity: CRITICAL 🔴

1. Blocking Preloader Pattern

File: /Users/mf/code/forscher/js/preloader.js

// Current implementation blocks for 950ms minimum
setTimeout(function() {
    preloader.style.display = 'none';
}, 600);
setTimeout(function() {
    document.body.style.overflow = 'visible';
}, 350);

Impact: Adds 950ms to all Core Web Vitals metrics

2. Continuous Animation Loop

File: /Users/mf/code/forscher/js/animation.js

setInterval(() => {
    if (document.querySelectorAll('.circle').length < maxCircles) {
        createCircle();
    }
}, 300); // Runs indefinitely

Impact: Constant CPU usage, battery drain, potential memory leak

Severity: HIGH 🟡

3. Missing Image Dimensions

Files: Various templates

<img src="" alt="" loading="lazy">
<!-- Missing width and height attributes -->

Impact: CLS shifts as images load

4. Render-Blocking Resources

File: /Users/mf/code/forscher/_includes/header.html

<link rel="stylesheet" type="text/css" href="/style.css" />
<!-- No critical CSS extraction -->

Impact: Delays FCP by ~200-400ms

Severity: MODERATE 🟢

5. Unoptimized Font Loading

@font-face {
  font-family: "Inconsolata";
  src: url('fonts/Inconsolata/Inconsolata-Regular.woff') format('woff');
  /* Missing font-display: swap */
}

Impact: FOIT (Flash of Invisible Text)


4. Optimization Recommendations

Priority 1: Core Web Vitals (Immediate Impact)

Remove Blocking Preloader

// Replace preloader.js with progressive enhancement
document.addEventListener('DOMContentLoaded', function() {
    const preloader = document.getElementById('preloader');
    if (preloader) {
        preloader.style.display = 'none';
    }
    document.body.style.overflow = 'visible';
});

Expected Improvement: -950ms to LCP, FCP

Add Image Dimensions

<!-- Update templates with explicit dimensions -->
<img src=""
     alt=""
     width="400"
     height="300"
     loading="lazy">

Expected Improvement: CLS < 0.1

Optimize Animation Performance

// Use requestAnimationFrame instead of setInterval
let animationFrame;
function animateCircles() {
    if (document.querySelectorAll('.circle').length < maxCircles) {
        createCircle();
    }
    animationFrame = requestAnimationFrame(animateCircles);
}
// Add cleanup on page unload
window.addEventListener('beforeunload', () => {
    cancelAnimationFrame(animationFrame);
});

Expected Improvement: -50% CPU usage, better FID

Priority 2: Asset Optimization

Implement Critical CSS

<!-- Inline critical CSS in head -->
<style>
/* Critical above-the-fold styles */
.layout { /* ... */ }
.nav { /* ... */ }
</style>
<link rel="preload" href="/style.css" as="style">
<link rel="stylesheet" href="/style.css" media="print" onload="this.media='all'">

Expected Improvement: -300ms to FCP

Add Font Display Swap

@font-face {
  font-family: "Inconsolata";
  font-display: swap; // Add this
  src: url('fonts/Inconsolata/Inconsolata-Regular.woff2') format('woff2'),
       url('fonts/Inconsolata/Inconsolata-Regular.woff') format('woff');
}

Expected Improvement: Eliminate FOIT

Implement Modern Image Formats

<picture>
  <source type="image/avif" srcset=".avif">
  <source type="image/webp" srcset=".webp">
  <img src="" alt=""
       width=""
       height=""
       loading="lazy">
</picture>

Expected Improvement: -30% image payload

Priority 3: Loading Performance

Implement Resource Hints

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="dns-prefetch" href="https://www.google-analytics.com">

Expected Improvement: -100ms connection setup

Code Splitting for Heavy Libraries

// Lazy load GSAP only when needed
if (window.pageType === 'home') {
    import('/js/gsap.min.js').then(() => {
        import('/js/animation.js');
    });
}

Expected Improvement: -64KB initial payload

Progressive Masonry Enhancement

// Start with CSS Grid, enhance with Masonry
.grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
// Load Masonry only after user interaction or scroll

Expected Improvement: -30KB initial payload


5. Expected Performance Improvements

Core Web Vitals Predictions

| Metric | Current (Est.) | After Optimizations | Improvement | |——–|—————|——————-|————-| | LCP | >2.5s | <1.8s | -28% | | FID | 100-300ms | <50ms | -70% | | CLS | >0.25 | <0.05 | -80% | | FCP | >1.8s | <1.0s | -44% |

Resource Loading Improvements

| Resource | Current Size | Optimized Size | Savings | |———-|————-|—————-|———| | JavaScript | 197KB | 95KB | -52% | | Images (avg) | 500KB | 200KB | -60% | | CSS | 12KB | 8KB (critical) + 4KB | Same | | Fonts | 40KB | 40KB (swap) | Faster |

User Experience Improvements

  • Perceived Performance: 40-60% faster page loads
  • Interactivity: Immediate response to user input
  • Visual Stability: No layout shifts during load
  • Battery Usage: 50% reduction in CPU usage

6. Implementation Priority Matrix

Quick Wins (1-2 hours)

  1. Remove blocking preloader ⚡
  2. Add font-display: swap 🔤
  3. Add image dimensions 📐
  4. Fix animation loop 🔄

Medium Effort (2-4 hours)

  1. Extract critical CSS 🎨
  2. Implement resource hints 🔗
  3. Complete WebP conversion 🖼️
  4. Optimize font loading 📝

Larger Projects (4-8 hours)

  1. Implement code splitting 📦
  2. Add AVIF support 🏞️
  3. Progressive enhancement strategy 📈
  4. Performance monitoring setup 📊

7. Monitoring & Validation

  1. Lighthouse CI - Automated performance testing
  2. Web Vitals Library - Real user monitoring
  3. Performance Observer API - Custom metrics
  4. Bundle Analyzer - JavaScript optimization

Key Metrics to Track

  • Core Web Vitals (LCP, FID, CLS, FCP, TTFB)
  • JavaScript execution time
  • Image loading performance
  • User interaction latency

Performance Budget

{
  "timings": {
    "firstContentfulPaint": 1000,
    "largestContentfulPaint": 1800,
    "firstInputDelay": 50,
    "cumulativeLayoutShift": 0.05
  },
  "resourceSizes": {
    "script": 100000,
    "image": 200000,
    "stylesheet": 15000,
    "total": 500000
  }
}

Conclusion

The forscher.com site has made good progress with recent optimizations, particularly the jQuery removal and WebP adoption. However, critical performance bottlenecks remain that significantly impact user experience and Core Web Vitals.

The most impactful immediate optimization would be removing the blocking preloader, which alone would improve all metrics by approximately 1 second. Combined with the other Priority 1 recommendations, the site could achieve “Good” Core Web Vitals scores within a day of implementation.

The recommended optimizations follow a progressive enhancement approach, ensuring the site remains functional while significantly improving performance for modern browsers. Implementation should focus on quick wins first, then proceed with medium-effort optimizations that provide the best return on investment.