DEV Community

Cover image for Building a Realistic 3D Monitor with Reflections and HTML Interface using React Three Fiber
A0mineTV
A0mineTV

Posted on

Building a Realistic 3D Monitor with Reflections and HTML Interface using React Three Fiber

Ever wanted to create stunning 3D visualizations that feel truly interactive? Today, we'll build a photorealistic 3D monitor with dynamic reflections and an HTML interface overlay using React Three Fiber and @react-three/drei.

🎯 What We'll Build

By the end of this tutorial, you'll have:

  • ✅ A 3D monitor model with realistic reflections
  • ✅ Smooth presentation controls for interaction
  • ✅ HTML interface overlay on the screen
  • ✅ Professional lighting and shadows
  • ✅ Full-screen immersive experience

🚀 Getting Started

Prerequisites

  • Basic React knowledge
  • Node.js installed
  • A GLTF/GLB 3D model (we'll use Monitor.glb)

Installation

npm create vite@latest 3d-monitor-demo -- --template react
cd 3d-monitor-demo
npm install @react-three/fiber @react-three/drei
npm run dev
Enter fullscreen mode Exit fullscreen mode

📁 Project Structure

src/
├── App.jsx       # Main 3D scene
├── App.css       # Styling for interface
└── main.jsx      # Entry point

public/
└── Monitor.glb   # Your 3D model
Enter fullscreen mode Exit fullscreen mode

🖥️ Setting Up the Full-Screen Canvas

First, let's create an immersive full-screen experience:

src/App.css

#root {
  max-width: 100vw;
  margin: 0;
  padding: 0;
  text-align: center;
  height: 100vh;
  overflow: hidden;
}

.canvas-container {
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0;
  left: 0;
}

.annotation {
  background: rgba(0, 0, 0, 0.8);
  color: white;
  padding: 8px 12px;
  border-radius: 6px;
  font-family: 'Arial', sans-serif;
  font-size: 12px;
  font-weight: bold;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  border: 1px solid rgba(255, 255, 255, 0.1);
}
Enter fullscreen mode Exit fullscreen mode

🎬 Building the 3D Scene

src/App.jsx

import './App.css'
import { Canvas } from '@react-three/fiber'
import { PresentationControls, ContactShadows, Environment, useGLTF, Html, CubeCamera } from '@react-three/drei'

function App() {
  return (
    <div className="canvas-container">
      <Canvas shadows camera={{ position: [0, 0, 20], fov: 35 }}>
        <ambientLight intensity={0.5} />
        <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} shadow-mapSize={2048} castShadow />

        {/* CubeCamera for realistic reflections */}
        <CubeCamera resolution={512} frames={2} near={0.1} far={1000}>
          {(texture) => (
            <PresentationControls
              global
              config={{ mass: 2, tension: 500 }}
              snap={{ mass: 4, tension: 1500 }}
              rotation={[0, 0.3, 0]}
              polar={[-Math.PI / 3, Math.PI / 3]}
              azimuth={[-Math.PI / 1.4, Math.PI / 2]}>
              <Monitor 
                rotation={[0, Math.PI, 0]} 
                position={[0, 0, 0]} 
                scale={5} 
                envMapTexture={texture}
              />
            </PresentationControls>
          )}
        </CubeCamera>

        <ContactShadows position={[0, -3, 0]} opacity={0.75} scale={15} blur={3} far={4} />
        <Environment preset="city" />
      </Canvas>
    </div>
  )
}

export default App
Enter fullscreen mode Exit fullscreen mode

🔧 Creating the Monitor Component

The magic happens in our Monitor component where we combine GLTF loading, reflections, and HTML overlays:

function Monitor({ envMapTexture, ...props }) {
  const { nodes, materials } = useGLTF('/Monitor.glb')

  return (
    <group {...props} dispose={null}>
      {/* Render all meshes with smart material detection */}
      {Object.entries(nodes).map(([key, node]) => {
        if (node.geometry) {
          // Detect screen elements for special treatment
          const isScreen = key.toLowerCase().includes('screen') || 
                          key.toLowerCase().includes('display');

          return (
            <mesh 
              key={key}
              castShadow 
              receiveShadow 
              geometry={node.geometry} 
              material={
                isScreen ? (
                  // Reflective material for screen
                  <meshStandardMaterial 
                    envMap={envMapTexture}
                    metalness={0.9}
                    roughness={0.1}
                    color="#000"
                  />
                ) : (
                  // Original materials for other parts
                  node.material || materials[Object.keys(materials)[0]]
                )
              }
            />
          )
        }
        return null
      })}

      {/* HTML annotation positioned next to monitor */}
      <Html scale={0.3} rotation={[0, -Math.PI, 0]} position={[1, 0, 0]} transform>
        <div className="annotation">
          Monitor 3D 🖥️
        </div>
      </Html>
    </group>
  )
}
Enter fullscreen mode Exit fullscreen mode

✨ Key Features Explained

🪞 CubeCamera for Reflections

The CubeCamera captures the environment in real-time, creating realistic reflections on the monitor screen:

<CubeCamera resolution={512} frames={2} near={0.1} far={1000}>
  {(texture) => (
    // Your 3D content here receives the reflection texture
  )}
</CubeCamera>
Enter fullscreen mode Exit fullscreen mode

Parameters:

  • resolution={512}: Good balance between quality and performance
  • frames={2}: Perfect for static scenes (updates every 2 frames)
  • The texture is automatically passed to your components
<PresentationControls
  global                                    // Mouse controls work globally
  config={{ mass: 2, tension: 500 }}      // Physics-based movement
  snap={{ mass: 4, tension: 1500 }}       // Snap-back behavior
  rotation={[0, 0.3, 0]}                   // Initial rotation
  polar={[-Math.PI / 3, Math.PI / 3]}     // Vertical rotation limits
  azimuth={[-Math.PI / 1.4, Math.PI / 2]} // Horizontal rotation limits
>
Enter fullscreen mode Exit fullscreen mode

🎯 Smart Material Detection

Automatically applies reflective materials to screen elements:

const isScreen = key.toLowerCase().includes('screen') || 
                key.toLowerCase().includes('display');

// Apply reflective material only to screen parts
material={
  isScreen ? (
    <meshStandardMaterial 
      envMap={envMapTexture}
      metalness={0.9}      // High metalness for reflection
      roughness={0.1}      // Low roughness for mirror-like surface
      color="#000"
    />
  ) : (
    node.material        // Keep original materials for other parts
  )
}
Enter fullscreen mode Exit fullscreen mode

🎨 Bonus: HTML Interface Overlay (Optional)

Note: This section is optional! The core 3D monitor with reflections works perfectly without any HTML overlay. Add this only if you want interactive content on the screen.

If you want to add interactive HTML content directly on the monitor screen, you can overlay HTML elements:

{/* Optional: HTML interface positioned on screen */}
<Html 
  scale={0.08} 
  rotation={[0, -Math.PI, 0]} 
  position={[0, 0, 0.8]} 
  transform 
  distanceFactor={5}
>
  <div className="screen-interface">
    <div className="status-bar">
      <span className="time">14:32</span>
      <span className="battery">🔋 85%</span>
    </div>

    <div className="desktop">
      <div className="app-icon">📁</div>
      <div className="app-icon">🎵</div>
      <div className="app-icon">📧</div>
      <div className="app-icon">⚙️</div>
    </div>

    <div className="taskbar">
      <div className="start-button">🪟 Start</div>
      <div className="running-apps">
        <span className="app">Chrome</span>
        <span className="app">VSCode</span>
      </div>
    </div>
  </div>
</Html>
Enter fullscreen mode Exit fullscreen mode

If you choose to add this overlay, include the CSS:

.screen-interface {
  width: 800px;
  height: 500px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  overflow: hidden;
  box-shadow: inset 0 0 50px rgba(0, 0, 0, 0.2);
  border: 2px solid #333;
}

.app-icon {
  width: 60px;
  height: 60px;
  background: rgba(255, 255, 255, 0.2);
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  cursor: pointer;
  transition: all 0.3s ease;
  backdrop-filter: blur(10px);
}

.app-icon:hover {
  background: rgba(255, 255, 255, 0.3);
  transform: translateY(-2px);
}
Enter fullscreen mode Exit fullscreen mode

🎭 Alternative Screen Content Ideas:

Instead of a full desktop interface, you could add simpler content:

// Simple logo or text on screen
<Html scale={0.1} position={[0, 0, 0.5]} transform>
  <div style={{
    color: 'white', 
    fontSize: '24px', 
    fontWeight: 'bold',
    textAlign: 'center'
  }}>
    Welcome to 3D Web
  </div>
</Html>

// Or a video element
<Html scale={0.08} position={[0, 0, 0.5]} transform>
  <video 
    width="400" 
    height="300" 
    autoPlay 
    loop 
    muted
    src="/demo-video.mp4"
  />
</Html>
Enter fullscreen mode Exit fullscreen mode

Why you might skip the HTML overlay:

  • Simpler setup - Focus on the 3D aspects
  • Better performance - Less DOM manipulation
  • Cleaner aesthetics - Let the reflections shine
  • More realistic - Real monitors don't float interfaces

🎪 Performance Tips

  1. Optimize CubeCamera: Use frames={2} for static scenes
  2. Resolution balance: 512px is usually sufficient for reflections
  3. Conditional rendering: Only apply expensive materials where needed
  4. LOD: Consider using Level of Detail for complex models

🛠️ Finding 3D Models

You'll need a GLTF/GLB 3D model to follow this tutorial. Here are the best sources for high-quality 3D monitor models:

🔥 Top Free Sources:

1. Sketchfab

  • Search terms: "monitor", "computer screen", "display", "LCD monitor"
  • Pros: Huge collection, preview in 3D before download
  • License: Check individual model licenses (CC, free for personal use)
  • Example searches:

2. Poly Haven

  • Pros: CC0 license (completely free), high quality
  • Cons: Smaller selection, but excellent quality
  • Perfect for: Professional projects

3. Kenney Assets

  • Pros: Game-ready models, consistent style
  • Search: Office/Electronics section
  • License: CC0 (free for any use)

4. Google Poly Archive (Poly Pizza)

  • Pros: Archive of Google's 3D models
  • Search terms: "monitor", "computer", "screen"
  • License: Various, check individual models

💎 Premium Sources (Paid):

1. TurboSquid

  • Best for: Professional, detailed models
  • Price range: $5-$200+
  • Search: "computer monitor GLTF" or "monitor GLB"

2. CGTrader

  • Pros: Good variety, competitive prices
  • Formats: Always check GLTF/GLB availability

3. Fab (Epic Games Store)

  • Pros: High-quality game assets
  • Integration: Works well with Unreal/Unity workflows

🎯 Specific Monitor Model Recommendations:

# Example searches that work well:
"Dell monitor GLTF"
"Apple monitor 3D model"
"Gaming monitor free download"
"Computer screen GLB"
"LCD display 3D"
Enter fullscreen mode Exit fullscreen mode

🔧 Model Requirements for This Tutorial:

When choosing a monitor model, ensure it has:

  • GLTF/GLB format (not OBJ, FBX, or other formats)
  • Reasonable polygon count (under 50k triangles for web)
  • Proper naming (ideally with "screen" or "display" in mesh names)
  • Good UVs (for textures and materials)

📥 How to Download and Prepare:

  1. Download your chosen model
  2. Place the .glb file in your public/ folder
  3. Rename it to Monitor.glb or update the code path
  4. Test by checking browser console for loading errors

🔍 Alternative: Create Your Own Simple Monitor

If you can't find the perfect model, you can create a basic one using:

// Simple monitor using basic geometries
function SimpleMonitor() {
  return (
    <group>
      {/* Monitor stand */}
      <mesh position={[0, -1, 0]}>
        <cylinderGeometry args={[0.3, 0.3, 0.1, 8]} />
        <meshStandardMaterial color="#333" />
      </mesh>

      {/* Monitor body */}
      <mesh position={[0, 0, -0.1]}>
        <boxGeometry args={[4, 2.5, 0.2]} />
        <meshStandardMaterial color="#222" />
      </mesh>

      {/* Screen */}
      <mesh position={[0, 0, 0]}>
        <boxGeometry args={[3.6, 2.1, 0.01]} />
        <meshStandardMaterial 
          color="#000" 
          metalness={0.9} 
          roughness={0.1}
        />
      </mesh>
    </group>
  )
}
Enter fullscreen mode Exit fullscreen mode

🚀 Pro Tips for Model Selection:

  • File size: Keep under 10MB for web performance
  • Complexity: More details = better visuals but slower loading
  • Licensing: Always check commercial use permissions
  • Preview: Use Sketchfab's 3D viewer to inspect before download
  • Backup options: Download 2-3 models in case one doesn't work

🎯 Next Steps

  • Animations: Add model animations with useAnimations
  • Physics: Integrate @react-three/cannon for physics
  • VR Support: Add @react-three/xr for VR experiences
  • Post-processing: Use @react-three/postprocessing for effects

🔗 Resources

🎉 Conclusion

You've just created a stunning 3D monitor visualization with:

  • ✅ Realistic reflections using CubeCamera
  • ✅ Smooth interactive controls
  • ✅ HTML interface overlays
  • ✅ Professional lighting and shadows

The combination of React Three Fiber and @react-three/drei makes creating complex 3D experiences surprisingly straightforward. The declarative React approach to 3D graphics opens up endless possibilities for web applications.

Top comments (0)