Navigate
Search topics across all sections
Built-in Geometries
Three.js comes with a toybox full of pre-made shapes — cubes, spheres, cylinders, toruses, and more. You don't need to define a single vertex. Just pick a shape, set some parameters, and you're building in 3D.
Your scene has 50 spheres and it should run smoothly. But it's crawling at 15fps on your laptop. You check the geometry — you set each sphere to 512 segments because 'more detail is better', right? That single decision created over 25 million triangles.
Scene renders at 15fps. GPU usage at 100%. Fans spinning loudly.
Real-world
Think of built-in geometries like LEGO bricks.
LEGO gives you pre-made pieces — flat plates, cubes, cylinders, arches. You don't need to sculpt each piece from scratch. You just grab the right brick and snap it in place.
Three.js works the same way. Need a cube? boxGeometry. A ball? sphereGeometry. A donut? torusGeometry.
Each "brick" comes with knobs you can adjust — width, height, how smooth the curves are. And just like LEGO, you can combine simple shapes to build surprisingly complex things.
Geometry
The shape (cube, sphere, etc.)
Material
The appearance (color, texture)
Mesh
Shape + Appearance = Visible object
Hands-On: Building with Shapes
Let's grab some LEGO bricks from the Three.js toybox and build something. Every shape just needs a geometry and a material.
Step 1: The basic shapes
{/* A 1x1x1 cube */}
<mesh>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="orange" />
</mesh>
{/* A sphere with radius 0.5 */}
<mesh position={[2, 0, 0]}>
<sphereGeometry args={[0.5, 32, 32]} />
<meshStandardMaterial color="royalblue" />
</mesh>The args prop passes parameters to the geometry constructor. For a box, it's [width, height, depth]. For a sphere, it's [radius, widthSegments, heightSegments]. The segments control how smooth the curve looks.
Step 2: More shapes from the toybox
{/* A cylinder */}
<mesh position={[-2, 0, 0]}>
<cylinderGeometry args={[0.5, 0.5, 2, 32]} />
<meshStandardMaterial color="mediumseagreen" />
</mesh>
{/* A donut (torus) */}
<mesh position={[0, 2, 0]}>
<torusGeometry args={[1, 0.4, 16, 48]} />
<meshStandardMaterial color="gold" />
</mesh>A cylinder takes [topRadius, bottomRadius, height, segments]. Make the top radius 0 and you get a cone! The torus (donut) takes [radius, tubeRadius, radialSegments, tubularSegments]. Play with the numbers to see how they change.
Step 3: Control the smoothness
{/* Low-poly look: 8 segments */}
<sphereGeometry args={[1, 8, 8]} />
{/* Smooth enough: 32 segments */}
<sphereGeometry args={[1, 32, 32]} />
{/* Ultra-smooth hero: 64 segments */}
<sphereGeometry args={[1, 64, 64]} />Segments are like how many flat panels make up a curved surface. More segments = smoother curves, but also more work for the GPU. 32 segments is the sweet spot for most cases. You usually can't tell the difference between 64 and 128.
What you just learned
Three.js provides pre-made shapes (box, sphere, cylinder, torus, cone, plane, and more) that you can use immediately.
A mesh = geometry (shape) + material (appearance). You need both to see something on screen.
The 'args' prop passes parameters to the geometry constructor — dimensions, radius, segments, etc.
Segments control smoothness of curved shapes. Start with 32 and only increase if you see visible flat edges.
Question
If a sphere with 32 segments looks smooth from 5 meters away, do you need more segments for a sphere that's 50 meters away? What about one that fills the entire screen?
Think about it...
You want to create a cone shape. Which geometry would you use?
Hint: Three.js has a dedicated geometry for this, but there's also a clever trick with another shape...
Try These Challenges
Put what you learned into practice. Try each challenge in the demo above using the Leva controls, then check the solution.
Try This!
Beginner
Switch to TorusKnot — how many segments feel smooth?
Try This!
Beginner
Set wireframe to true — count the triangles
Try This!
Intermediate
Scale to 2x — does the segment count change?
These are the patterns that trip up developers most often. Switch between Wrong and Fixed to compare the code side by side.
// 512x512 = 524,288 triangles for ONE sphere!
<sphereGeometry args={[1, 512, 512]} />
// 20 of these = 10 million triangles
{Array.from({ length: 20 }).map((_, i) => (
<mesh key={i}><sphereGeometry args={[1, 512, 512]} />// WRONG: 100 separate geometry instances!
{items.map((item, i) => (
<mesh key={i} position={item.pos}>
<sphereGeometry args={[0.3, 16, 16]} />
<meshStandardMaterial color="coral" />
</mesh>
))}// Removing mesh from scene does NOT free GPU memory!
scene.remove(oldMesh);
// Memory leak: geometry buffers stay on GPUBest Practices
Start with Low Segments
Begin with 16-32 segments for curved shapes. Only increase if you can actually see flat edges at your target camera distance. Distant objects need even fewer.
Reuse Geometry Instances
For many identical objects, share a single geometry instance with useMemo. The GPU stores the vertex buffer once — each mesh just adds its own position.
Let R3F Handle Disposal
When you remove a component from the tree, R3F automatically disposes its geometry and material. No manual cleanup needed — one of the biggest wins of using R3F.
Use PlaneGeometry for Floors
PlaneGeometry faces the camera by default. Rotate it -90 degrees on X to make a horizontal floor. Add segments only if you plan to deform it (waves, terrain).