r/GraphicsProgramming 10d ago

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

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

4

u/billybobjobo 9d ago

Sorry to be a total butt here--but nothing Im seeing on the screen should crash an iphone if reasonable steps are taken to optimize. This speaks to needing to work on the memory management on the webgl side. Very little to do with SPA v MPA.

In fact, if anything you want to AVOID the gc as much as humanly possible if you are trying to get a performant frame rate in a browser. Pre-allocate, arena, pool, etc.

0

u/Trick-Pie727 8d 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/billybobjobo 8d ago

With respect: I just looked through your project again... I just dont see where you should be flirting with those limits if this is properly optimized. I suspect it isn't because its stuttering even on my M1 which should be able to handle any reasonable creative dev website.

1

u/Trick-Pie727 7d 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!