fix(3d/ui): Dispose Three.js geometries, materials, and canvas listeners on unmount to prevent WebGL crashes on mobile#394
Merged
2 commits merged intoMay 31, 2026
Conversation
Contributor
Author
|
@Aditya948351 kindly merge it |
05d6327
Aditya948351
approved these changes
May 31, 2026
Collaborator
Aditya948351
left a comment
There was a problem hiding this comment.
Disposing of the Three.js materials properly on unmount is crucial for mobile performance. Fantastic fix for the WebGL memory leaks!
Contributor
Author
|
@Aditya948351 kindly add labels according to issue |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(3d/ui): Dispose Three.js geometries, materials, and canvas listeners on unmount to prevent WebGL crashes on mobile #364
Summary
Audited all canvas and Three.js components and resolved three distinct resource leak
patterns that collectively caused WebGL context crashes on mobile devices, dangling
event listeners accumulating across route changes, and GPU memory never being freed
between theme/color transitions.
Two files were patched.
InteractiveBackground.tsxwas already correct and requiredno changes.
Root Cause Analysis
Bug 1 —
HeaderScene.tsx/Model: Materials leaked on every color changeEvery time the
colorprop changed (e.g. theme toggle), theuseEffectran again,creating N new
MeshStandardMaterialinstances and assigning them to meshes —without ever calling
.dispose()on the materials being replaced. On mobileWebGL contexts, which have a strict GPU memory budget, this silently accumulated
allocated shader programs and texture slots until the browser killed the context.
Bug 2 —
HeaderScene.tsx/FallbackGeometry: Geometry and material never freed on unmountWhen
FallbackGeometryunmounted (e.g. navigating away from the homepage), theBufferGeometryfor the torus knot and its associatedMeshStandardMaterialremained allocated on the WebGL context. React Three Fiber does not automatically
call
.dispose()on JSX-declared Three.js objects — this is an intentional designdecision by R3F to give authors control. Without an explicit cleanup, every unmount
leaked GPU resources.
Bug 3 —
ParticleSystem.tsx: Anonymous resize listener impossible to deregisterremoveEventListenerrequires the exact same function reference that was passedto
addEventListener. Because an inline arrow function creates a new anonymousfunction object that is immediately discarded, the cleanup's call to
removeEventListener('resize', resizeCanvas)silently did nothing. Every timethe component mounted, a new resize listener accumulated. On SPAs with frequent
route changes, this eventually produced dozens of stale, duplicate listeners all
firing on every window resize — updating canvas dimensions on a dead, unmounted
component.
Fix
src/components/3d/HeaderScene.tsxModel— dispose replaced and created materialsFallbackGeometry— dispose geometry and material on unmountThe
meshreference is captured at mount time (before the ref can become null),ensuring the cleanup always has access to the correct object. The
Array.isArrayguard handles both single-material and multi-material mesh configurations.
src/components/ui/ParticleSystem.tsxFix dangling resize listener via named function reference
Files Changed
src/components/3d/HeaderScene.tsxuseEffectcleanup; add unmount disposaluseEffecttoFallbackGeometrysrc/components/ui/ParticleSystem.tsxhandleResizefunction soremoveEventListenercan correctly deregister itsrc/components/ui/InteractiveBackground.tsxVerification
unreleased shader programs; now each toggle disposes the prior materials before creating new ones.
chrome://gpuoruse the Memory tab in DevTools; WebGL memory should return to baseline after each navigation.
N resize callbacks for N mount cycles; now exactly one fires.
WebGL: CONTEXT_LOST_WEBGLerrors orWarning: Can't perform a React state update on an unmounted componentmessages.Impact
low-memory mobile devices caused by accumulated unreleased GPU buffers.
and color-change cycle, keeping GPU allocation flat across the session.
ParticleSystemno longer accumulates stale resize listenersacross route changes — listener count stays at exactly one regardless of mount frequency.
purely additive cleanup with no effect on render output.