Tiny Projects: Cubes With React Three Fiber

Lloyd Atkinson

Use your mouse or touch screen to interact

This is my second post in my series of tiny projects. I’ve been studying geometry, graphics, and the mathematics behind all of that. I previously wrote a little about P5 with React and this time I’m experimenting with React Three Fiber (R3F for short). I have projects in mind for both libraries.

R3F brings Three to React:

Build your scene declaratively with re-usable, self-contained components that react to state, are readily interactive and can participate in React’s ecosystem.

This is the first time I’ve used Three and, by extension, WebGL. I wanted to create something simple that builds on my existing knowledge but applied to a 3D environment.

  • Display some very simple geometry (cubes)
  • Rotate geometry smoothly
  • Interact with mouse and touch gestures

Firstly I needed to create a type to represent a single cube:

type Cube = {
position: Vector3;
color: Color;
rotationSpeed: number;
};

This is a very simplistic idea of a cube, I just needed three properties for this learning project. The position is totally random and will sometimes overlap other cubes. Solving that is a topic for another time. The rotation speed (the X and Y coordinates being updated over time) is also randomly set.

The cube is updated every frame to create the rotation effect.

useFrame((_, delta) => {
ref.current.rotation.x += delta * rotationSpeed;
ref.current.rotation.y += delta * rotationSpeed;
});

The previous two blocks mostly concerned themselves with the particulars of representing and updating cubes. Following this I have a component which renders a single cube.

const Box = ({ color, rotationSpeed, position }: Cube) => {
const ref = useRef<Mesh>(null!);
const [clicked, setClicked] = useState(false);
useFrame((_, delta) => {
ref.current.rotation.x += delta * rotationSpeed;
ref.current.rotation.y += delta * rotationSpeed;
});
return (
<mesh
ref={ref}
position={position}
scale={clicked ? 1.5 : 1}
onClick={() => setClicked(!clicked)}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color={color} />
</mesh>
);
};

Now all of the building blocks (or maybe, building cubes?) are in place I need to render them. This code again highlighting the benefits of using a declarative framework like React with a graphics library like Three.

export const RotatingCubes = () => {
const [cubes, setCubes] = useState<Cube[]>([...Array(20)].map<Cube>(() => ({
position: new Vector3(...randomPosition()),
color: randomColor(),
rotationSpeed: Math.random(),
})));
return (
<div style={{ width: '100%', height: '500px' }}>
<Canvas>
<OrbitControls />
<ambientLight intensity={0.4} />
<spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} />
<pointLight position={[-10, -10, -10]} />
{
cubes.map((cube, index) => (
<Box {...cube} key={index} />
))
}
</Canvas>
</div>
);
};

This was a lot of fun to create and I’m excited to see what I make next!

Share:

Spotted a typo or want to leave a comment? Send feedback

Stay up to date

Subscribe to my newsletter to stay up to date on my articles and projects

© Lloyd Atkinson 2024 ✌

I'm avaliable for work 💡