
The Babylon.js structure and graphs
First, let's create and explain the necessary tools to draw things on the screen, such as an engine that will handle a scene to draw a 3D object and use a camera and light.
The engine and a scene
The engine is the core of Babylon.js and a scene allows you to create and manage entities that you'll draw on the screen (objects, lights, cameras, and so on), thanks to an engine. You can see the engine like a gateway to communicate with the video card (GPU) while a scene is a high-level interface that handles the following multiple entities:
- 3D objects (more on this in Chapter 3, Create, Load, and Draw 3D Objects on the Screen)
- Cameras
- Lights
- Particle systems (smoke, rain, and others)
- Textures
- Skeletons (animated 3D objects)
- Post-processes (effects)
- Materials (more on this in Chapter 4, Using Materials to Customize 3D Objects Appearance)
- Sprites
In other words, a scene handles multiple entities and will call the engine to draw these entities on the screen.
To draw on the screen, the web page (index.html
) must contain a canvas. The canvas is used to create the WebGL context that the engine will use. The only parameter needed to create an engine is the canvas and the only parameter needed to create a scene is an engine.
The canvas in the web page is as follows:
<canvas id="renderCanvas"></canvas>
The following code is performed to render on the entire page:
<style> html, body { overflow: hidden; width: 100%; height: 100%; margin: 0; padding: 0; } #renderCanvas { width: 100%; height: 100%; } </style>
To create an engine, you have to give the canvas reference as a parameter. Get the reference of the DOM object as follows:
var canvas = document.getElementById("renderCanvas");
Create an engine by passing the canvas reference:
var engine: BABYLON.Engine = new BABYLON.Engine(canvas);
Create a scene as follows:
var scene: BABYLON.Scene = new BABYLON.Scene(engine);
Each new scene created is stored in engine.scenes
, which is an array of BABYLON.Scene
.
Once you create a scene, you can render every frame (60 frames per second in a perfect case and less if your hardware isn't sufficient). To perform this action, you have to call the engine.runRunderLoop(function)
function. The anonymous function function
is called every frame and this is the place to draw the scene:
// runRenderLoop with TypeScript. The anonymous function doesn't // have any parameter engine.runRenderLoop(() => { scene.render(); });
The scene.render()
function is called to draw every object on the screen during the frame.
Adding cameras and lights
Once you create a scene, you can create objects such as lights and cameras. The minimal scene requirement is composed of a camera (needed to get a view in the scene) and light (that illuminates the 3D objects in the scene, but this is not required if objects in the scene are self-illuminated).
Adding a camera
There are several types of cameras and lights. The Babylon.js framework provides you with a fixed camera (no movements), FPS camera, rotation camera (turns around a point), gamepad camera (XBOX 360 gamepad), and touch camera (for touch devices). All these cameras can be created in the scene with a minimum number of lines of code. For example, let's start with a free camera (FPS) with its position at coordinates (x=10
,y=20
,z=30
). This is the occasion to introduce the BABYLON.Vector3
class:
var camera = new BABYLON.FreeCamera("cameraName", new BABYLON.Vector3(10, 20, 30), scene);
The constructor needs a name (cameraName
), position for the camera, and scene reference (where to add the camera). The position is in the BABYLON.Vector3(x, y, z)
type, which is the 3D vector class of Babylon.js. The Vector3
object provides mathematical functions such as addition, subtraction, multiplication, pision, and more.
Once you create the camera, let's change the camera's position and perform a Vector3
addition:
camera.position = new BABYLON.Vector3(10, 0, 10).addInPlace(new BABYLON.Vector3(0, 10, 0));
The .addInPlace
method modifies the current instance of Vector3
, but you can also call the .add
method that will return a new instance of Vector3
with the addition applied.
The new position of the camera is now (x=10
,y=10
,z=10
). Modifying the .position
property does not work for all the cameras, especially the rotation camera (BABYLON.ArcRotateCamera
). The rotation camera turns around a point (which is the target of the camera) and can be explained as follows:

The cube is at the position (x=0
,y=0
,z=0
) and is the target of the camera. To change the camera's position, you have to call the camera.setPosition(position)
function. This function will calculate the appropriate values for the alpha (angle around Y), beta (angle around X), and radius. Then, the .position
property is only left to read.
Adding a light
Once you create your camera, let's add a light. The different lights allow you to perform lighting in your scene with different features. The point lights and hemispheric lights tend to illuminate the objects in the scene, while the spot lights and directional lights also provide support for the real-time shadows.
Let's start with a point light. Lights work like cameras, which means that you have to provide the scene reference (where to add the light):
var light = new BABYLON.PointLight("lightName", BABYLON.Vector3.Zero(), scene);
The second parameter is the light position. BABYLON.Vector3.Zero()
is a static method that is a shortcut to create a Vector3
instance with coordinates (x=0
,y=0
,z=0
). Now, let's play with the light's parameters:
light.position = new BABYLON.Vector3(20, 20, 20); light.diffuse = new BABYLON.Color3(0, 1, 0); light.specular = new BABYLON.Color3(1, 0, 0); light.intensity = 1.0;
The following are the parameters of the preceding code:
- The
.diffuse
parameter represents the native color of the light, which is green in the example - The
.specular
parameter represents the light color reflected by a surface, which is red here - The
.intensity
parameter is the light's intensity that is equal to 1.0 by default
The following is the result of these parameters applied to a 3D object:

The following is achieved by modifying the specular color to red and blue:

Adding a mesh
What are meshes? In fact, 3D objects are what we call meshes. Their structure is pretty simple as they have two important buffers in memory:
- The vertex buffer is an array of 3D points: the vertices. The vertices represent the 3D points needed to build geometrical forms such as cubes, spheres, characters, weapons, and so on. For example, a cube has eight vertices.
- The index buffer, which is an array of numbers, represents the indices in the vertex buffer to build triangles. Indeed, graphic cards are optimized to calculate and render triangles on the screen. For example, a face of a cube will be rendered using two triangles, as shown in the following image, where the green lines show the face:
The job of graphic cards is separated into two steps:
- They project (transformation) the vertices (3D points) of the vertex buffer on the screen. These operations are performed by a GPU program named vertex shader. In other words, the vertex shader computes the 2D position of each triangle.
- They fill the pixels associated with the triangles with their colors using a GPU program named pixel shader.
To conclude the graphic cards' job, the vertex shader computes the 2D triangles on the screen and the pixel shader illuminates the pixels with different colors.
Using Babylon.js to create meshes
Babylon.js provides you with static methods in the BABYLON.Mesh
class that allow you to create basic meshes such as boxes, spheres, and torus. Every created mesh is an instance of BABYLON.Mesh
. Let's start with a box:
var box = BABYLON.Mesh.CreateBox("boxName", size, scene);
The size
parameter represents the distance between the vertices (for example, 5) and the scene
parameter is the scene reference (where to add the mesh). Once you get the mesh created, you can access its properties and methods as follows:
box.position = new BABYLON.Vector3(0, 2.5, 0); box.rotation = new BABYLON.Vector3(0, Math.PI / 4, 0); box.scaling = new BABYLON.Vector3(2, 2, 2);
The .rotation
parameter represents the mesh's rotation expressed in radians [0, 2π]
, and then, in degrees, π = 180 degrees
and 2π = 360 degrees
.
The .scaling
parameter represents the mesh's scale in three directions (x,y,z). In the example, the mesh is two times bigger.
Let's see the result if you modify its rotation only:

The following image is the result of applying a new scale, (x=2
,y=2
,z=2
):

Some other basic meshes
There exist multiple basic meshes that BABYLON.Mesh
can create for you, such as spheres and planes. Let's create a sphere and plane using the BABYLON.Mesh
class:
var sphere = BABYLON.Mesh.CreateSphere("sphereName", segments, size, scene); var plane = BABYLON.Mesh.CreatePlane("planeName", size, scene);
The sphere's segments
parameter represents the level of detail for the sphere, for example, 10.
The plane's size
parameter represents the distance between vertices, for example, 10.
Managing a scene graph
The scene can be managed using a graph of nodes named scene graph. In Babylon.js, each mesh, light, and camera extends the BABYLON.Node
class. In other words, each BABYLON.Node
is in the scene graph.
Each node has an array of children and one, and only one, parent. Each created node is a child of the graph's root by default.
To modify the parent of a node, just modify the node's reference:
light.parent = camera;
Now, because the light is a child of the camera, the light's position depends on the camera's position. Indeed, the light space is a subspace of the camera. In other words, you can see the position (x=0
,y=0
,z=0
) as the position of the camera.
When setting a parent, the transformation properties (position, rotation, and scaling) of the children are influenced by the parent.
An example
Let's manage the scene graph with two nodes (node1
and node2
) and compare it with the equivalent code if you were to do it manually:
node1.position = new BABYLON.Vector3(0, 0, 0); node1.parent = node2;
This is equivalent to the following code:
engine.runRenderLoop(() => { node1.position = node2.position; node1.rotation = node2.rotation; node1.scaling = node2.scaling; });
The following schema explains the preceding code:
