Introduction
Babylon.js is a powerful open-source 3D engine for the web. It enables immersive experiences directly in the browser without heavy installations. This intermediate tutorial guides you step-by-step through building a complete 3D scene including meshes, dynamic lighting, animations, and keyboard/mouse interactions. You will learn to properly structure your TypeScript code and leverage advanced engine features for smooth 60 FPS rendering.
Prerequisites
- Basic knowledge of TypeScript and HTML5
- Node.js 20+ and npm
- A modern editor (VS Code recommended)
- Recent browser supporting WebGL 2
Project Initialization
npm create vite@latest babylon-scene -- --template react-ts
cd babylon-scene
npm install babylonjs babylonjs-guiVite + React + TypeScript provides a fast development environment. Babylon.js and its GUI module are installed to handle both 3D rendering and user interfaces.
Creating the Engine and Scene
import { Engine, Scene, ArcRotateCamera, Vector3, HemisphericLight } from 'babylonjs';
import { useEffect, useRef } from 'react';
export const BabylonScene = () => {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
const canvas = canvasRef.current!;
const engine = new Engine(canvas, true);
const scene = new Scene(engine);
const camera = new ArcRotateCamera('camera', Math.PI / 2, Math.PI / 3, 10, Vector3.Zero(), scene);
camera.attachControl(canvas, true);
const light = new HemisphericLight('light', new Vector3(0, 1, 0), scene);
engine.runRenderLoop(() => scene.render());
window.addEventListener('resize', () => engine.resize());
return () => engine.dispose();
}, []);
return <canvas ref={canvasRef} style={{ width: '100%', height: '100vh' }} />;
};This code initializes the Babylon.js engine, creates a scene, and sets up an orbital camera. The runRenderLoop ensures 60 FPS rendering while resize handles window size changes.
Adding Basic Meshes
import { MeshBuilder, StandardMaterial, Color3 } from 'babylonjs';
// Inside the useEffect, after creating the light
const sphere = MeshBuilder.CreateSphere('sphere', { diameter: 2 }, scene);
sphere.position.y = 1;
const ground = MeshBuilder.CreateGround('ground', { width: 10, height: 10 }, scene);
const material = new StandardMaterial('mat', scene);
material.diffuseColor = new Color3(0.2, 0.6, 0.9);
sphere.material = material;MeshBuilder allows quick creation of primitives. A StandardMaterial is applied to the sphere to give it color and a realistic appearance.
Adding Lighting and Shadows
import { DirectionalLight, ShadowGenerator } from 'babylonjs';
const dirLight = new DirectionalLight('dirLight', new Vector3(-1, -2, -1), scene);
dirLight.position = new Vector3(5, 10, 5);
const shadowGenerator = new ShadowGenerator(1024, dirLight);
shadowGenerator.addShadowCaster(sphere);
ground.receiveShadows = true;A directional light with shadow generator adds realism. The sphere casts a shadow on the ground, an essential technique for intermediate scenes.
Animation with AnimationGroup
import { Animation, AnimationGroup } from 'babylonjs';
const animGroup = new AnimationGroup('bounce');
const anim = new Animation('bounceAnim', 'position.y', 30, Animation.ANIMATIONTYPE_FLOAT);
const keys = [
{ frame: 0, value: 1 },
{ frame: 30, value: 3 },
{ frame: 60, value: 1 }
];
anim.setKeys(keys);
animGroup.addTargetedAnimation(anim, sphere);
animGroup.play(true);AnimationGroup allows controlling multiple animations together. Here the sphere bounces in a loop, demonstrating Babylon.js frame-based animation system.
Adding Keyboard Interactions
window.addEventListener('keydown', (evt) => {
if (evt.key === 'r') {
sphere.rotation.y += Math.PI / 2;
}
if (evt.key === ' ') {
animGroup.play(true);
}
});Listening to keyboard events enables scene interaction. Pressing R rotates the sphere and the spacebar restarts the animation.
Best Practices
- Always dispose resources (engine.dispose) to avoid memory leaks
- Use AnimationGroup instead of individual animations for better management
- Preload textures and models with AssetsManager
- Separate rendering logic from business logic in dedicated classes
- Enable WebGL 2 support and hardware extensions for better performance
Common Mistakes to Avoid
- Forgetting to call engine.resize() on window changes
- Creating materials inside the render loop
- Not checking WebGL availability before initialization
- Using excessively high frame values without adjusting animation frequency
Going Further
Deepen your skills with the Babylon.js courses from Learni. You will explore glTF model loading, custom shaders, and integration with React Three Fiber.