
Brings Three.js, React Three Fiber, Spline, and WebGL expertise with a pragmatic eye on performance. Covers the full pipeline from model optimization and format selection to scroll-driven animations and mobile fallbacks. The patterns are practical: stack selection guides, GLTF compression commands, and device-specific performance targets. What's refreshing is the restraint, it pushes back on gratuitous 3D and emphasizes loading states, WebGL fallbacks, and keeping triangle counts sane for mobile. Good for product configurators, immersive portfolios, or any project where you need 3D that actually ships and performs, not just demos well on your MacBook.
$ npx -y skills add sickn33/antigravity-awesome-skills --skill 3d-web-experience --agent claude-codeInstalls into .claude/skills of the current project.
Expert in building 3D experiences for the web - Three.js, React Three Fiber, Spline, WebGL, and interactive 3D scenes. Covers product configurators, 3D portfolios, immersive websites, and bringing depth to web experiences.
Role: 3D Web Experience Architect
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.
Choosing the right 3D approach
When to use: When starting a 3D web project
| 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 |
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 R3F
import Spline from '@splinetool/react-spline';
export default function Scene() {
return (
<Spline scene="https://prod.spline.design/xxx/scene.splinecode" />
);
}
import { Canvas } from '@react-three/fiber';
import { OrbitControls, useGLTF } from '@react-three/drei';
import { Suspense } from 'react';
function Model() {
const { scene } = useGLTF('/model.glb');
return <primitive object={scene} />;
}
export default function Scene() {
return (
<Canvas>
<ambientLight />
<Suspense fallback={null}>
<Model />
</Suspense>
<OrbitControls enableZoom={false} />
</Canvas>
);
}
Getting models web-ready
When to use: When preparing 3D assets
| Format | Use Case | Size |
|---|---|---|
| GLB/GLTF | Standard web 3D | Depends on geometry/textures/compression |
| FBX | From 3D software | Large |
| OBJ | Simple meshes | Medium |
| USDZ | Apple AR | Medium |
1. Model in Blender/etc
2. Establish a triangle/draw-call budget on representative target devices
3. Bake textures (combine materials)
4. Export as GLB
5. Compress with gltf-transform
6. Measure transfer size, decode time, memory and frame time against the project budget
# Use the reviewed version already pinned in the project lockfile.
# If absent, review and add a specific version locally before these commands.
# Compress model
./node_modules/.bin/gltf-transform optimize input.glb output.glb \
--compress draco \
--texture-compress webp
This component needs the React/R3F/drei versions installed by the project and an
actual /model.glb asset. Add an application error boundary for failed asset loads.
import { useGLTF, useProgress, Html } from '@react-three/drei';
import { Suspense } from 'react';
import { Canvas } from '@react-three/fiber';
function Model() {
const { scene } = useGLTF('/model.glb');
return <primitive object={scene} />;
}
function Loader() {
const { progress } = useProgress();
return <Html center>{progress.toFixed(0)}%</Html>;
}
export default function Scene() {
return (
<Canvas>
<ambientLight />
<Suspense fallback={<Loader />}>
<Model />
</Suspense>
</Canvas>
);
}
3D that responds to scroll
When to use: When integrating 3D with scroll
import { ScrollControls, useScroll } from '@react-three/drei';
import { Canvas, useFrame } from '@react-three/fiber';
import { useRef } from 'react';
function RotatingModel() {
const scroll = useScroll();
const ref = useRef();
useFrame(() => {
// Rotate based on scroll position
if (ref.current) ref.current.rotation.y = scroll.offset * Math.PI * 2;
});
return (
<mesh ref={ref}>
<boxGeometry args={[1, 1, 1]} />
<meshNormalMaterial />
</mesh>
);
}
export default function Scene() {
return (
<Canvas>
<ScrollControls pages={3}>
<RotatingModel />
</ScrollControls>
</Canvas>
);
}
import gsap from 'gsap';
import ScrollTrigger from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
// Integration fragment: camera and the section are supplied by your scene.
// Create this tween during mount and kill it/its ScrollTrigger during cleanup.
gsap.to(camera.position, {
scrollTrigger: {
trigger: '.section',
scrub: true,
},
z: 5,
y: 2,
});
Keeping 3D fast
When to use: Always - 3D is expensive
The following are illustrative starting budgets, not hardware limits or guarantees. Measure GPU time, draw calls, memory and load time; triangle count alone cannot establish performance. Respect reduced-motion settings and retain ordinary controls.
| Device | Target FPS | Max Triangles |
|---|---|---|
| Desktop | 60fps | 500K |
| Mobile | 30-60fps | 100K |
| Low-end | 30fps | 50K |
These are independent integration fragments, not one copyable module. Configure instances/LOD and lights in your actual scene, and wrap lazy components in Suspense.
// 1. Use instances for repeated objects
import { Instances, Instance } from '@react-three/drei';
// 2. Limit lights
const lights = (
<>
<ambientLight intensity={0.5} />
<directionalLight />
</>
);
// 3. Use LOD (Level of Detail)
import { LOD } from 'three';
// 4. Lazy load models
import { lazy } from 'react';
const Model = lazy(() => import('./Model'));
Use a conservative initial DPR and measure actual rendering performance rather than inferring device capacity from the user-agent string:
import { Canvas } from '@react-three/fiber';
export default function Scene() {
return (
<Canvas dpr={[1, 1.5]} fallback={<p>3D preview unavailable.</p>}>
<mesh>
<boxGeometry />
<meshNormalMaterial />
</mesh>
</Canvas>
);
}
Canvas supports a DOM fallback when GL cannot initialize. Also wrap the scene
in an application error boundary for context crashes and asset errors; Suspense
handles loading, not failures. Keep product descriptions, variant controls and
purchase actions in accessible DOM so the task works without the canvas. Test
keyboard operation, zoom, reduced motion and an unavailable WebGL context.
See the Canvas API and fallback guidance.
Severity: HIGH
Message: No loading indicator for 3D content.
Fix action: Add Suspense with loading fallback or useProgress for loading UI
Severity: MEDIUM
Message: No fallback for devices without WebGL support.
Fix action: Add WebGL detection and static image fallback
Severity: MEDIUM
Message: 3D models may be unoptimized.
Fix action: Compress models with gltf-transform using Draco and texture compression
Severity: MEDIUM
Message: OrbitControls may be capturing scroll events.
Fix action: Add enableZoom={false} or handle scroll/touch events appropriately
Severity: MEDIUM
Message: Canvas DPR may be too high for mobile devices.
Fix action: Limit DPR to 1 on mobile devices for better performance
These are capability labels; check actual IDs and read their scope in the current catalog before recommending them. They do not authorize spawning agents.
Skills: 3d-web-experience, frontend-developer, frontend-design
Workflow:
1. Prepare 3D product model
2. Set up React Three Fiber scene
3. Add interactivity (colors, variants)
4. Integrate with product page
5. Optimize for mobile
6. Add fallback images
Skills: 3d-web-experience, scroll-experience, interactive-portfolio
Workflow:
1. Design 3D scene concept
2. Build scene in Spline or R3F
3. Add scroll-driven animations
4. Integrate with portfolio sections
5. Ensure mobile fallback
6. Optimize performance
Works well with: scroll-experience, interactive-portfolio, frontend-developer, frontend-design
Sponsors