From enzed-r3f-skills
Creates 3D geometries in React Three Fiber: built-in shapes (box, sphere, cylinder, torus), BufferGeometry, path-based shapes, polyhedrons, instancing with Drei. Use for custom meshes, point clouds, lines, optimized rendering.
npx claudepluginhub joshuarweaver/cascade-code-languages-misc-1 --plugin enzed-r3f-skillsThis skill uses the workspace's default tool permissions.
```tsx
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
import { Canvas } from '@react-three/fiber'
function Scene() {
return (
<Canvas>
<ambientLight />
<mesh position={[0, 0, 0]}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="hotpink" />
</mesh>
</Canvas>
)
}
All Three.js geometries are available as JSX elements. The args prop passes constructor arguments.
// BoxGeometry(width, height, depth, widthSegments, heightSegments, depthSegments)
<boxGeometry args={[1, 1, 1]} />
<boxGeometry args={[2, 1, 0.5, 2, 2, 2]} />
// SphereGeometry(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength)
<sphereGeometry args={[1, 32, 32]} />
<sphereGeometry args={[1, 64, 64]} /> // High quality
<sphereGeometry args={[1, 32, 32, 0, Math.PI]} /> // Hemisphere
// PlaneGeometry(width, height, widthSegments, heightSegments)
<planeGeometry args={[10, 10]} />
<planeGeometry args={[10, 10, 32, 32]} /> // Subdivided for displacement
// CircleGeometry(radius, segments, thetaStart, thetaLength)
<circleGeometry args={[1, 32]} />
<circleGeometry args={[1, 32, 0, Math.PI]} /> // Semicircle
// CylinderGeometry(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded)
<cylinderGeometry args={[1, 1, 2, 32]} />
<cylinderGeometry args={[0, 1, 2, 32]} /> // Cone
<cylinderGeometry args={[1, 1, 2, 6]} /> // Hexagonal prism
// ConeGeometry(radius, height, radialSegments, heightSegments, openEnded)
<coneGeometry args={[1, 2, 32]} />
// TorusGeometry(radius, tube, radialSegments, tubularSegments, arc)
<torusGeometry args={[1, 0.4, 16, 100]} />
// TorusKnotGeometry(radius, tube, tubularSegments, radialSegments, p, q)
<torusKnotGeometry args={[1, 0.4, 100, 16, 2, 3]} />
// RingGeometry(innerRadius, outerRadius, thetaSegments, phiSegments)
<ringGeometry args={[0.5, 1, 32]} />
// CapsuleGeometry(radius, length, capSegments, radialSegments)
<capsuleGeometry args={[0.5, 1, 4, 16]} />
// Polyhedrons
<dodecahedronGeometry args={[1, 0]} /> // radius, detail
<icosahedronGeometry args={[1, 0]} />
<octahedronGeometry args={[1, 0]} />
<tetrahedronGeometry args={[1, 0]} />
// Higher detail = more subdivisions
<icosahedronGeometry args={[1, 4]} /> // Approximates sphere
import * as THREE from 'three'
// LatheGeometry - revolve points around Y axis
function LatheShape() {
const points = [
new THREE.Vector2(0, 0),
new THREE.Vector2(0.5, 0),
new THREE.Vector2(0.5, 0.5),
new THREE.Vector2(0.3, 1),
new THREE.Vector2(0, 1),
]
return (
<mesh>
<latheGeometry args={[points, 32]} />
<meshStandardMaterial color="gold" side={THREE.DoubleSide} />
</mesh>
)
}
// TubeGeometry - extrude along a curve
function TubeShape() {
const curve = new THREE.CatmullRomCurve3([
new THREE.Vector3(-2, 0, 0),
new THREE.Vector3(-1, 1, 0),
new THREE.Vector3(1, -1, 0),
new THREE.Vector3(2, 0, 0),
])
return (
<mesh>
<tubeGeometry args={[curve, 64, 0.2, 8, false]} />
<meshStandardMaterial color="blue" />
</mesh>
)
}
// ExtrudeGeometry - extrude a 2D shape
function ExtrudedShape() {
const shape = new THREE.Shape()
shape.moveTo(0, 0)
shape.lineTo(1, 0)
shape.lineTo(1, 1)
shape.lineTo(0, 1)
shape.lineTo(0, 0)
const extrudeSettings = {
steps: 2,
depth: 0.5,
bevelEnabled: true,
bevelThickness: 0.1,
bevelSize: 0.1,
bevelSegments: 3,
}
return (
<mesh>
<extrudeGeometry args={[shape, extrudeSettings]} />
<meshStandardMaterial color="purple" />
</mesh>
)
}
@react-three/drei provides convenient shape components.
import {
Box, Sphere, Plane, Circle, Cylinder, Cone,
Torus, TorusKnot, Ring, Capsule, Dodecahedron,
Icosahedron, Octahedron, Tetrahedron, RoundedBox
} from '@react-three/drei'
function DreiShapes() {
return (
<>
{/* All shapes accept mesh props directly */}
<Box args={[1, 1, 1]} position={[-3, 0, 0]}>
<meshStandardMaterial color="red" />
</Box>
<Sphere args={[0.5, 32, 32]} position={[-1, 0, 0]}>
<meshStandardMaterial color="blue" />
</Sphere>
<Cylinder args={[0.5, 0.5, 1, 32]} position={[1, 0, 0]}>
<meshStandardMaterial color="green" />
</Cylinder>
{/* RoundedBox - box with rounded edges */}
<RoundedBox
args={[1, 1, 1]} // width, height, depth
radius={0.1} // border radius
smoothness={4} // smoothness of rounded edges
position={[3, 0, 0]}
>
<meshStandardMaterial color="orange" />
</RoundedBox>
</>
)
}
import { useMemo, useRef } from 'react'
import * as THREE from 'three'
function CustomTriangle() {
const geometry = useMemo(() => {
const geo = new THREE.BufferGeometry()
// Vertices (3 floats per vertex: x, y, z)
const vertices = new Float32Array([
-1, -1, 0, // vertex 0
1, -1, 0, // vertex 1
0, 1, 0, // vertex 2
])
// Normals (pointing toward camera)
const normals = new Float32Array([
0, 0, 1,
0, 0, 1,
0, 0, 1,
])
// UVs
const uvs = new Float32Array([
0, 0,
1, 0,
0.5, 1,
])
geo.setAttribute('position', new THREE.BufferAttribute(vertices, 3))
geo.setAttribute('normal', new THREE.BufferAttribute(normals, 3))
geo.setAttribute('uv', new THREE.BufferAttribute(uvs, 2))
return geo
}, [])
return (
<mesh geometry={geometry}>
<meshStandardMaterial color="cyan" side={THREE.DoubleSide} />
</mesh>
)
}
function CustomQuad() {
const geometry = useMemo(() => {
const geo = new THREE.BufferGeometry()
// 4 vertices for a quad
const vertices = new Float32Array([
-1, -1, 0, // 0: bottom-left
1, -1, 0, // 1: bottom-right
1, 1, 0, // 2: top-right
-1, 1, 0, // 3: top-left
])
// Indices to form 2 triangles
const indices = new Uint16Array([
0, 1, 2, // triangle 1
0, 2, 3, // triangle 2
])
const normals = new Float32Array([
0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
])
const uvs = new Float32Array([
0, 0, 1, 0, 1, 1, 0, 1,
])
geo.setAttribute('position', new THREE.BufferAttribute(vertices, 3))
geo.setAttribute('normal', new THREE.BufferAttribute(normals, 3))
geo.setAttribute('uv', new THREE.BufferAttribute(uvs, 2))
geo.setIndex(new THREE.BufferAttribute(indices, 1))
return geo
}, [])
return (
<mesh geometry={geometry}>
<meshStandardMaterial color="lime" side={THREE.DoubleSide} />
</mesh>
)
}
import { useRef } from 'react'
import { useFrame } from '@react-three/fiber'
function WavyPlane() {
const meshRef = useRef()
useFrame(({ clock }) => {
const positions = meshRef.current.geometry.attributes.position
const time = clock.elapsedTime
for (let i = 0; i < positions.count; i++) {
const x = positions.getX(i)
const y = positions.getY(i)
positions.setZ(i, Math.sin(x * 2 + time) * Math.cos(y * 2 + time) * 0.5)
}
positions.needsUpdate = true
meshRef.current.geometry.computeVertexNormals()
})
return (
<mesh ref={meshRef} rotation={[-Math.PI / 2, 0, 0]}>
<planeGeometry args={[10, 10, 32, 32]} />
<meshStandardMaterial color="royalblue" side={THREE.DoubleSide} />
</mesh>
)
}
Efficient rendering of many identical objects.
import { Instances, Instance } from '@react-three/drei'
import { useFrame } from '@react-three/fiber'
import { useRef } from 'react'
function InstancedBoxes() {
const count = 1000
return (
<Instances limit={count} range={count}>
<boxGeometry args={[0.5, 0.5, 0.5]} />
<meshStandardMaterial />
{Array.from({ length: count }, (_, i) => (
<AnimatedInstance key={i} index={i} />
))}
</Instances>
)
}
function AnimatedInstance({ index }) {
const ref = useRef()
// Random initial position
const position = useMemo(() => [
(Math.random() - 0.5) * 20,
(Math.random() - 0.5) * 20,
(Math.random() - 0.5) * 20,
], [])
const color = useMemo(() =>
['red', 'blue', 'green', 'yellow', 'purple'][index % 5],
[index])
useFrame(({ clock }) => {
const t = clock.elapsedTime
ref.current.rotation.x = t + index
ref.current.rotation.y = t * 0.5 + index
})
return (
<Instance
ref={ref}
position={position}
color={color}
scale={0.5 + Math.random() * 0.5}
/>
)
}
For static instances, merge geometry for best performance:
import { Merged } from '@react-three/drei'
import { useMemo } from 'react'
import * as THREE from 'three'
function MergedMeshes() {
// Create geometries to merge
const meshes = useMemo(() => ({
Sphere: new THREE.SphereGeometry(0.5, 32, 32),
Box: new THREE.BoxGeometry(1, 1, 1),
Cone: new THREE.ConeGeometry(0.5, 1, 32),
}), [])
return (
<Merged meshes={meshes}>
{({ Sphere, Box, Cone }) => (
<>
<Sphere position={[-2, 0, 0]} color="red" />
<Sphere position={[-2, 2, 0]} color="orange" />
<Box position={[0, 0, 0]} color="blue" />
<Box position={[0, 2, 0]} color="cyan" />
<Cone position={[2, 0, 0]} color="green" />
<Cone position={[2, 2, 0]} color="lime" />
</>
)}
</Merged>
)
}
import { Points, Point, PointMaterial } from '@react-three/drei'
function ParticleField() {
const count = 5000
return (
<Points limit={count}>
<PointMaterial
transparent
vertexColors
size={0.05}
sizeAttenuation
depthWrite={false}
/>
{Array.from({ length: count }, (_, i) => (
<Point
key={i}
position={[
(Math.random() - 0.5) * 10,
(Math.random() - 0.5) * 10,
(Math.random() - 0.5) * 10,
]}
color={`hsl(${Math.random() * 360}, 100%, 50%)`}
/>
))}
</Points>
)
}
import { useMemo, useRef } from 'react'
import { useFrame } from '@react-three/fiber'
import * as THREE from 'three'
function BufferParticles() {
const count = 10000
const pointsRef = useRef()
const { positions, colors } = useMemo(() => {
const positions = new Float32Array(count * 3)
const colors = new Float32Array(count * 3)
for (let i = 0; i < count; i++) {
positions[i * 3] = (Math.random() - 0.5) * 10
positions[i * 3 + 1] = (Math.random() - 0.5) * 10
positions[i * 3 + 2] = (Math.random() - 0.5) * 10
colors[i * 3] = Math.random()
colors[i * 3 + 1] = Math.random()
colors[i * 3 + 2] = Math.random()
}
return { positions, colors }
}, [])
useFrame(({ clock }) => {
pointsRef.current.rotation.y = clock.elapsedTime * 0.1
})
return (
<points ref={pointsRef}>
<bufferGeometry>
<bufferAttribute
attach="attributes-position"
count={count}
array={positions}
itemSize={3}
/>
<bufferAttribute
attach="attributes-color"
count={count}
array={colors}
itemSize={3}
/>
</bufferGeometry>
<pointsMaterial size={0.05} vertexColors sizeAttenuation />
</points>
)
}
import { Line } from '@react-three/drei'
function BasicLine() {
const points = [
[0, 0, 0],
[1, 1, 0],
[2, 0, 0],
[3, 1, 0],
]
return (
<Line
points={points}
color="red"
lineWidth={2}
/>
)
}
import { CatmullRomLine, QuadraticBezierLine, CubicBezierLine } from '@react-three/drei'
function CurvedLines() {
return (
<>
{/* Smooth curve through points */}
<CatmullRomLine
points={[[0, 0, 0], [1, 1, 0], [2, 0, 0], [3, 1, 0]]}
color="blue"
lineWidth={2}
segments={64}
/>
{/* Quadratic bezier */}
<QuadraticBezierLine
start={[0, 0, 0]}
mid={[1, 2, 0]}
end={[2, 0, 0]}
color="green"
lineWidth={2}
/>
{/* Cubic bezier */}
<CubicBezierLine
start={[0, 0, 0]}
midA={[0.5, 2, 0]}
midB={[1.5, -1, 0]}
end={[2, 0, 0]}
color="purple"
lineWidth={2}
/>
</>
)
}
<Line
points={[[0, 0, 0], [5, 0, 0]]}
color="white"
lineWidth={2}
dashed
dashScale={50}
dashSize={0.5}
dashOffset={0}
gapSize={0.2}
/>
import { Edges } from '@react-three/drei'
function BoxWithEdges() {
return (
<mesh>
<boxGeometry />
<meshStandardMaterial color="orange" />
<Edges
scale={1.1}
threshold={15} // Display edges with angle > 15 degrees
color="black"
/>
</mesh>
)
}
// Wireframe material
function WireframeBox() {
return (
<mesh>
<boxGeometry />
<meshBasicMaterial color="cyan" wireframe />
</mesh>
)
}
import { Text3D, Center } from '@react-three/drei'
function Text3DExample() {
return (
<Center>
<Text3D
font="/fonts/helvetiker_regular.typeface.json"
size={1}
height={0.2}
curveSegments={12}
bevelEnabled
bevelThickness={0.02}
bevelSize={0.02}
bevelOffset={0}
bevelSegments={5}
>
Hello R3F
<meshStandardMaterial color="gold" />
</Text3D>
</Center>
)
}
import { Center } from '@react-three/drei'
function CenteredModel() {
return (
<Center>
<mesh>
<boxGeometry args={[2, 1, 0.5]} />
<meshStandardMaterial />
</mesh>
</Center>
)
}
// With options
<Center top left> {/* Align to top-left */}
<Model />
</Center>
// Get bounding info
<Center onCentered={({ width, height, depth, boundingBox }) => {
console.log('Dimensions:', width, height, depth)
}}>
<Model />
</Center>
import { useBounds, Bounds } from '@react-three/drei'
function FitToView() {
return (
<Bounds fit clip observe margin={1.2}>
<SelectToZoom />
</Bounds>
)
}
function SelectToZoom() {
const bounds = useBounds()
return (
<mesh
onClick={(e) => {
e.stopPropagation()
bounds.refresh(e.object).fit()
}}
>
<boxGeometry />
<meshStandardMaterial />
</mesh>
)
}
<Merged> for static scenes// Good segment counts
<sphereGeometry args={[1, 32, 32]} /> // Standard quality
<sphereGeometry args={[1, 64, 64]} /> // High quality
<sphereGeometry args={[1, 16, 16]} /> // Performance mode
// Reuse geometry
const sharedGeometry = useMemo(() => new THREE.BoxGeometry(), [])
<mesh geometry={sharedGeometry} position={[0, 0, 0]} />
<mesh geometry={sharedGeometry} position={[2, 0, 0]} />
<mesh geometry={sharedGeometry} position={[4, 0, 0]} />
r3f-fundamentals - JSX elements and refsr3f-materials - Materials for meshesr3f-shaders - Custom vertex manipulation