THREE JS Scene As a React Component
It's possible to embed ThreeJS scenes as React Component. Which means you can build an awesome WebGL intro page for your site and use React Routers to replace it with other components, How cool is that?
If you don't know much about THREE js then have no worry. Last year on this very same day I started learning THREE js. It has been a year now. I made around 30+ WebGL cool demos. Many of them got featured in BABYLON js 4.1 release Demos:
If you are interested in that kind of stuff then you can see them live:
At the end of this page, you will know enough to start creating stuff in the 3D world.
Let's Start Building a Simple Component which renders an 800x800 <div/>
import React, { Component } from "react";import * as THREE from "three";
import { MTLLoader, OBJLoader } from "three-obj-mtl-loader";
import OrbitControls from "three-orbitcontrols";class ThreeScene extends Component {render() {
return (<div
style={{ width: "800px", height: "800px" }}
ref={mount => { this.mount = mount}}
/>)
}}export default ThreeScene;
Now add Light, Camera, and Renderer
Once your React Component is mounted componentDidMount call will get triggered this is where you start setting up Your WebGL scene:
The first thing you need is THREE.scene() object which sets up what and where to render your scene.
Next, you need a WebGLRenderer which will gonna render WebGL stuff on 800x800 div element.
Once the render is mounted on <div/> we add Camera which can see what is being rendered on Scene and then we add Lights.
We can add Any models after this setup is done.
Now e a start to draw WebGL scene on each draw of Browser
The
window.requestAnimationFrame()
the method tells the browser that you wish to perform an animation and requests that the browser calls a specified function to update an animation before the next repaint. The method takes a callback as an argument to be invoked before the repaint.
On each request animation Frame call of the browser, we request WebGL renderer to redraw WebGL scene. Before rerendering, we can animate our 3D Models. We need to be cautious that it must not take too long because firstly:
- Everything is running on single thread javaScript Thread. Performing too much animation & physics calculation will cause the browser to become nonresponding.
- If it takes too long then we rendering will not be 60 frames per seconds
After this is done your code should it look like this.This is basic setup for any THREE.js Scene
componentDidMount() {const width = this.mount.clientWidth;
const height = this.mount.clientHeight;this.scene = new THREE.Scene();//Add Renderer
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setClearColor("#263238");
this.renderer.setSize(width, height);
this.mount.appendChild(this.renderer.domElement);//add Camera
this.camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
this.camera.position.z = 8;
this.camera.position.y = 5;//Camera Controls
const controls = new OrbitControls(this.camera, this.renderer.domElement);//LIGHTS
var lights = [];lights[0] = new THREE.PointLight(0x304ffe, 1, 0);
lights[1] = new THREE.PointLight(0xffffff, 1, 0);
lights[2] = new THREE.PointLight(0xffffff, 1, 0);
lights[0].position.set(0, 200, 0);
lights[1].position.set(100, 200, 100);
lights[2].position.set(-100, -200, -100);
this.scene.add(lights[0]);
this.scene.add(lights[1]);
this.scene.add(lights[2]);//ADD Your 3D Models herethis.renderScene();//start animation
this.start();}start = () => {
if (!this.frameId) {
this.frameId = requestAnimationFrame(this.animate);}
};stop = () => {
cancelAnimationFrame(this.frameId);
};animate = () => {//Animate Models Here//ReDraw Scene with Camera and Scene Object
this.renderScene();
this.frameId = window.requestAnimationFrame(this.animate);
};renderScene = () => {
if (this.renderer) this.renderer.render(this.scene, this.camera);
};
Models in THREE.js are called Mesh. A Mesh is a Geometry with Material:
Mesh = Geometry +Material
For example, a Cube Mesh is made with Cube Geometry and a Material passed in Mesh Constructor:
const cubeGeometry = new THREE.BoxGeometry(5, 5, 5);
const material = new THREE.MeshBasicMaterial({
color: “#0F0”,
});this.cubeMesh = new THREE.Mesh(geometry, material);this.scene.add(this.cube);
BufferGeometry have better performance than normal Geometry
Example Box Buffer Geometry:
const bufferCubegeometry = new THREE.BoxBufferGeometry( 5, 5, 5); const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } ); this.cubeBufferMesh = new THREE.Mesh( bufferCubegeometry, material );
scene.add( this.cubeBufferMesh);
This will add a Box of dimensions 5x5x5 in the scene

We can Enable Wireframe in Material like this
const material = new THREE.MeshBasicMaterial({
color: “#0F0”,
wireframe: true
});

Adding Texture
We can load any image as Texture using TextureLoader and once the image is loaded we can update the material like shown below:
//LOAD texture from Web and on completion apply it on SPHEREnew THREE.TextureLoader().load("https://images.pexels.com/photos
/1089438/pexels-photo-1089438.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260",texture => { //Update Texture
this.cube.material.map = texture;
this.cube.material.needsUpdate = true;
},
xhr => {
//Download Progress
console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
},
error => {
//Error CallBack
console.log("An error happened" + error);}
);
I loaded Matrix Image and applied it as texture over the Cube:

Animate Cube
In the animate method of the first step, simply add animation like this:
animate = () => {//Rotate Models
if (this.cube) this.cube.rotation.y += 0.01;this.renderScene();
this.frameId = window.requestAnimationFrame(this.animate);
};
This will make Cube to rotate around Y-axis
Loading 3D Models
We need to use Loaders to load 3D models. The most common model formats are Babylon, Gltf, Glb, Obj, fbx , JSON, and dae. There are different ways to load different model formats in THREE .js
I just gonna show the OBJ model, for now, I will explain about other ways in some other nest post.
// -----Step 4--------//Loading 3d Models//Load Material Firstvar mtlLoader = new MTLLoader();
mtlLoader.setBaseUrl("./assets/");
mtlLoader.load("freedom.mtl", materials => {materials.preload();console.log("Material loaded");//Load Object Now and Set Materialvar objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
objLoader.load("./assets/freedom.obj",
object => {this.freedomMesh = object;
this.freedomMesh.position.setY(3); //or this
this.freedomMesh.scale.set(0.02, 0.02, 0.02);
this.scene.add(this.freedomMesh);
},xhr => {
console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
},// called when loading has errors
error => {
console.log("An error happened" + error);
});
});
The Final Result Look like this:
Open it on CodeSendbox to see the final code. or some reason Embedding is not working on Medium:
That is all for now. Please like and hit the clap:)
Follow me on Tweeter for Inspiring demos every Week:
https://twitter.com/HiteshSahu_
Visit my site for awesome WebGL Demos:
https://hiteshsahu.com/lab