BabylonJS

How to add 3D scene to your React app

The number of sites on the Internet is constantly growing. Today it is not enough to have a website attract the user’s attention. The sites have also become more complex and use more techniques to attract and hold the user’s attention. One of these techniques can be 3D graphics. The scene with interactive elements can help not only attract the attention of the user but also greatly increase the time he stays on your site or web application. Let’s check an example of the creation 3D scene for the React app using BabylonJS.

Introduction

Before diving into the technical details of implementing a 3d scene in your application, I would like to briefly define what is meant by a 3d scene and what they can be useful for.

A 3D scene is a virtual space available for viewing from any angle. It provides a realistic immersive experience in that space. Adding a 3D scene to your app can increase user engagement, provide new ways to interact with content, and create stunning visuals.

There are many tools for developing 3D graphics on the web. Today I suggest paying attention to the open-source project BabylonJS. It is a powerful and flexible framework that provides a wide range of tools to create complex 3D scenes.

It is also supposed to implement the 3d scene in the React project. To use declarative syntax I suggest using react-babylonjs.

Initial setup

To use BabylonJS in the React application, you will need to install some dependencies:

npm install babylonjs react-babylonjs

or

yarn add babylonjs react-babylonjs

After installing these dependencies, you can start using BabylonJS in your React application:

import * as BABYLON from 'babylonjs';
import { Scene, Engine } from 'react-babylonjs';

The next step is a creation of a basic 3D scene:

const App = () => {
  return (
    <Engine antialias adaptToDeviceRatio canvasId="babylon-js-canvas">
      <Scene>
        <arcRotateCamera name="camera" target={BABYLON.Vector3.Zero()}>
          <hemisphericLight name="light1" intensity={0.7} direction={BABYLON.Vector3.Up()} />
          <box name="box" size={2}>
            <standardMaterial name="mat" diffuseColor={BABYLON.Color3.Red()} />
          </box>
        </arcRotateCamera>
      </Scene>
    </Engine>
  );
};

The code snippet above creates a simple scene with a camera, a light, and a red box. The Engine component creates a BabylonJS engine, while the Scene component creates a scene. The arcRotateCamera component creates a camera that can be rotated around a target position. The hemisphericLight component creates a light source, while the box component creates a 3D box object. The standardMaterial component sets the material of the box to a red color.

Creating 3D objects with BabylonJS

Now that we have created a scene we can start creating and manipulating 3D objects.

BabylonJS provides a wide range of 3D object types: meshes, lights, cameras, etc. You can use the appropriate component to create a 3D object in BabylonJS and set its properties as needed.

For the example, we add a sphere object and adjust its position, rotation, and material:

const App = () => {
 return (
  <Engine antialias adaptToDeviceRatio canvasId="babylon-js-canvas">
   <Scene>
    <arcRotateCamera name="camera" target={BABYLON.Vector3.Zero()} alpha={-Math.PI / 2} beta={Math.PI / 4} radius={5} />
    <hemisphericLight name="light1" intensity={0.7} direction={BABYLON.Vector3.Up()} />
    <sphere name="sphere" diameter={2} segments={32}>
     <standardMaterial name="mat" diffuseColor={BABYLON.Color3.Yellow()} />
    </sphere>
   </Scene>
  </Engine>
 );
};

This part of the code adds a 3d sphere to the scene. The diameter prop sets the sphere’s size, and the segments prop sets the number of segments used to create the sphere’s surface. The standardMaterial component sets the sphere’s material and makes it yellow with the diffuseColor property. 

BabylonJS also provides the ability to manipulate 3D objects on stage by changing their position, rotation, and scaling. For example, let’s move the sphere we just created:

const App = () => {
 const sphereRef = useRef(null);

 useEffect(() => {
  if (sphereRef.current) {
   sphereRef.current.position.y = 2;
  }
 }, []);

 return (
  <Engine antialias adaptToDeviceRatio canvasId="babylon-js-canvas">
   <Scene>
    <arcRotateCamera name="camera" target={BABYLON.Vector3.Zero()} alpha={-Math.PI / 2} beta={Math.PI / 4} radius={5} />
    <hemisphericLight name="light1" intensity={0.7} direction={BABYLON.Vector3.Up()} />
    <sphere ref={sphereRef} name="sphere" diameter={2} segments={32}>
     <standardMaterial name="mat" diffuseColor={BABYLON.Color3.Yellow()} />
    </sphere>
   </Scene>
  </Engine>
 );
};

We use the useRef hook to access a sphere object and change its position in the useEffect hook.

Adding interactivity with BabylonJS

One of the most powerful features of BabylonJS is its ability to add interactivity to 3d scenes.

The easiest way to add interactivity is to use a simple event listener.

For example, let’s make it so that when you click on the sphere, it changes its color:

const App = () => {
  const sphereRef = useRef(null);

  const handleSphereClick = () => {
   if (sphereRef.current) {
    sphereRef.current.material.diffuseColor = BABYLON.Color3.Blue();
   }
  };

  return (
   <Engine antialias adaptToDeviceRatio canvasId="babylon-js-canvas">
    <Scene>
     <arcRotateCamera name="camera" target={BABYLON.Vector3.Zero()} alpha={-Math.PI / 2} beta={Math.PI / 4} radius={5} />
     <hemisphericLight name="light1" intensity={0.7} direction={BABYLON.Vector3.Up()} />
     <sphere ref={sphereRef} name="sphere" diameter={2} segments={32} onPointerDown={handleSphereClick}>
      <standardMaterial name="mat" diffuseColor={BABYLON.Color3.Yellow()} />
     </sphere>
    </Scene>
   </Engine>
  );
};

In this code snippet, we have added a simple event handler to the sphere component on onPointerDown. This handler will be called every time you click on the sphere. Inside the handleSphereClick we change the sphere’s color to blue.

Another example of adding interactivity to the 3d scene is animations. You can use the useEffect hook to set an animation loop for changing the position or rotating an object in the scene:

const App = () => {
  const sphereRef = useRef(null);

  useEffect(() => {
   let animation = null;

   const animateSphere = () => {
    if (sphereRef.current) {
     animation = scene.beginAnimation(sphereRef.current, 0, 60, true);
    }
   };

   const scene = sphereRef.current?.getScene();

   if (scene) {
    animateSphere();
   }

   return () => {
    if (animation) {
     scene.stopAnimation(animation);
    }
   };
  }, []);

  return (
   <Engine antialias adaptToDeviceRatio canvasId="babylon-js-canvas">
    <Scene>
     <arcRotateCamera name="camera" target={BABYLON.Vector3.Zero()} alpha={-Math.PI / 2} beta={Math.PI / 4} radius={5} />
     <hemisphericLight name="light1" intensity={0.7} direction={BABYLON.Vector3.Up()} />
     <sphere ref={sphereRef} name="sphere" diameter={2} segments={32}>
      <standardMaterial name="mat" diffuseColor={BABYLON.Color3.Yellow()} />
     </sphere>
    </Scene>
   </Engine>
  );
};

In this example, we set up an animation loop using the beginAnimation method from the BabylonJS scene object.

The animation will update the sphere’s position from its original point to its ending position for 60 frames and then loop back to the beginning. We also set up a cleanup function to stop the animation when the component unmounts.

How to use Custom models with BabylonJS

In addition to using pre-built objects from BabylonJS, this framework also allows you to import any other 3D objects and use them in your scene.

To do this, you need to:

1. Find the GLB file of the 3d model you need. You can either create it yourself or find ready-to-use models online.

2. After that you can load the file on your 3d scene using the GLTFFileLoader component or the useGLTF hook from react-babylonjs

import { Engine, Scene, useGLTF } from 'react-babylonjs';
import { GLTFFileLoader } from '@babylonjs/loaders';

const CustomModel = () => {
 const { nodes } = useGLTF('/path/to/custom-model.glb', true, GLTFFileLoader);

 return (
  <mesh name="customModel" position={new BABYLON.Vector3(0, 0, 0)} rotation={new BABYLON.Vector3(0, 0, 0)} scaling={new BABYLON.Vector3(1, 1, 1)}>
   <modelMesh mesh={nodes['CustomModel']} />
  </mesh>
 );
};

const App = () => {
 return (
  <Engine antialias adaptToDeviceRatio canvasId="babylon-js-canvas">
   <Scene>
    <arcRotateCamera name="camera" target={BABYLON.Vector3.Zero()} alpha={-Math.PI / 2} beta={Math.PI / 4} radius={5} />
    <hemisphericLight name="light1" intensity={0.7} direction={BABYLON.Vector3.Up()} />
    <CustomModel />
   </Scene>
  </Engine>
 );
};

3. The last step is adding the uploaded model to your 3d scene. In the example above, we used a separate CustomModel component for it.

Conclusion

In this article, I explained the basics of working with 3D graphics in a React application using BabylonJS.

In the examples, we created a 3d scene, added an object to it, learned how to manipulate it, and added interactivity in the form of the onPointerDown event and animation loop.