
Threejs Syntax Geometries
- 19 installs
- 11 repo stars
- Updated July 8, 2026
- openaec-foundation/three.js-claude-skill-package
Helps with ai & agent building tasks.
About
threejs-syntax-geometries is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- threejs-syntax-geometries
- AI & Agent Building
- AI-coding skill
Threejs Syntax Geometries by the numbers
- 19 all-time installs (skills.sh)
- +2 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #10,587 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/openaec-foundation/three.js-claude-skill-package --skill threejs-syntax-geometriesAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 19 |
|---|---|
| repo stars | ★ 11 |
| Last updated | July 8, 2026 |
| Repository | openaec-foundation/three.js-claude-skill-package ↗ |
What it does
Helps with ai & agent building tasks.
Files
threejs-syntax-geometries
Quick Reference
BufferGeometry: Core Properties
| Property | Type | Default | Description |
|---|---|---|---|
attributes | Object | {} | Hash map of named BufferAttribute instances |
index | `BufferAttribute \ | null` | null |
morphAttributes | Object | {} | Morph target attribute arrays |
morphTargetsRelative | Boolean | false | If true, morph data = relative offsets |
groups | Array | [] | { start, count, materialIndex } for multi-material |
drawRange | Object | { start: 0, count: Infinity } | Portion of geometry to render |
boundingBox | `Box3 \ | null` | null |
boundingSphere | `Sphere \ | null` | null |
BufferGeometry: Key Methods
| Method | Returns | Description |
|---|---|---|
setAttribute(name, attr) | this | Add or replace a named attribute |
getAttribute(name) | BufferAttribute | Retrieve attribute by name |
deleteAttribute(name) | this | Remove a named attribute |
hasAttribute(name) | boolean | Check if attribute exists |
setIndex(attr) | — | Set the index buffer |
addGroup(start, count, materialIndex?) | — | Define a render group for multi-material |
clearGroups() | — | Remove all groups |
setDrawRange(start, count) | — | Limit rendered range |
computeVertexNormals() | — | Compute smooth normals from face topology |
computeTangents() | — | Compute tangent vectors (requires position, normal, uv, index) |
computeBoundingBox() | — | Compute and cache AABB |
computeBoundingSphere() | — | Compute and cache bounding sphere |
toNonIndexed() | new geometry | Create non-indexed copy with duplicated vertices |
translate(x, y, z) | this | Translate vertex positions in-place |
rotateX/Y/Z(radians) | this | Rotate vertex positions in-place |
scale(x, y, z) | this | Scale vertex positions in-place |
center() | this | Center geometry at the origin |
dispose() | — | Free GPU resources |
BufferAttribute: Constructor and Properties
import * as THREE from 'three';
new THREE.BufferAttribute(array: TypedArray, itemSize: number, normalized?: boolean)| Property | Type | Default | Description |
|---|---|---|---|
array | TypedArray | — | The underlying data |
itemSize | number | — | Values per vertex (1=scalar, 2=UV, 3=position, 4=RGBA) |
count | number | computed | array.length / itemSize |
needsUpdate | boolean | false | Set true to upload changes to GPU |
usage | number | StaticDrawUsage | GPU usage hint |
Typed Convenience Classes
| Class | Underlying Type | Use Case |
|---|---|---|
Float32BufferAttribute | Float32Array | Positions, normals, UVs (most common) |
Float16BufferAttribute | Float16Array | Memory-optimized attributes |
Uint16BufferAttribute | Uint16Array | Index buffers (<65536 vertices) |
Uint32BufferAttribute | Uint32Array | Index buffers (>65536 vertices) |
Uint8BufferAttribute | Uint8Array | Byte colors (with normalized=true) |
Int8/Int16/Int32BufferAttribute | Respective arrays | Signed integer data |
Usage Hints
| Constant | When to Use |
|---|---|
THREE.StaticDrawUsage | Data uploaded once, read many times (default) |
THREE.DynamicDrawUsage | Data updated frequently (particles, animated verts) |
THREE.StreamDrawUsage | Data updated every frame and read once |
Critical Warnings
NEVER forget attribute.needsUpdate = true after modifying BufferAttribute data via setX/setXY/setXYZ or direct array writes. Without this flag, changes will NOT reach the GPU.
NEVER assume boundingBox or boundingSphere are populated automatically. They are null until you explicitly call computeBoundingBox() / computeBoundingSphere().
ALWAYS call computeVertexNormals() after building custom geometry if you need lighting. Without normals, MeshStandardMaterial and other lit materials render black.
ALWAYS call geometry.dispose() when removing geometry from the scene permanently. Failing to dispose leaks GPU memory. The garbage collector does NOT free GPU-side buffers.
NEVER call translate(), rotateX(), scale(), or other in-place transform methods in an animation loop. These modify the actual vertex data permanently. Use mesh.position, mesh.rotation, and mesh.scale for runtime transforms.
ALWAYS set usage to DynamicDrawUsage BEFORE the first render if you plan to update attribute data every frame. Changing usage after initial upload has no effect on some GPU drivers.
NEVER forget instanceMatrix.needsUpdate = true after calling setMatrixAt() on InstancedMesh. Without this flag, all instances render at the origin.
---
Indexed vs Non-Indexed Geometry
| Aspect | Indexed | Non-Indexed |
|---|---|---|
| Memory | Lower (shared vertices) | Higher (duplicated vertices) |
| GPU cache | Better (vertex reuse) | No reuse |
| Flat shading | Requires duplicated normals | Natural per-face normals |
| Wireframe | Clean edges | May show diagonal lines |
ALWAYS use indexed geometry for smooth-shaded meshes with shared vertices. Use geometry.toNonIndexed() ONLY when you need per-face attributes (flat shading with unique normals).
---
All 21 Built-in Geometries
Primitives
| Geometry | Key Parameters |
|---|---|
BoxGeometry | (width=1, height=1, depth=1, wSeg=1, hSeg=1, dSeg=1) |
SphereGeometry | (radius=1, wSeg=32, hSeg=16, phiStart, phiLen, thetaStart, thetaLen) |
PlaneGeometry | (width=1, height=1, wSeg=1, hSeg=1) |
CylinderGeometry | (radTop=1, radBot=1, height=1, radSeg=32, hSeg=1, open, thetaStart, thetaLen) |
ConeGeometry | (radius=1, height=1, radSeg=32, hSeg=1, open, thetaStart, thetaLen) |
CapsuleGeometry | (radius=1, length=1, capSeg=4, radSeg=8) |
TorusGeometry | (radius=1, tube=0.4, radSeg=12, tubSeg=48, arc=2PI) |
TorusKnotGeometry | (radius=1, tube=0.4, tubSeg=64, radSeg=8, p=2, q=3) |
CircleGeometry | (radius=1, seg=32, thetaStart=0, thetaLen=2PI) |
RingGeometry | (inner=0.5, outer=1, thetaSeg=32, phiSeg=1, thetaStart, thetaLen) |
Polyhedra
All accept (radius=1, detail=0). Detail controls subdivision level.
| Geometry | Base Faces |
|---|---|
TetrahedronGeometry | 4 |
OctahedronGeometry | 8 |
DodecahedronGeometry | 12 |
IcosahedronGeometry | 20 |
PolyhedronGeometry | custom (vertices, indices, radius, detail) |
Path-Based
| Geometry | Description |
|---|---|
ExtrudeGeometry(shapes, options) | Extrude 2D Shape into 3D |
ShapeGeometry(shapes, curveSegments=12) | Flat 2D geometry from Shape |
LatheGeometry(points, segments=12, phiStart, phiLength) | Revolve 2D profile around Y axis |
TubeGeometry(path, tubSeg=64, radius=1, radSeg=8, closed) | Tube along a Curve3 |
Utility
| Geometry | Description |
|---|---|
EdgesGeometry(geometry, thresholdAngle=1) | Edges where angle > threshold (clean outlines) |
WireframeGeometry(geometry) | ALL triangle edges (debug visualization) |
---
ExtrudeGeometry Options
| Option | Type | Default | Description |
|---|---|---|---|
depth | number | 1 | Extrusion depth |
steps | number | 1 | Subdivision steps along depth |
bevelEnabled | boolean | true | Enable beveled edges |
bevelThickness | number | 0.2 | Bevel depth into shape |
bevelSize | number | bevelThickness - 0.1 | Bevel distance from outline |
bevelOffset | number | 0 | Bevel offset from edge |
bevelSegments | number | 3 | Bevel resolution |
curveSegments | number | 12 | Points on curves |
extrudePath | Curve | undefined | 3D path to extrude along |
UVGenerator | Object | WorldUVGenerator | Custom UV generation |
---
Shape Class: 2D Path Drawing
import * as THREE from 'three';
const shape = new THREE.Shape();
shape.moveTo(x, y); // Start new subpath
shape.lineTo(x, y); // Straight line
shape.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); // Cubic Bezier
shape.quadraticCurveTo(cpx, cpy, x, y); // Quadratic Bezier
shape.splineThru(points); // Smooth spline through Vector2[]
shape.arc(aX, aY, radius, startAngle, endAngle, cw); // Relative arc
shape.absarc(aX, aY, radius, startAngle, endAngle, cw); // Absolute arc
// Holes
const holePath = new THREE.Path();
holePath.absarc(0, 0, 0.5, 0, Math.PI * 2);
shape.holes.push(holePath);---
Morph Targets
import * as THREE from 'three';
// Define morph target positions
const morphPositions = new Float32Array([/* ... */]);
geometry.morphAttributes.position = [
new THREE.BufferAttribute(morphPositions, 3)
];
// Control on the mesh
mesh.morphTargetInfluences[0] = 0.5; // 50% blend toward morph target 0
// Named morph targets (from glTF)
mesh.morphTargetDictionary; // { "smile": 0, "frown": 1 }Set geometry.morphTargetsRelative = true when morph data represents offsets rather than absolute positions.
---
InstancedMesh
import * as THREE from 'three';
const mesh = new THREE.InstancedMesh(geometry, material, count);| Property | Type | Description |
|---|---|---|
instanceMatrix | InstancedBufferAttribute | 4x4 matrices (16 floats per instance) |
instanceColor | `InstancedBufferAttribute \ | null` |
count | number | Instance count (read-only after construction) |
| Method | Description |
|---|---|
setMatrixAt(index, matrix4) | Set transform for instance |
getMatrixAt(index, matrix4) | Read transform into target |
setColorAt(index, color) | Set per-instance color |
getColorAt(index, color) | Read per-instance color |
Performance Guidelines
| Instance Count | Recommendation |
|---|---|
| < 10 | Use individual Mesh objects |
| 10 - 100 | Either approach; profile your case |
| 100 - 10,000 | ALWAYS use InstancedMesh |
| > 10,000 | Use InstancedMesh with spatial subdivision or BatchedMesh (r160+) |
Custom Per-Instance Attributes
import * as THREE from 'three';
const phases = new Float32Array(count);
for (let i = 0; i < count; i++) phases[i] = Math.random();
mesh.geometry.setAttribute('aPhase', new THREE.InstancedBufferAttribute(phases, 1));
// Access in vertex shader: attribute float aPhase;---
Multi-Material with Groups
import * as THREE from 'three';
geometry.addGroup(0, 6, 0); // First 6 indices -> material[0]
geometry.addGroup(6, 6, 1); // Next 6 indices -> material[1]
const mesh = new THREE.Mesh(geometry, [materialA, materialB]);---
InterleavedBuffer
For optimal GPU cache performance on large meshes (10,000+ vertices):
import * as THREE from 'three';
const stride = 8; // 3 (position) + 3 (normal) + 2 (uv)
const buffer = new THREE.InterleavedBuffer(new Float32Array(vertexCount * stride), stride);
geometry.setAttribute('position', new THREE.InterleavedBufferAttribute(buffer, 3, 0));
geometry.setAttribute('normal', new THREE.InterleavedBufferAttribute(buffer, 3, 3));
geometry.setAttribute('uv', new THREE.InterleavedBufferAttribute(buffer, 2, 6));ALWAYS use separate buffers when individual attributes are updated independently. Use interleaved buffers ONLY when all attributes are static or updated together.
---
Disposal Rules
- ALWAYS call
geometry.dispose()when removing geometry from the scene permanently - Shared geometries (used by multiple meshes): dispose ONLY after ALL meshes are removed
- Built-in geometries: dispose when the mesh using them is removed
- The JavaScript garbage collector does NOT free GPU-side vertex buffers
---
Reference Links
- references/methods.md — Complete API signatures for BufferGeometry, BufferAttribute, InstancedMesh
- references/examples.md — Working code examples for custom geometry, instancing, extrusion
- references/anti-patterns.md — What NOT to do with geometry in Three.js
Official Sources
- https://threejs.org/docs/#api/en/core/BufferGeometry
- https://threejs.org/docs/#api/en/core/BufferAttribute
- https://threejs.org/docs/#api/en/objects/InstancedMesh
- https://threejs.org/docs/#api/en/geometries/ExtrudeGeometry
- https://threejs.org/docs/#api/en/extras/core/Shape
Anti-Patterns (Three.js Geometries r160+)
1. Forgetting needsUpdate After Modifying BufferAttribute
// WRONG: GPU buffer will NOT update — changes are invisible
const positions = geometry.getAttribute('position');
positions.setXYZ(0, 5, 0, 0);
// Nothing happens on screen
// CORRECT: ALWAYS set needsUpdate = true after modifying attribute data
const positions = geometry.getAttribute('position');
positions.setXYZ(0, 5, 0, 0);
positions.needsUpdate = true;WHY: Three.js does NOT automatically detect changes to typed arrays. The needsUpdate flag tells the renderer to re-upload the data to the GPU on the next frame.
---
2. Forgetting instanceMatrix.needsUpdate on InstancedMesh
// WRONG: All instances render at the origin (identity matrix)
const mesh = new THREE.InstancedMesh(geometry, material, 100);
const dummy = new THREE.Object3D();
for (let i = 0; i < 100; i++) {
dummy.position.set(i * 2, 0, 0);
dummy.updateMatrix();
mesh.setMatrixAt(i, dummy.matrix);
}
// Forgot: mesh.instanceMatrix.needsUpdate = true;
// CORRECT: ALWAYS set needsUpdate after all setMatrixAt calls
mesh.instanceMatrix.needsUpdate = true;WHY: setMatrixAt writes to a CPU-side buffer. Without needsUpdate = true, the GPU never receives the updated matrices.
---
3. Using In-Place Transform Methods in Animation Loops
// WRONG: geometry.translate modifies vertex data permanently — each frame accumulates
function animate() {
geometry.translate(0, 0.01, 0); // Vertices drift further every frame!
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
// CORRECT: Use the mesh transform for runtime animation
function animate() {
mesh.position.y += 0.01;
renderer.render(scene, camera);
requestAnimationFrame(animate);
}WHY: geometry.translate(), geometry.rotateX(), geometry.scale(), and similar methods permanently alter the vertex position data in the BufferAttribute. They are intended for one-time geometry alignment, NEVER for animation.
---
4. Not Disposing Geometry When Removing from Scene
// WRONG: GPU memory leak — vertex buffers stay allocated
scene.remove(mesh);
mesh = null; // JavaScript GC frees the JS object, but GPU buffers remain
// CORRECT: ALWAYS dispose geometry (and material) when removing permanently
scene.remove(mesh);
mesh.geometry.dispose();
mesh.material.dispose();
mesh = null;WHY: The JavaScript garbage collector only frees JavaScript objects. GPU-side vertex buffers, index buffers, and shader programs are NOT freed automatically. You MUST call dispose() explicitly.
---
5. Not Computing Normals on Custom Geometry
// WRONG: MeshStandardMaterial renders completely black
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
geometry.setIndex(indices);
const mesh = new THREE.Mesh(geometry, new THREE.MeshStandardMaterial({ color: 0xff0000 }));
// CORRECT: ALWAYS compute normals for lit materials
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
geometry.setIndex(indices);
geometry.computeVertexNormals(); // Required for lighting calculations
const mesh = new THREE.Mesh(geometry, new THREE.MeshStandardMaterial({ color: 0xff0000 }));WHY: Lit materials (MeshStandardMaterial, MeshPhongMaterial, etc.) require normal vectors to calculate light interaction. Without normals, every surface normal defaults to zero, and all lighting calculations produce black.
---
6. Using StaticDrawUsage for Frequently Updated Attributes
// WRONG: Default usage hint tells GPU driver the data is static
const positions = new THREE.Float32BufferAttribute(data, 3);
// positions.usage = THREE.StaticDrawUsage (default)
// Then updating every frame — GPU driver may use slow upload path
// CORRECT: Set DynamicDrawUsage BEFORE the first render
const positions = new THREE.Float32BufferAttribute(data, 3);
positions.usage = THREE.DynamicDrawUsage; // MUST be set before first render
geometry.setAttribute('position', positions);WHY: GPU drivers use the usage hint to decide where to allocate the buffer. StaticDrawUsage places the buffer in GPU-optimized memory that is slow to update. DynamicDrawUsage uses a buffer that is fast to update from the CPU. Changing usage after the first render has no effect on some drivers.
---
7. Trying to Resize InstancedMesh After Creation
// WRONG: count is read-only after construction
const mesh = new THREE.InstancedMesh(geometry, material, 100);
mesh.count = 200; // This does NOT allocate more buffer space!
// CORRECT: Create with the maximum count, then control visible count
const maxCount = 1000;
const mesh = new THREE.InstancedMesh(geometry, material, maxCount);
mesh.count = 100; // Only render first 100 instances (read property is writable for this purpose)
// Later, increase up to maxCount:
mesh.count = 500; // Works because buffer was allocated for 1000WHY: The instanceMatrix buffer is allocated once in the constructor based on count. You CANNOT grow it. Pre-allocate the maximum expected count and set mesh.count to control how many are rendered.
---
8. Assuming boundingBox/boundingSphere Are Auto-Computed
// WRONG: boundingBox is null — causes crash or incorrect behavior
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(data, 3));
console.log(geometry.boundingBox.min); // TypeError: Cannot read property 'min' of null
// CORRECT: ALWAYS call compute methods explicitly
geometry.computeBoundingBox();
geometry.computeBoundingSphere();
console.log(geometry.boundingBox.min); // WorksWHY: Bounding volumes are null by default and are NOT automatically computed. The renderer calls computeBoundingSphere() internally for frustum culling, but boundingBox is NEVER computed automatically. If your code needs bounding data, ALWAYS call the compute methods yourself.
---
9. Using Indexed Geometry for Flat Shading Without Duplicating Normals
// WRONG: Shared vertices = shared normals = smooth shading (not flat)
const geometry = new THREE.BoxGeometry(1, 1, 1);
// BoxGeometry is already non-indexed with separate normals per face.
// But custom indexed geometry shares vertices, forcing smooth normals.
// CORRECT option A: Convert to non-indexed for true flat shading
const flatGeometry = indexedGeometry.toNonIndexed();
flatGeometry.computeVertexNormals(); // Now each face gets independent normals
// CORRECT option B: Use material flatShading property
const material = new THREE.MeshStandardMaterial({
color: 0x888888,
flatShading: true // Forces flat shading in the shader
});WHY: Indexed geometry shares vertices between triangles, which means normals are interpolated (smooth). For true flat shading with indexed geometry, either convert to non-indexed or use flatShading: true on the material.
---
10. Creating Thousands of Individual Meshes Instead of InstancedMesh
// WRONG: 10,000 draw calls — extremely slow
for (let i = 0; i < 10000; i++) {
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(Math.random() * 100, 0, Math.random() * 100);
scene.add(mesh);
}
// CORRECT: Single draw call with InstancedMesh
const instancedMesh = new THREE.InstancedMesh(geometry, material, 10000);
const dummy = new THREE.Object3D();
for (let i = 0; i < 10000; i++) {
dummy.position.set(Math.random() * 100, 0, Math.random() * 100);
dummy.updateMatrix();
instancedMesh.setMatrixAt(i, dummy.matrix);
}
instancedMesh.instanceMatrix.needsUpdate = true;
scene.add(instancedMesh);WHY: Each individual Mesh triggers a separate draw call. At 10,000+ meshes, the CPU overhead of issuing draw calls dominates frame time. InstancedMesh renders all instances in a single draw call using GPU instancing, which is orders of magnitude faster.
---
11. Forgetting to Call updateMatrix() Before setMatrixAt()
// WRONG: dummy.matrix is still the identity matrix
const dummy = new THREE.Object3D();
dummy.position.set(5, 0, 0);
mesh.setMatrixAt(0, dummy.matrix); // Writes identity matrix, NOT the translated position
// CORRECT: ALWAYS call updateMatrix() after changing position/rotation/scale
const dummy = new THREE.Object3D();
dummy.position.set(5, 0, 0);
dummy.updateMatrix(); // Composes position/rotation/scale into dummy.matrix
mesh.setMatrixAt(0, dummy.matrix);WHY: Object3D.matrix is NOT automatically updated when you change position, rotation, or scale. You MUST call updateMatrix() to compose these into the matrix before passing it to setMatrixAt().
Working Code Examples (Three.js Geometries r160+)
Example 1: Custom Quad Geometry with Position, Normal, and UV
import * as THREE from 'three';
// Create an empty BufferGeometry
const geometry = new THREE.BufferGeometry();
// Step 1: Define 4 vertex positions (3 floats each)
const positions = new Float32Array([
-1, -1, 0, // vertex 0 (bottom-left)
1, -1, 0, // vertex 1 (bottom-right)
1, 1, 0, // vertex 2 (top-right)
-1, 1, 0 // vertex 3 (top-left)
]);
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
// Step 2: Define indices for two triangles forming a quad
const indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
geometry.setIndex(new THREE.BufferAttribute(indices, 1));
// Step 3: Define UV coordinates (2 floats each)
const uvs = new Float32Array([
0, 0, // vertex 0
1, 0, // vertex 1
1, 1, // vertex 2
0, 1 // vertex 3
]);
geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));
// Step 4: Compute normals automatically from face topology
geometry.computeVertexNormals();
// Step 5: Compute bounding sphere for frustum culling
geometry.computeBoundingSphere();
// Step 6: Create mesh
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);---
Example 2: InstancedMesh — 1000 Random Cubes
import * as THREE from 'three';
const geometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
const material = new THREE.MeshStandardMaterial({ color: 0xffffff });
const count = 1000;
const instancedMesh = new THREE.InstancedMesh(geometry, material, count);
// Use a dummy Object3D to compose transform matrices
const dummy = new THREE.Object3D();
const color = new THREE.Color();
for (let i = 0; i < count; i++) {
// Set position
dummy.position.set(
(Math.random() - 0.5) * 50,
(Math.random() - 0.5) * 50,
(Math.random() - 0.5) * 50
);
// Set rotation
dummy.rotation.set(
Math.random() * Math.PI,
Math.random() * Math.PI,
0
);
// Set scale
dummy.scale.setScalar(0.5 + Math.random() * 1.5);
// CRITICAL: updateMatrix() computes the local matrix from position/rotation/scale
dummy.updateMatrix();
instancedMesh.setMatrixAt(i, dummy.matrix);
// Set per-instance color
color.setHSL(Math.random(), 0.8, 0.5);
instancedMesh.setColorAt(i, color);
}
// CRITICAL: ALWAYS set needsUpdate after writing matrices/colors
instancedMesh.instanceMatrix.needsUpdate = true;
instancedMesh.instanceColor.needsUpdate = true;
scene.add(instancedMesh);---
Example 3: ExtrudeGeometry — L-Shaped Profile with Hole
import * as THREE from 'three';
// Define the L-shaped outline
const shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.lineTo(3, 0);
shape.lineTo(3, 1);
shape.lineTo(1, 1);
shape.lineTo(1, 3);
shape.lineTo(0, 3);
shape.lineTo(0, 0); // Close the shape
// Add a circular hole
const holePath = new THREE.Path();
holePath.absarc(0.5, 2, 0.3, 0, Math.PI * 2, false);
shape.holes.push(holePath);
// Extrude with bevel
const extrudeSettings = {
depth: 2,
bevelEnabled: true,
bevelThickness: 0.1,
bevelSize: 0.1,
bevelOffset: 0,
bevelSegments: 3,
curveSegments: 12,
steps: 1
};
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
const material = new THREE.MeshStandardMaterial({ color: 0x888888 });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);---
Example 4: Extrude Along a 3D Path (extrudePath)
import * as THREE from 'three';
// Define a circular cross-section shape
const circleShape = new THREE.Shape();
circleShape.absarc(0, 0, 0.3, 0, Math.PI * 2, false);
// Define a 3D curve to extrude along
const curve = new THREE.CatmullRomCurve3([
new THREE.Vector3(-5, 0, 0),
new THREE.Vector3(-2, 3, 2),
new THREE.Vector3(2, -3, -2),
new THREE.Vector3(5, 0, 0)
]);
const extrudeSettings = {
steps: 100,
bevelEnabled: false,
extrudePath: curve
};
const geometry = new THREE.ExtrudeGeometry(circleShape, extrudeSettings);
const material = new THREE.MeshStandardMaterial({ color: 0xff6600, side: THREE.DoubleSide });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);---
Example 5: Dynamic Geometry — Animated Vertex Positions
import * as THREE from 'three';
// Create a plane with enough segments to deform
const geometry = new THREE.PlaneGeometry(10, 10, 64, 64);
// CRITICAL: Set usage hint BEFORE first render for dynamic data
const positionAttr = geometry.getAttribute('position');
positionAttr.usage = THREE.DynamicDrawUsage;
const material = new THREE.MeshStandardMaterial({
color: 0x0088ff,
wireframe: true
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// Animation loop — wave effect
function animate(time) {
const positions = geometry.getAttribute('position');
const t = time * 0.001;
for (let i = 0; i < positions.count; i++) {
const x = positions.getX(i);
const y = positions.getY(i);
// Compute new Z based on wave function
const z = Math.sin(x * 0.5 + t) * Math.cos(y * 0.5 + t) * 1.5;
positions.setZ(i, z);
}
// CRITICAL: ALWAYS set needsUpdate after modifying attribute data
positions.needsUpdate = true;
// Recompute normals for correct lighting
geometry.computeVertexNormals();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);---
Example 6: Multi-Material with Groups
import * as THREE from 'three';
const geometry = new THREE.BoxGeometry(2, 2, 2);
// BoxGeometry already has groups defined (one per face pair).
// Clear them and define custom groups:
geometry.clearGroups();
// Each face of a box = 6 indices (2 triangles * 3 vertices)
// Box has 6 faces = 36 total indices
geometry.addGroup(0, 12, 0); // Front + back faces -> material 0
geometry.addGroup(12, 12, 1); // Top + bottom faces -> material 1
geometry.addGroup(24, 12, 2); // Left + right faces -> material 2
const materials = [
new THREE.MeshStandardMaterial({ color: 0xff0000 }), // Red
new THREE.MeshStandardMaterial({ color: 0x00ff00 }), // Green
new THREE.MeshStandardMaterial({ color: 0x0000ff }) // Blue
];
const mesh = new THREE.Mesh(geometry, materials);
scene.add(mesh);---
Example 7: EdgesGeometry for Architectural Outlines
import * as THREE from 'three';
// Create a box
const boxGeometry = new THREE.BoxGeometry(2, 3, 1);
const boxMaterial = new THREE.MeshStandardMaterial({ color: 0xcccccc });
const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial);
scene.add(boxMesh);
// Create clean edges (only edges where angle > 1 degree)
const edgesGeometry = new THREE.EdgesGeometry(boxGeometry, 1);
const edgesMaterial = new THREE.LineBasicMaterial({ color: 0x000000 });
const edgesMesh = new THREE.LineSegments(edgesGeometry, edgesMaterial);
scene.add(edgesMesh);API Signatures Reference (Three.js Geometries r160+)
BufferGeometry
import { BufferGeometry } from 'three';Constructor
new BufferGeometry()
// Creates an empty geometry. ALWAYS add attributes via setAttribute() before rendering.Properties
geometry.id: number // Auto-incremented unique identifier
geometry.uuid: string // Auto-generated UUID
geometry.name: string // Optional human-readable name (default: "")
geometry.type: string // Class type identifier (default: "BufferGeometry")
geometry.attributes: Object // Hash map of named BufferAttribute instances
geometry.index: BufferAttribute | null // Optional index buffer (default: null)
geometry.morphAttributes: Object // Hash map of morph target attribute arrays (default: {})
geometry.morphTargetsRelative: boolean // true = morph data is relative offsets (default: false)
geometry.groups: Array // Array of { start, count, materialIndex } (default: [])
geometry.drawRange: Object // { start: 0, count: Infinity }
geometry.boundingBox: Box3 | null // null until computeBoundingBox() is called
geometry.boundingSphere: Sphere | null // null until computeBoundingSphere() is called
geometry.userData: Object // Arbitrary user data storage (default: {})Attribute Management Methods
setAttribute(name: string, attribute: BufferAttribute): BufferGeometry
// Add or replace a named attribute. Returns this for chaining.
getAttribute(name: string): BufferAttribute | undefined
// Retrieve attribute by name.
deleteAttribute(name: string): BufferGeometry
// Remove a named attribute. Returns this for chaining.
hasAttribute(name: string): boolean
// Check if attribute exists.
setIndex(index: BufferAttribute | number[]): void
// Set the index buffer. Accepts BufferAttribute or plain array (auto-converted).Bounding Volume Methods
computeBoundingBox(): void
// Compute and cache the axis-aligned bounding box.
computeBoundingSphere(): void
// Compute and cache the bounding sphere.Normal and Tangent Methods
computeVertexNormals(): void
// Compute smooth vertex normals from face topology.
// ALWAYS call this after building custom geometry if lighting is needed.
computeTangents(): void
// Compute tangent vectors. REQUIRES position, normal, uv attributes AND an index buffer.
// Fails silently if any prerequisite is missing.
normalizeNormals(): void
// Normalize all normal vectors to unit length.Group Management (Multi-Material)
addGroup(start: number, count: number, materialIndex?: number): void
// Define a render group. start/count refer to index positions (indexed) or vertex positions (non-indexed).
clearGroups(): void
// Remove all groups.Draw Range
setDrawRange(start: number, count: number): void
// Limit which vertices/indices are rendered. Useful for progressive rendering or LOD.Transform Methods (All return this, modify vertex data in-place)
translate(x: number, y: number, z: number): BufferGeometry
rotateX(radians: number): BufferGeometry
rotateY(radians: number): BufferGeometry
rotateZ(radians: number): BufferGeometry
scale(x: number, y: number, z: number): BufferGeometry
center(): BufferGeometry // Center geometry at origin
lookAt(vector: Vector3): BufferGeometry // Orient geometry to face a pointConversion and Serialization
toNonIndexed(): BufferGeometry // Create non-indexed copy (duplicates shared vertices)
clone(): BufferGeometry // Deep copy the geometry
copy(source: BufferGeometry): BufferGeometry // Copy attributes from source
toJSON(): Object // Serialize to JSON
dispose(): void // Free GPU resources---
BufferAttribute
import { BufferAttribute } from 'three';Constructor
new BufferAttribute(array: TypedArray, itemSize: number, normalized?: boolean)
// array: Float32Array, Uint16Array, etc.
// itemSize: values per vertex (1=scalar, 2=UV, 3=position/normal, 4=RGBA)
// normalized: if true, integer values mapped to [0,1] or [-1,1] on GPU (default: false)Properties
attribute.array: TypedArray // The underlying data
attribute.itemSize: number // Values per vertex
attribute.count: number // Computed: array.length / itemSize
attribute.normalized: boolean // Normalize integer data (default: false)
attribute.usage: number // GPU usage hint (default: StaticDrawUsage)
attribute.needsUpdate: boolean // Set true to trigger GPU upload (default: false)
attribute.name: string // Optional identifier (default: "")
attribute.version: number // Auto-incremented on needsUpdate = trueAccessor Methods
getX(index: number): number
getY(index: number): number
getZ(index: number): number
getW(index: number): number
setX(index: number, x: number): this
setY(index: number, y: number): this
setZ(index: number, z: number): this
setW(index: number, w: number): this
setXY(index: number, x: number, y: number): this
setXYZ(index: number, x: number, y: number, z: number): this
setXYZW(index: number, x: number, y: number, z: number, w: number): thisOther Methods
clone(): BufferAttribute
copy(source: BufferAttribute): this
copyArray(array: TypedArray): this
copyAt(index1: number, attribute: BufferAttribute, index2: number): this
set(value: TypedArray, offset?: number): thisTyped Convenience Constructors
new Float32BufferAttribute(array: number[] | Float32Array, itemSize: number, normalized?: boolean)
new Float16BufferAttribute(array: number[] | Float16Array, itemSize: number, normalized?: boolean)
new Int8BufferAttribute(array: number[] | Int8Array, itemSize: number, normalized?: boolean)
new Int16BufferAttribute(array: number[] | Int16Array, itemSize: number, normalized?: boolean)
new Int32BufferAttribute(array: number[] | Int32Array, itemSize: number, normalized?: boolean)
new Uint8BufferAttribute(array: number[] | Uint8Array, itemSize: number, normalized?: boolean)
new Uint8ClampedBufferAttribute(array: number[] | Uint8ClampedArray, itemSize: number, normalized?: boolean)
new Uint16BufferAttribute(array: number[] | Uint16Array, itemSize: number, normalized?: boolean)
new Uint32BufferAttribute(array: number[] | Uint32Array, itemSize: number, normalized?: boolean)---
InterleavedBuffer
import { InterleavedBuffer } from 'three';
new InterleavedBuffer(array: TypedArray, stride: number)
// array: single typed array containing all interleaved data
// stride: number of values between consecutive entries of the same attributeProperties
buffer.array: TypedArray
buffer.stride: number
buffer.count: number // array.length / stride
buffer.usage: number // GPU usage hint
buffer.needsUpdate: boolean---
InterleavedBufferAttribute
import { InterleavedBufferAttribute } from 'three';
new InterleavedBufferAttribute(
interleavedBuffer: InterleavedBuffer,
itemSize: number,
offset: number,
normalized?: boolean
)
// interleavedBuffer: parent InterleavedBuffer
// itemSize: values per vertex for this attribute
// offset: starting position within each stride---
InstancedMesh
import { InstancedMesh } from 'three';Constructor
new InstancedMesh(geometry: BufferGeometry, material: Material, count: number)
// geometry: shared geometry (one copy on GPU)
// material: shared material (supports arrays for multi-material)
// count: maximum number of instances (CANNOT be changed after creation)Properties
mesh.instanceMatrix: InstancedBufferAttribute // 4x4 matrices (16 floats per instance)
mesh.instanceColor: InstancedBufferAttribute | null // Per-instance RGB (null until first setColorAt)
mesh.count: number // Number of instances (read-only after construction)
mesh.frustumCulled: boolean // Entire InstancedMesh culled as one unit (default: true)
mesh.boundingBox: Box3 | null
mesh.boundingSphere: Sphere | nullMethods
setMatrixAt(index: number, matrix: Matrix4): void
// Set transform matrix for instance at index.
getMatrixAt(index: number, matrix: Matrix4): Matrix4
// Read transform matrix for instance into target Matrix4.
setColorAt(index: number, color: Color): void
// Set per-instance color. Creates instanceColor on first call.
getColorAt(index: number, color: Color): Color
// Read per-instance color into target Color.
computeBoundingBox(): void
computeBoundingSphere(): void
dispose(): void---
InstancedBufferAttribute
import { InstancedBufferAttribute } from 'three';
new InstancedBufferAttribute(array: TypedArray, itemSize: number, normalized?: boolean, meshPerAttribute?: number)
// meshPerAttribute: number of meshes per attribute value (default: 1)---
Shape
import { Shape } from 'three';Constructor
new Shape(points?: Vector2[])
// Creates a shape. If points provided, creates a shape from them.Path Drawing Methods (inherited from Path)
shape.moveTo(x: number, y: number): this
shape.lineTo(x: number, y: number): this
shape.bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): this
shape.quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): this
shape.splineThru(points: Vector2[]): this
shape.arc(aX: number, aY: number, aRadius: number, aStartAngle: number, aEndAngle: number, aClockwise?: boolean): this
shape.absarc(aX: number, aY: number, aRadius: number, aStartAngle: number, aEndAngle: number, aClockwise?: boolean): this
shape.absellipse(aX: number, aY: number, xRadius: number, yRadius: number, aStartAngle: number, aEndAngle: number, aClockwise?: boolean, aRotation?: number): thisShape-Specific Properties
shape.holes: Path[] // Array of hole paths to subtract from the shape
shape.uuid: stringShape-Specific Methods
shape.getPointsHoles(divisions: number): Vector2[][]
shape.extractPoints(divisions: number): { shape: Vector2[], holes: Vector2[][] }---
Built-in Geometry Constructors — Complete Signatures
Primitives
new BoxGeometry(width?: number, height?: number, depth?: number,
widthSegments?: number, heightSegments?: number, depthSegments?: number)
new SphereGeometry(radius?: number, widthSegments?: number, heightSegments?: number,
phiStart?: number, phiLength?: number, thetaStart?: number, thetaLength?: number)
new PlaneGeometry(width?: number, height?: number, widthSegments?: number, heightSegments?: number)
new CylinderGeometry(radiusTop?: number, radiusBottom?: number, height?: number,
radialSegments?: number, heightSegments?: number, openEnded?: boolean,
thetaStart?: number, thetaLength?: number)
new ConeGeometry(radius?: number, height?: number, radialSegments?: number,
heightSegments?: number, openEnded?: boolean, thetaStart?: number, thetaLength?: number)
new CapsuleGeometry(radius?: number, length?: number, capSegments?: number, radialSegments?: number)
new TorusGeometry(radius?: number, tube?: number, radialSegments?: number,
tubularSegments?: number, arc?: number)
new TorusKnotGeometry(radius?: number, tube?: number, tubularSegments?: number,
radialSegments?: number, p?: number, q?: number)
new CircleGeometry(radius?: number, segments?: number, thetaStart?: number, thetaLength?: number)
new RingGeometry(innerRadius?: number, outerRadius?: number, thetaSegments?: number,
phiSegments?: number, thetaStart?: number, thetaLength?: number)Polyhedra
new TetrahedronGeometry(radius?: number, detail?: number)
new OctahedronGeometry(radius?: number, detail?: number)
new DodecahedronGeometry(radius?: number, detail?: number)
new IcosahedronGeometry(radius?: number, detail?: number)
new PolyhedronGeometry(vertices: number[], indices: number[], radius?: number, detail?: number)Path-Based
new ExtrudeGeometry(shapes: Shape | Shape[], options?: ExtrudeGeometryOptions)
new ShapeGeometry(shapes: Shape | Shape[], curveSegments?: number)
new LatheGeometry(points: Vector2[], segments?: number, phiStart?: number, phiLength?: number)
new TubeGeometry(path: Curve, tubularSegments?: number, radius?: number,
radialSegments?: number, closed?: boolean)Utility
new EdgesGeometry(geometry: BufferGeometry, thresholdAngle?: number)
new WireframeGeometry(geometry: BufferGeometry)