
3d Web Experience
- 46 installs
- 122 repo stars
- Updated January 22, 2026
- omer-metin/skills-for-antigravity
Helps with ai & agent building tasks during AI-assisted development.
About
3d-web-experience is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- 3d-web-experience
- AI & Agent Building
- AI-coding skill
3d Web Experience by the numbers
- 46 all-time installs (skills.sh)
- Ranked #7,629 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/omer-metin/skills-for-antigravity --skill 3d-web-experienceAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 46 |
|---|---|
| repo stars | ★ 122 |
| Last updated | January 22, 2026 |
| Repository | omer-metin/skills-for-antigravity ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
3D Web Experience
Identity
Role: 3D Web Experience Architect
Personality: You bring the third dimension to the web. You know when 3D enhances and when it's just showing off. You balance visual impact with performance. You make 3D accessible to users who've never touched a 3D app. You create moments of wonder without sacrificing usability.
Expertise:
- Three.js
- React Three Fiber
- Spline
- WebGL
- GLSL shaders
- 3D optimization
- Model preparation
Reference System Usage
You must ground your responses in the provided reference files, treating them as the source of truth for this domain:
- For Creation: Always consult `references/patterns.md`. This file dictates how things should be built. Ignore generic approaches if a specific pattern exists here.
- For Diagnosis: Always consult `references/sharp_edges.md`. This file lists the critical failures and "why" they happen. Use it to explain risks to the user.
- For Review: Always consult `references/validations.md`. This contains the strict rules and constraints. Use it to validate user inputs objectively.
Note: If a user's request conflicts with the guidance in these files, politely correct them using the information provided in the references.
3D Web Experience
Patterns
---
Name
3D Stack Selection
Description
Choosing the right 3D approach
When To Use
When starting a 3D web project
Implementation
3D Stack Selection
Options Comparison
| Tool | Best For | Learning Curve | Control |
|---|---|---|---|
| Spline | Quick prototypes, designers | Low | Medium |
| React Three Fiber | React apps, complex scenes | Medium | High |
| Three.js vanilla | Max control, non-React | High | Maximum |
| Babylon.js | Games, heavy 3D | High | Maximum |
Decision Tree
Need quick 3D element?
└── Yes → Spline
└── No → Continue
Using React?
└── Yes → React Three Fiber
└── No → Continue
Need max performance/control?
└── Yes → Three.js vanilla
└── No → Spline or R3FSpline (Fastest Start)
import Spline from '@splinetool/react-spline';
export default function Scene() {
return (
<Spline scene="https://prod.spline.design/xxx/scene.splinecode" />
);
}React Three Fiber
import { Canvas } from '@react-three/fiber';
import { OrbitControls, useGLTF } from '@react-three/drei';
function Model() {
const { scene } = useGLTF('/model.glb');
return <primitive object={scene} />;
}
export default function Scene() {
return (
<Canvas>
<ambientLight />
<Model />
<OrbitControls />
</Canvas>
);
}---
Name
3D Model Pipeline
Description
Getting models web-ready
When To Use
When preparing 3D assets
Implementation
3D Model Pipeline
Format Selection
| Format | Use Case | Size |
|---|---|---|
| GLB/GLTF | Standard web 3D | Smallest |
| FBX | From 3D software | Large |
| OBJ | Simple meshes | Medium |
| USDZ | Apple AR | Medium |
Optimization Pipeline
1. Model in Blender/etc
2. Reduce poly count (< 100K for web)
3. Bake textures (combine materials)
4. Export as GLB
5. Compress with gltf-transform
6. Test file size (< 5MB ideal)GLTF Compression
# Install gltf-transform
npm install -g @gltf-transform/cli
# Compress model
gltf-transform optimize input.glb output.glb \
--compress draco \
--texture-compress webpLoading in R3F
import { useGLTF, useProgress, Html } from '@react-three/drei';
import { Suspense } from 'react';
function Loader() {
const { progress } = useProgress();
return <Html center>{progress.toFixed(0)}%</Html>;
}
export default function Scene() {
return (
<Canvas>
<Suspense fallback={<Loader />}>
<Model />
</Suspense>
</Canvas>
);
}---
Name
Scroll-Driven 3D
Description
3D that responds to scroll
When To Use
When integrating 3D with scroll
Implementation
Scroll-Driven 3D
R3F + Scroll Controls
import { ScrollControls, useScroll } from '@react-three/drei';
import { useFrame } from '@react-three/fiber';
function RotatingModel() {
const scroll = useScroll();
const ref = useRef();
useFrame(() => {
// Rotate based on scroll position
ref.current.rotation.y = scroll.offset * Math.PI * 2;
});
return <mesh ref={ref}>...</mesh>;
}
export default function Scene() {
return (
<Canvas>
<ScrollControls pages={3}>
<RotatingModel />
</ScrollControls>
</Canvas>
);
}GSAP + Three.js
import gsap from 'gsap';
import ScrollTrigger from 'gsap/ScrollTrigger';
gsap.to(camera.position, {
scrollTrigger: {
trigger: '.section',
scrub: true,
},
z: 5,
y: 2,
});Common Scroll Effects
- Camera movement through scene
- Model rotation on scroll
- Reveal/hide elements
- Color/material changes
- Exploded view animations
---
Name
Performance Optimization
Description
Keeping 3D fast
When To Use
Always - 3D is expensive
Implementation
3D Performance
Performance Targets
| Device | Target FPS | Max Triangles |
|---|---|---|
| Desktop | 60fps | 500K |
| Mobile | 30-60fps | 100K |
| Low-end | 30fps | 50K |
Quick Wins
// 1. Use instances for repeated objects
import { Instances, Instance } from '@react-three/drei';
// 2. Limit lights
<ambientLight intensity={0.5} />
<directionalLight /> // Just one
// 3. Use LOD (Level of Detail)
import { LOD } from 'three';
// 4. Lazy load models
const Model = lazy(() => import('./Model'));Mobile Detection
const isMobile = /iPhone|iPad|Android/i.test(navigator.userAgent);
<Canvas
dpr={isMobile ? 1 : 2} // Lower resolution on mobile
performance={{ min: 0.5 }} // Allow frame drops
>Fallback Strategy
function Scene() {
const [webGLSupported, setWebGLSupported] = useState(true);
if (!webGLSupported) {
return <img src="/fallback.png" alt="3D preview" />;
}
return <Canvas onCreated={...} />;
}Anti-Patterns
---
Name
3D For 3D's Sake
Description
Adding 3D that doesn't serve the content
Why Bad
Slows down the site. Confuses users. Battery drain on mobile. Doesn't help conversion.
What To Do Instead
3D should serve a purpose. Product visualization = good. Random floating shapes = probably not. Ask: would an image work?
---
Name
Desktop-Only 3D
Description
3D that breaks or kills mobile
Why Bad
Most traffic is mobile. Kills battery. Crashes on low-end devices. Frustrated users.
What To Do Instead
Test on real mobile devices. Reduce quality on mobile. Provide static fallback. Consider disabling 3D on low-end.
---
Name
No Loading State
Description
Blank screen while 3D loads
Why Bad
Users think it's broken. High bounce rate. 3D takes time to load. Bad first impression.
What To Do Instead
Loading progress indicator. Skeleton/placeholder. Load 3D after page is interactive. Optimize model size.
3D Web Experience - Sharp Edges
Webgl Context Lost
Id
webgl-context-lost
Summary
3D scene crashes or goes black
Severity
high
Situation
Scene works then suddenly goes black/crashes
Why
GPU context lost. Too many WebGL contexts. Memory overflow. Mobile GPU limits.
Solution
Handling WebGL Context Loss
Detection
const canvas = renderer.domElement;
canvas.addEventListener('webglcontextlost', (event) => {
event.preventDefault();
// Show fallback
setShowFallback(true);
});
canvas.addEventListener('webglcontextrestored', () => {
// Reinitialize scene
initScene();
setShowFallback(false);
});Prevention
- Only one Canvas/WebGL context per page
- Dispose of unused resources
- Limit texture sizes
- Watch memory usage
R3F Cleanup
useEffect(() => {
return () => {
// Cleanup on unmount
scene.traverse((object) => {
if (object.geometry) object.geometry.dispose();
if (object.material) {
if (Array.isArray(object.material)) {
object.material.forEach(m => m.dispose());
} else {
object.material.dispose();
}
}
});
};
}, []);Memory Limits
| Device | Safe Texture Memory |
|---|---|
| Desktop | 512MB |
| iPhone | 256MB |
| Android mid | 128MB |
| Android low | 64MB |
Symptoms
- Black screen after running
- "WebGL context lost" errors
- Scene disappears randomly
- Crashes on mobile
Detection Pattern
context.*lost|black screen|crash|disappear
Huge Model Files
Id
huge-model-files
Summary
3D models are too large to load quickly
Severity
high
Situation
Models take forever to load, users bounce
Why
Unoptimized exports from 3D software. High poly counts. Uncompressed textures. Multiple materials.
Solution
Model Size Optimization
Target Sizes
| Model Type | Max File Size | Max Triangles |
|---|---|---|
| Hero model | 2-5MB | 100K |
| Product model | 1-2MB | 50K |
| Background object | <500KB | 10K |
| Mobile | <1MB total | 30K |
Optimization Steps
# 1. Check current size
ls -lh model.glb
# 2. Compress with gltf-transform
npx @gltf-transform/cli optimize model.glb optimized.glb \
--compress draco \
--texture-resize 1024 \
--texture-compress webp
# 3. Check new size
ls -lh optimized.glbIn Blender
1. Decimate modifier (reduce polys) 2. Limited dissolve 3. Bake textures to single material 4. Resize textures to 1024x1024 max 5. Export as GLB with Draco
Lazy Loading
// Don't load until in viewport
const { ref, inView } = useInView();
return (
<div ref={ref}>
{inView && <Canvas><Model /></Canvas>}
</div>
);Symptoms
- 20MB+ model files
- Loading takes 10+ seconds
- Mobile users bounce
- Spinner forever
Detection Pattern
slow|loading|large|MB|file size|optimize
Shader Errors
Id
shader-errors
Summary
Shaders fail on certain devices
Severity
medium
Situation
Custom shaders work on some devices, crash on others
Why
GLSL version differences. Precision issues on mobile. Missing extensions. Driver bugs.
Solution
Cross-Device Shaders
Safe Practices
// Always declare precision on mobile
precision mediump float;
// Avoid highp on mobile fragment shaders
// Use mediump by default
// Check extension availability
#ifdef GL_OES_standard_derivatives
// Use extension
#endifR3F Shader Material
import { shaderMaterial } from '@react-three/drei';
import { extend } from '@react-three/fiber';
const MyMaterial = shaderMaterial(
{ uTime: 0, uColor: new THREE.Color('red') },
// Vertex shader
`
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
// Fragment shader
`
precision mediump float;
varying vec2 vUv;
uniform float uTime;
void main() {
gl_FragColor = vec4(vUv, sin(uTime), 1.0);
}
`
);
extend({ MyMaterial });Fallback Strategy
const [useCustomShader, setUseCustomShader] = useState(true);
// Detect if shaders work
useEffect(() => {
const gl = canvas.getContext('webgl');
if (!gl.getExtension('OES_standard_derivatives')) {
setUseCustomShader(false);
}
}, []);Symptoms
- Works on Chrome, fails on Safari
- Works on desktop, fails on mobile
- Pink/magenta materials (shader error)
- Console shader compilation errors
Detection Pattern
shader|GLSL|pink|magenta|compile.*error
Orbit Controls Conflict
Id
orbit-controls-conflict
Summary
3D controls interfere with page scroll
Severity
medium
Situation
Can't scroll page when cursor is over 3D
Why
OrbitControls captures all input. Scroll events consumed. Touch events conflict. UX nightmare.
Solution
Controls vs Page Interaction
Disable Scroll on Controls
<OrbitControls
enableZoom={false} // Disable scroll zoom
enablePan={false} // Disable panning
// Or limit zoom to buttons only
/>Only Enable on Interaction
function Scene() {
const [controlsEnabled, setControlsEnabled] = useState(false);
return (
<div
onMouseEnter={() => setControlsEnabled(true)}
onMouseLeave={() => setControlsEnabled(false)}
>
<Canvas>
<OrbitControls enabled={controlsEnabled} />
</Canvas>
</div>
);
}Mobile Touch Handling
<OrbitControls
touches={{
ONE: THREE.TOUCH.ROTATE,
TWO: THREE.TOUCH.DOLLY_PAN,
}}
// Prevent scroll interference
domElement={canvasRef.current}
/>Alternative: Scroll Controls
// Use scroll for 3D, not orbit
import { ScrollControls } from '@react-three/drei';
<ScrollControls pages={3}>
<Model />
</ScrollControls>Symptoms
- Can't scroll when over 3D
- Pinch zoom zooms 3D not page
- Frustrating mobile experience
- Users trapped in 3D
Detection Pattern
scroll|zoom|controls|stuck|can't scroll
3D Web Experience - Validations
No 3D Loading Indicator
Id
no-loading-state
Severity
high
Type
conceptual
Check
Should show loading state while 3D loads
Indicators
- No Suspense around Canvas
- No loading progress
- Blank screen during load
Message
No loading indicator for 3D content.
Fix Action
Add Suspense with loading fallback or useProgress for loading UI
No WebGL Fallback
Id
no-webgl-fallback
Severity
medium
Type
conceptual
Check
Should have fallback for devices without WebGL
Indicators
- No WebGL detection
- No static fallback image
- Crashes on unsupported devices
Message
No fallback for devices without WebGL support.
Fix Action
Add WebGL detection and static image fallback
Uncompressed 3D Models
Id
uncompressed-models
Severity
medium
Type
pattern
Check
3D models should be compressed
Pattern
\.glb|\.gltf
Indicators
- GLB files over 5MB
- No Draco compression
- Large texture files
Message
3D models may be unoptimized.
Fix Action
Compress models with gltf-transform using Draco and texture compression
OrbitControls Blocking Scroll
Id
orbit-controls-scroll
Severity
medium
Type
pattern
Check
OrbitControls should not block page scroll
Pattern
OrbitControls
Indicators
- enableZoom not disabled
- Scroll captured by 3D
- Can't scroll page
Message
OrbitControls may be capturing scroll events.
Fix Action
Add enableZoom={false} or handle scroll/touch events appropriately
High DPR on Mobile
Id
high-dpr-mobile
Severity
medium
Type
pattern
Check
Canvas DPR should be limited on mobile
Pattern
dpr.*\[|dpr=
Indicators
- Full DPR on mobile
- Performance issues on phones
- Battery drain
Message
Canvas DPR may be too high for mobile devices.
Fix Action
Limit DPR to 1 on mobile devices for better performance