Navigate

Search topics across all sections

GitHub
Geometries

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.

terminal
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

App.tsxTSX
{/* 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

App.tsxTSX
{/* 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

App.tsxTSX
{/* 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.

1
Way too many segments on simple objects
A sphere with 512 segments that nobody will ever notice
Don't do this
App.tsxTSX
// 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]} />
Each segment adds vertices the GPU must process. A 512-segment sphere has over 500,000 triangles but looks identical to a 64-segment sphere at most viewing distances. Start with 16-32 segments and only increase if you can actually see faceting.
2
Not sharing geometry between meshes
100 identical spheres each create their own geometry data
Don't do this
App.tsxTSX
// 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>
))}
R3F's JSX creates new geometry per element by default. For many identical objects, share a single geometry and material instance. The GPU stores the vertex buffer once and each mesh just references it with its own transform.
3
Not disposing geometry when removing objects
GPU memory leaks as objects are created and removed
Don't do this
App.tsxTSX
// Removing mesh from scene does NOT free GPU memory!
scene.remove(oldMesh);
// Memory leak: geometry buffers stay on GPU
In vanilla Three.js, removing a mesh does NOT free its GPU memory. You must call .dispose() explicitly. The good news: R3F handles disposal automatically when components unmount. This is one of the biggest quality-of-life improvements R3F offers.

Best 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).