---
name: r3f-geometry
description: BufferGeometry creation, built-in geometries, custom geometry with buffer attributes, instanced meshes for rendering thousands of objects, and geometry manipulation. Use when creating custom shapes, optimizing with instancing, or working with vertex data directly.
---
# R3F Geometry
Geometry defines the shape of 3D objects via vertices, faces, normals, and UVs stored in buffer attributes.
## Quick Start
```tsx
// Built-in geometry
// Custom geometry
```
## Built-in Geometries
All geometries accept `args` array matching constructor parameters:
```tsx
// Box: [width, height, depth, widthSegments?, heightSegments?, depthSegments?]
// Sphere: [radius, widthSegments, heightSegments, phiStart?, phiLength?, thetaStart?, thetaLength?]
// Plane: [width, height, widthSegments?, heightSegments?]
// Cylinder: [radiusTop, radiusBottom, height, radialSegments?, heightSegments?, openEnded?]
// Cone: [radius, height, radialSegments?, heightSegments?, openEnded?]
// Torus: [radius, tube, radialSegments, tubularSegments, arc?]
// TorusKnot: [radius, tube, tubularSegments, radialSegments, p?, q?]
// Ring: [innerRadius, outerRadius, thetaSegments?, phiSegments?]
// Circle: [radius, segments?, thetaStart?, thetaLength?]
// Dodecahedron/Icosahedron/Octahedron/Tetrahedron: [radius, detail?]
```
## Buffer Attributes
Geometry data lives in typed arrays attached as attributes:
| Attribute | ItemSize | Purpose |
|-----------|----------|---------|
| `position` | 3 | Vertex positions (x, y, z) |
| `normal` | 3 | Surface normals for lighting |
| `uv` | 2 | Texture coordinates (u, v) |
| `color` | 3 | Per-vertex colors (r, g, b) |
| `index` | 1 | Triangle indices (optional) |
### Custom Geometry from Scratch
```tsx
import { useMemo } from 'react';
import * as THREE from 'three';
function Triangle() {
const geometry = useMemo(() => {
const geo = new THREE.BufferGeometry();
// 3 vertices × 3 components (x, y, z)
const positions = new Float32Array([
-1, -1, 0, // vertex 0
1, -1, 0, // vertex 1
0, 1, 0 // vertex 2
]);
// 3 vertices × 3 components (nx, ny, nz)
const normals = new Float32Array([
0, 0, 1,
0, 0, 1,
0, 0, 1
]);
// 3 vertices × 2 components (u, v)
const uvs = new Float32Array([
0, 0,
1, 0,
0.5, 1
]);
geo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geo.setAttribute('normal', new THREE.BufferAttribute(normals, 3));
geo.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));
return geo;
}, []);
return (
);
}
```
### Declarative Buffer Attributes
```tsx
function Triangle() {
const positions = useMemo(() =>
new Float32Array([-1, -1, 0, 1, -1, 0, 0, 1, 0]),
[]);
return (
);
}
```
### Indexed Geometry
Use indices to share vertices between triangles:
```tsx
function Quad() {
const geometry = useMemo(() => {
const geo = new THREE.BufferGeometry();
// 4 unique vertices
const positions = 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
]);
// 2 triangles, 6 indices
const indices = new Uint16Array([
0, 1, 2, // first triangle
0, 2, 3 // second triangle
]);
geo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geo.setIndex(new THREE.BufferAttribute(indices, 1));
geo.computeVertexNormals();
return geo;
}, []);
return (
);
}
```
## Dynamic Geometry Updates
```tsx
import { useRef } from 'react';
import { useFrame } from '@react-three/fiber';
import * as THREE from 'three';
function WavingPlane() {
const geometryRef = useRef(null!);
useFrame(({ clock }) => {
const positions = geometryRef.current.attributes.position;
const time = clock.elapsedTime;
for (let i = 0; i < positions.count; i++) {
const x = positions.getX(i);
const y = positions.getY(i);
const z = Math.sin(x * 2 + time) * Math.cos(y * 2 + time) * 0.5;
positions.setZ(i, z);
}
positions.needsUpdate = true; // Critical!
geometryRef.current.computeVertexNormals();
});
return (
);
}
```
## Instanced Mesh
Render thousands of identical meshes with different transforms in a single draw call:
```tsx
import { useRef, useMemo } from 'react';
import { useFrame } from '@react-three/fiber';
import * as THREE from 'three';
function Particles({ count = 1000 }) {
const meshRef = useRef(null!);
// Pre-allocate transformation objects
const dummy = useMemo(() => new THREE.Object3D(), []);
// Initialize instance matrices
useEffect(() => {
for (let i = 0; i < count; i++) {
dummy.position.set(
(Math.random() - 0.5) * 10,
(Math.random() - 0.5) * 10,
(Math.random() - 0.5) * 10
);
dummy.rotation.set(
Math.random() * Math.PI,
Math.random() * Math.PI,
0
);
dummy.scale.setScalar(0.1 + Math.random() * 0.2);
dummy.updateMatrix();
meshRef.current.setMatrixAt(i, dummy.matrix);
}
meshRef.current.instanceMatrix.needsUpdate = true;
}, [count, dummy]);
// Animate instances
useFrame(({ clock }) => {
for (let i = 0; i < count; i++) {
meshRef.current.getMatrixAt(i, dummy.matrix);
dummy.matrix.decompose(dummy.position, dummy.quaternion, dummy.scale);
dummy.rotation.x += 0.01;
dummy.rotation.y += 0.01;
dummy.updateMatrix();
meshRef.current.setMatrixAt(i, dummy.matrix);
}
meshRef.current.instanceMatrix.needsUpdate = true;
});
return (
);
}
```
### Instance Colors
```tsx
function ColoredInstances({ count = 1000 }) {
const meshRef = useRef(null!);
useEffect(() => {
const color = new THREE.Color();
for (let i = 0; i < count; i++) {
color.setHSL(i / count, 1, 0.5);
meshRef.current.setColorAt(i, color);
}
meshRef.current.instanceColor!.needsUpdate = true;
}, [count]);
return (
);
}
```
### Instance Attributes (Custom Data)
```tsx
function CustomInstanceData({ count = 1000 }) {
const meshRef = useRef(null!);
// Custom per-instance data
const speeds = useMemo(() => {
const arr = new Float32Array(count);
for (let i = 0; i < count; i++) {
arr[i] = 0.5 + Math.random();
}
return arr;
}, [count]);
useEffect(() => {
// Attach as instanced buffer attribute
meshRef.current.geometry.setAttribute(
'aSpeed',
new THREE.InstancedBufferAttribute(speeds, 1)
);
}, [speeds]);
return (
);
}
```
## Geometry Utilities
### Compute Normals
```tsx
const geometry = useMemo(() => {
const geo = new THREE.BufferGeometry();
// ... set positions
geo.computeVertexNormals(); // Auto-calculate smooth normals
return geo;
}, []);
```
### Compute Bounding Box/Sphere
```tsx
useEffect(() => {
geometry.computeBoundingBox();
geometry.computeBoundingSphere();
console.log(geometry.boundingBox); // THREE.Box3
console.log(geometry.boundingSphere); // THREE.Sphere
}, [geometry]);
```
### Center Geometry
```tsx
const geometry = useMemo(() => {
const geo = new THREE.BoxGeometry(2, 3, 1);
geo.center(); // Move to origin
return geo;
}, []);
```
### Merge Geometries
```tsx
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils';
const merged = useMemo(() => {
const box = new THREE.BoxGeometry(1, 1, 1);
const sphere = new THREE.SphereGeometry(0.5, 16, 16);
sphere.translate(0, 1, 0);
return mergeGeometries([box, sphere]);
}, []);
```
## Performance Tips
| Technique | When to Use | Impact |
|-----------|-------------|--------|
| Instancing | 100+ identical meshes | Massive |
| Indexed geometry | Shared vertices | Moderate |
| Lower segments | Non-hero geometry | Moderate |
| Merge geometries | Static scene | Moderate |
| Dispose unused | Dynamic loading | Memory |
### Disposal
```tsx
useEffect(() => {
return () => {
geometry.dispose(); // Clean up GPU memory
};
}, [geometry]);
```
## File Structure
```
r3f-geometry/
├── SKILL.md
├── references/
│ ├── buffer-attributes.md # Deep-dive on attribute types
│ ├── instancing-patterns.md # Advanced instancing
│ └── procedural-shapes.md # Algorithmic geometry
└── scripts/
├── procedural/
│ ├── grid.ts # Grid mesh generator
│ ├── terrain.ts # Heightmap terrain
│ └── tube.ts # Custom tube geometry
└── utils/
├── geometry-utils.ts # Merge, center, clone
└── instancing.ts # Instance helpers
```
## Reference
- `references/buffer-attributes.md` — All attribute types and usage
- `references/instancing-patterns.md` — Advanced instancing techniques
- `references/procedural-shapes.md` — Generating geometry algorithmically