✳ Nairobi, Kenya
- WhatsApp+254 799 756 331
- Emailhello@muvevi.com
- LinkedInlinkedin.com/in/muvevi
- GitHubgithub.com/mutua-muvevi
- Twitter / X@muvevi
Available for freelance & full-time roles.
Let's build something great.
WebGL fragment shaders in Next.js without React Three Fiber
R3F is great until you only need a fullscreen shader — then it's 200KB of overhead for a triangle. Here's the minimal Three.js setup for background shaders in App Router.
React Three Fiber is the right tool for 3D scenes. For a fullscreen background shader, it's significant overhead — you're loading a reconciler, hooks layer, and the entire @react-three/fiber package for what is fundamentally a WebGL triangle.
The minimal setup with raw Three.js is about 60 lines:
A PlaneGeometry(2,2) with an OrthographicCamera(-1,1,1,-1,0,1) and a vertex shader that writes gl_Position = vec4(position.xy, 0.0, 1.0) bypasses the entire model/view/projection pipeline. The geometry fills clip space exactly — no matrix math needed.
For the fragment shader, I use FBM (Fractal Brownian Motion) — five octaves of value noise — with a time uniform for animation and a mouse uniform for parallax. The noise runs on the GPU so it's essentially free at 60fps, even on mobile.
The Three.js boilerplate (Scene, Renderer, animation loop) lives in a useEffect with a cleanup that disposes the renderer, geometry, and material. This prevents the memory leak that crashes tabs after repeated hot reloads.
Pixel ratio capping at 1.5 is important. Retina displays at 2x–3x pixel ratio turn a cheap shader into an expensive one. The visual difference above 1.5 is negligible; the performance difference is not.
For Next.js App Router specifically, the component needs "use client" and should use useRef for the canvas element rather than querySelector — document access in Server Components throws at build time.