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
📁 Project Structure
src/
├── App.jsx # Main 3D scene
├── App.css # Styling for interface
└── main.jsx # Entry point
public/
└── Monitor.glb # Your 3D model
🖥️ 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);
}
🎬 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
🔧 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>
)
}
✨ 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>
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
>
🎯 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
)
}
🎨 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>
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);
}
🎭 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>
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
-
Optimize CubeCamera: Use
frames={2}
for static scenes - Resolution balance: 512px is usually sufficient for reflections
- Conditional rendering: Only apply expensive materials where needed
- 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
- 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
- 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"
🔧 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:
- Download your chosen model
-
Place the
.glb
file in yourpublic/
folder -
Rename it to
Monitor.glb
or update the code path - 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>
)
}
🚀 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
- React Three Fiber Docs
- @react-three/drei Documentation
- Three.js Documentation
- Complete Code Repository
🎉 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)