BLOG

Stories, experiments, and cool things I've developed

Remember that performance monitoring system I built? The one I was so proud of? Well, it turns out that was just a band-aid on a much bigger problem. Let me tell you about how my portfolio's past caught up with me.

The Origin Story

When I first built this portfolio, I had just learned web development. The only framework I knew was Angular - and an old version at that. I had no concept of maintainability, code quality, or best practices. The result? One massive HTML file, one enormous SCSS file, and one absolutely monstrous TypeScript file containing my entire website.

But here's the thing - it worked. And when something works, you keep building on it. Over the years, I kept adding features: the WebGL galaxy background, the click spark effects, the decrypt animations, the Magic Bento hover effects. Each addition made the codebase more tangled, but the site kept looking better and better.

The Breaking Point

The performance monitor was my attempt to fix everything without addressing the root cause. It was like putting a turbocharger on a car with a cracked engine block. Sure, it helped - but the foundation was crumbling.

The real problems were deeper:

• No code splitting - users loaded everything at once
• No lazy loading - below-fold content blocked initial render
• Outdated Angular patterns - no modern optimization techniques
• Unmaintainable structure - any change risked breaking everything
• No SSR - poor SEO and slow initial paint

The Decision

I knew what I needed: a complete rewrite. Not a refactor. Not an upgrade. A full, ground-up rebuild using modern best practices. The choice of framework was easy - Next.js 15 with React 19, the latest and greatest, with server components, app router, and built-in optimizations.

The Migration Journey

Here's how I approached the rewrite:

1. Component Architecture
Instead of one monolithic file, I broke everything into focused components. Each section (Hero, Education, Experience, Projects, Skills, Interests) became its own module with clear responsibilities.

2. Server Components
Next.js server components meant I could render static content on the server and only hydrate interactive parts. The Hero shell renders on the server; the decrypt animation hydrates on the client.

3. Dynamic Imports
Below-fold sections are now lazy loaded with next/dynamic. Users see the hero instantly while the rest loads in the background.

4. Porting the WebGL
The trickiest part was porting the galaxy background. The GLSL shaders translated directly, but the React lifecycle required careful handling of the WebGL context - creating on mount, cleanup on unmount, and proper resize handling.

5. Asset Optimization
Every PNG became a WebP. Every image uses next/image for automatic optimization. The result? Dramatically smaller payloads.

The Results

The new portfolio is everything the old one couldn't be:

Maintainable - Clean component structure, no comments needed
Fast - Server-rendered, lazy-loaded, optimized assets
Modern - Next.js 15, React 19, latest best practices
SEO-ready - Proper meta tags, server rendering, sitemap
Buttery smooth - GPU-accelerated animations, event delegation

And the best part? That performance monitor I was so proud of? I removed it entirely. When your foundation is solid, you don't need band-aids.

Sometimes the right answer isn't to fix what's broken - it's to build something better.

It all started when my friend Salim - did I mention he's a fellow developer? Because he certainly won't let me forget it - visited my portfolio on his older Android phone. And he was right.

The Problem

My portfolio looked gorgeous on modern devices with its WebGL galaxy background, particle effects, and smooth animations. But on older phones? It was a slideshow. The frame rate would drop to 15-20 FPS, making the entire experience feel janky and unresponsive.

The Solution: Dynamic Performance Monitoring

Instead of creating a dumbed-down mobile version, I built a sophisticated performance monitoring service that adapts in real-time. Here's how it works:

1. Initial Device Detection
On load, the service checks hardware capabilities including CPU cores, available memory, GPU tier, and even battery level. It also respects user preferences like reduced motion.

2. Real-Time FPS Monitoring
The service continuously monitors frame rates using requestAnimationFrame, maintaining a rolling average over 5 seconds. If performance drops below certain thresholds (30 FPS for low, 45 FPS for medium), it automatically adjusts.

3. Dynamic Quality Adjustment
Based on performance metrics, the system adjusts:
• Galaxy star density (0.3x to 0.8x)
• Particle counts in effects (4-8 sparks)
• Animation complexity and glow intensity
• WebGL shader calculations

Secret Developer Tools

For testing and debugging, I built a hidden performance overlay that's only accessible through the browser console. It's a floating widget that displays real-time performance metrics and allows manual control over performance levels.

To access it, developers can open the console and use commands like:
perf.show() - Reveals the performance monitor
perf.setLevel('low') - Forces a specific performance tier
perf.help() - Shows all available commands

This approach keeps the UI clean for regular users while giving developers powerful debugging tools. The overlay shows current FPS, performance level, memory usage, and battery status - everything needed to diagnose performance issues across different devices.

Even Salim was impressed.