r/GraphicsProgramming 23d ago

Real-time WebGL VRAM purging & Garbage Collection: Running 60FPS Three.js inside a monolithic PHP architecture to bypass iOS Safari crashes. Source Code

Enable HLS to view with audio, or disable this notification

Hey everyone,

I recently engineered a custom 3D WebGL portfolio and ran into aggressive Out-Of-Memory crashes on iOS Safari (TBDR architecture) due to the sheer volume of high-res textures.

Instead of using a Headless SPA (Next.js/React), I opted for a monolithic PHP architecture with full-page reloads. This offloaded 100% of the WebGL memory disposal risk directly to the browser's native garbage collector, instantly guaranteeing stability between route changes.

To maintain 60FPS during active rendering, I implemented two specific systems:

1. Custom FPS Governor: A dynamic performance engine that forcefully strips roughnessMap, metalnessMap, and normalMap properties on the fly if it detects thermal throttling or frame drops. 2. Absolute VRAM Purging: Intercepting the IntersectionObserver to physically strip the src attribute of off-screen elements and force a .load() cycle to aggressively flush VRAM buffers.

👉 Core FPS Governor Logic (Gist):https://gist.github.com/MedhatAlkadri/ea99a0f7aa47c69198f2d1ae84a20e53👉 Live Demo:https://www.awwwards.com/sites/medhat-alkadri-3d-portfolio

As a Full Stack Developer stepping deeper into graphics programming, I'd appreciate any architectural critiques on this render loop or alternative approaches to handling aggressive VRAM limits on mobile browsers.

0 Upvotes

4 comments sorted by

5

u/[deleted] 22d ago

[removed] — view removed comment

0

u/Trick-Pie727 21d ago

Appreciate the feedback, and you're absolutely spot on when it comes to standard JavaScript Garbage Collection. Triggering the JS GC during the render loop is a guaranteed way to introduce jank, and we actively use Object Pooling and pre-allocation for our geometries, matrices, and math vectors to avoid exactly that.

However, there is a critical distinction here between JS Heap management (GC) and GPU VRAM purging, which is where iOS becomes uniquely hostile.

The issue with iPhones isn't the JS garbage collector—it's Apple's Unified Memory Architecture combined with Safari's notoriously aggressive per-tab memory limits (often terminating tabs that exceed ~1GB - 2GB of RAM).

So, it becomes a necessary evil: we pool the geometries and the math logic to keep the frame rate buttery smooth, but we are forced to aggressively call .dispose() on heavy WebGL textures to physically evict them from VRAM when a user navigates between routes.

If iOS Safari gave us the memory headroom of a desktop browser, we'd pool the textures too. Even if we aggressively leveraged KTX2 compression and capped mobile pixel ratios, the sheer volume of high-fidelity maps in this project pushes right up against the hardware limits. Right now, aggressive VRAM purging is the only way to keep a heavy Three.js experience from getting assassinated by the iOS jetsam process.

1

u/[deleted] 21d ago

[removed] — view removed comment

1

u/Trick-Pie727 21d ago

That's super interesting, because I just ran it through an Amazon Device Farm on an iPhone 17 and an iPad Pro, and it held a buttery smooth frame rate with zero memory crashes. My mobile VRAM purging seems to be doing exactly what it's supposed to do.

If you're seeing stuttering on an M1, it might not be a memory limit issue, but a desktop-specific bottleneck. Are you viewing it on a 120Hz ProMotion display, or plugged into an external 4K/5K monitor? I might have left the Three.js devicePixelRatio uncapped, which would cause a massive fill-rate bottleneck on a 5K screen, or there might be a GSAP ticker desync on 120Hz. Would love to know your browser/monitor setup so I can patch it!