Home/Blog/frontend engineering
Back to all articles
frontend engineering#Three.js#React Three Fiber#GLSL Shaders

3D on the Web: React Three Fiber, Custom GLSL Shaders & 60fps WebGL Optimization

RB
Rajput BhavinArchitect
Founder, Rajput Bhavin Engineering
24 August 2026•11 min read

Quick Answer: High-Performance 3D on the Web#

⚡ Quick Answer / Executive Summary
Building high-performance 3D web graphics requires React Three Fiber (R3F) for declarative scene graphs, custom GLSL (OpenGL Shading Language) shaders for GPU-parallelized vertex and fragment visual effects, Draco-compressed .glb asset pipelines, and InstancedMesh draw-call batching to render tens of thousands of dynamic 3D elements at a constant 60 frames per second without overheating client devices.

The 4 Core Bottlenecks in 3D Web Graphics#

Most 3D websites fail because developers export heavy, uncompressed 3D models from Blender and spawn hundreds of individual Three.js mesh instances:

  1. ✓Massive Asset Payloads: Loading uncompressed 50MB .gltf or .obj files that cause 10-second white screens on mobile cellular connections.
  2. ✓Excessive CPU Draw Calls: Each individual 3D mesh forces the CPU to issue a separate draw call to the GPU. Exceeding 100 draw calls causes severe frame drops.
  3. ✓GPU Memory (VRAM) Leaks: Failing to call .dispose() on geometries and textures when unmounting React components, eventually crashing the browser tab.
  4. ✓Retina Display Overdraw: Rendering high-polygon 3D canvases at full 3x Device Pixel Ratio on 4K/Retina displays, overwhelming the mobile GPU.

1. Declarative 3D Canvas in React Three Fiber (R3F)#

TypeScript
Production Code
'use client';

import { Canvas, useFrame } from '@react-three/fiber';
import { Float, OrbitControls, MeshDistortMaterial } from '@react-three/drei';
import { useRef } from 'react';
import * as THREE from 'three';

function InteractiveSphere() {
  const meshRef = useRef<THREE.Mesh>(null);

  useFrame((state, delta) => {
    if (meshRef.current) {
      meshRef.current.rotation.x += delta * 0.2;
      meshRef.current.rotation.y += delta * 0.3;
    }
  });

  return (
    <Float speed={2} rotationIntensity={1} floatIntensity={2}>
      <mesh ref={meshRef} scale={1.8}>
        <sphereGeometry args={[1, 64, 64]} />
        <MeshDistortMaterial
          color="#3b82f6"
          emissive="#1e1b4b"
          roughness={0.1}
          metalness={0.8}
          distort={0.4}
          speed={3}
        />
      </mesh>
    </Float>
  );
}

export function Hero3DScene() {
  return (
    <div className="h-[500px] w-full relative">
      <Canvas
        camera={{ position: [0, 0, 5], fov: 45 }}
        dpr={[1, 2]} // Cap pixel ratio to 2 to protect mobile GPUs
        gl={{ powerPreference: 'high-performance', antialias: true }}
      >
        <ambientLight intensity={0.7} />
        <directionalLight position={[10, 10, 5]} intensity={1.5} color="#60a5fa" />
        <InteractiveSphere />
        <OrbitControls enableZoom={false} autoRotate autoRotateSpeed={0.5} />
      </Canvas>
    </div>
  );
}

2. Custom GLSL Shaders: Real-Time Wave Displacement#

Shaders are compiled programs executed in parallel on thousands of GPU cores.

#Custom Shader Material:

TypeScript
Production Code
import { shaderMaterial } from '@react-three/drei';
import { extend } from '@react-three/fiber';
import * as THREE from 'three';

export const CyberWaveMaterial = shaderMaterial(
  {
    uTime: 0,
    uColorA: new THREE.Color('#3b82f6'),
    uColorB: new THREE.Color('#a855f7'),
  },
  // Vertex Shader
  `
    varying vec2 vUv;
    varying float vDisplacement;
    uniform float uTime;

    void main() {
      vUv = uv;
      vec3 pos = position;
      float displacement = sin(pos.x * 3.0 + uTime) * cos(pos.y * 3.0 + uTime) * 0.2;
      pos.z += displacement;
      vDisplacement = displacement;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
    }
  `,
  // Fragment Shader
  `
    varying vec2 vUv;
    varying float vDisplacement;
    uniform vec3 uColorA;
    uniform vec3 uColorB;

    void main() {
      vec3 color = mix(uColorA, uColorB, vDisplacement * 2.5 + 0.5);
      gl_FragColor = vec4(color, 0.9);
    }
  `
);

extend({ CyberWaveMaterial });

3. The 3D Optimization Playbook#

Optimization TechniqueImplementationPerformance Benefit
Draco CompressionCompress .gltf with Google DracoReduces 25MB file to under 2.1MB (90% savings)
InstancedMeshRender 10,000 particles in a single draw callEliminates CPU draw call bottlenecks
DPR Cappingdpr={[1, 2]} on Prevents GPU overheating on 4K/Retina displays
Suspense Pre-loadinguseGLTF.preload('/model.glb')Instant rendering on navigation with zero layout pop-in

4. Proper Memory Cleanup & Garbage Collection#

Three.js does not automatically garbage-collect GPU memory when components unmount. Always dispose geometries and textures in useEffect cleanup handlers:

TypeScript
Production Code
useEffect(() => {
  return () => {
    geometry.dispose();
    material.dispose();
    texture.dispose();
  };
}, [geometry, material, texture]);

Elevate Your Brand with Rajput Bhavin Engineering#

Create stunning, interactive 3D web applications, product configurators, and immersive digital platforms.

Modern Web Engineering

Building a high-performance web experience?

Rajput Bhavin Engineering builds 100/100 Core Web Vitals websites, web applications, and headless e-commerce stores using Next.js 15, React 19, and Tailwind CSS.

RB

Rajput Bhavin

Systems Architect

Founder and Systems Architect at Rajput Bhavin Engineering in Gota, Ahmedabad. Specializes in high-concurrency cloud software, Next.js web applications, multi-tenant SaaS architecture, and autonomous AI systems.

Connect Now