Comprehensive Guide to Babylon.js Game Engine

.

Introduction to Babylon.js

Originally created by Microsoft developers and now powered by hundreds of contributors, Babylon.js is a comprehensive open-source 3D game engine that empowers developers with exceptional freedom. Unlike other 3D frameworks that lock users into proprietary editors and workflows, Babylon.js takes a pure code-first approach—it's entirely JavaScript-based, requiring no resource-heavy editing tools, compilation steps, or specialized environments. This liberating design philosophy means developers can work with familiar web technologies and their preferred coding tools, whether that's a lightweight text editor or full IDE. The framework's straightforward API allows direct manipulation of the 3D world through clean, readable code that integrates seamlessly with existing web projects. Built on WebGL, Babylon.js delivers impressive performance for games, visualizations, and immersive applications while maintaining the flexibility that web developers value. This rare combination of sophisticated capabilities and development freedom has made Babylon.js the engine of choice for developers who want powerful 3D features without sacrificing their autonomy or adding bloated toolchains to their workflow.

Key strengths of Babylon.js include:

  • Performance: Optimized for speed and efficiency with powerful rendering capabilities
  • Extensibility: Modular architecture that allows for custom features and extensions
  • Comprehensive Features: Built-in physics, audio, particle systems, and advanced materials
  • Cross-Platform: Works across desktop and mobile browsers without plugins
  • Community Support: Active development community and extensive documentation
  • Modern Web Standards: Supports WebXR, WebGPU, and other cutting-edge web technologies

Babylon.js stands out for its balanced approach - powerful enough for professional game development while remaining accessible for web developers transitioning into 3D graphics.

.

Getting Started with Babylon.js

There are multiple ways to incorporate Babylon.js into your project, giving you flexibility based on your development environment and needs. You can include it via CDN for quick prototyping, install it using npm for modern development workflows, or download the files directly for offline use.

When importing Babylon.js in your code, you have two primary approaches: a modular approach where you selectively import only the specific components you need (recommended for production to minimize bundle size), or importing the entire library for a more convenient development experience when experimenting with different features. This flexibility allows you to optimize for either development speed or production performance.

.

CDN Integration

You can quickly add Babylon.js to your project by including scripts from the Content Delivery Network:

<script src="https://cdn.babylonjs.com/babylon.js"></script>
<!-- Optional modules as needed -->
<script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
<script src="https://cdn.babylonjs.com/materials/babylonjs.materials.min.js"></script>
<script src="https://cdn.babylonjs.com/gui/babylon.gui.min.js"></script>
.

NPM Installation

For modern JavaScript projects using build tools, you can install Babylon.js via npm:

npm install babylonjs babylonjs-loaders --save
.

Global Import

Import the entire library for easier development experience:

With Babylon.js 7, the modular approach has been refined to improve loading performance and reduce bundle sizes. For development environments, using the global import provides convenience with access to all APIs without needing to add imports for each component.

// Import everything - larger bundle size but simpler development
import * as BABYLON from 'babylonjs';
import 'babylonjs-loaders';
.

Selective Import

Import only the specific components you need to minimize bundle size. This approach is recommended for production as it can significantly reduce final bundle size.

// Import only required components
import { Engine, Scene, FreeCamera, Vector3, HemisphericLight, MeshBuilder } from 'babylonjs';
import { SceneLoader } from 'babylonjs/Loading/sceneLoader';
.

Setting Up the Canvas

The HTML5 canvas element provides the foundation for Babylon.js rendering. This section covers how to properly set up your canvas element and prepare it for 3D rendering.

The canvas acts as the viewport into your 3D world, and proper setup is crucial for performance and user experience. Here's how to create a basic HTML structure with a properly configured canvas:

.

Basic HTML Setup

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Babylon.js Basic Scene</title>
    <style>
        html, body {
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
    <script src="https://cdn.babylonjs.com/babylon.js"></script>
</head>
<body>
    <canvas id="renderCanvas"></canvas>
    <script src="app.js"></script>
</body>
</html>
.

Canvas Considerations

Key canvas considerations:

  • Fullscreen Canvas: Setting width and height to 100% creates an immersive experience that fills the viewport
  • Touch Action: The touch-action: none CSS property prevents default touch behaviors like scrolling when interacting with the canvas
  • Canvas ID: Assigning an ID makes it easy to reference the canvas in JavaScript
  • Overflow Handling: Setting overflow: hidden on the body prevents scrollbars when the canvas fills the viewport
.

Responsive Canvas

For responsive applications, you might need to handle canvas resizing when the window dimensions change:

window.addEventListener('resize', function() {
    engine.resize();
});

This ensures your 3D scene maintains the correct aspect ratio and fills the available space when the browser window is resized.

.

The Engine Object

The Engine is the foundation of Babylon.js, responsible for interfacing with WebGL, managing the rendering context, and coordinating the rendering pipeline. Understanding the Engine object is essential for creating optimized 3D applications.

.

Engine Initialization

Creating an Engine instance links Babylon.js to your canvas and initializes the WebGL context:

// Get the canvas element from the DOM
const canvas = document.getElementById("renderCanvas");

// Initialize the Babylon Engine with the canvas
// Parameters: canvas, antialias, options, adaptToDeviceRatio
const engine = new BABYLON.Engine(canvas, true, {
    preserveDrawingBuffer: true,
    stencil: true,
    disableWebGL2Support: false
}, true);
.

Engine Options

The Engine constructor accepts several important options:

  • antialias (boolean): Enables antialiasing for smoother edges
  • preserveDrawingBuffer (boolean): Required for taking screenshots of the canvas
  • stencil (boolean): Enables stencil buffer for advanced rendering effects
  • premultipliedAlpha (boolean): Controls alpha blending behavior
  • adaptToDeviceRatio (boolean): Adjusts rendering resolution based on device pixel ratio

In Babylon.js 7, the Engine architecture has been refined for better performance with modern WebGL implementations and includes improved support for WebGPU, offering significant performance improvements on compatible browsers.

.

Engine Architecture

The Engine acts as an abstraction layer between your application code and the underlying WebGL context. It manages crucial aspects including:

  • Rendering Context: Handles WebGL state and operations
  • Render Loop: Controls the animation timing and frame rate
  • Resource Management: Manages GPU resources like textures and buffers
  • Capabilities Detection: Identifies hardware/browser capabilities and limitations
  • Scene Rendering: Coordinates the rendering of all scenes
.

Engine Methods

Common Engine methods include:

// Start the render loop
engine.runRenderLoop(() => {
    scene.render();
});

// Handle window resize events
engine.resize();

// Get current FPS
const fps = engine.getFps().toFixed();

// Set hardware scaling level
engine.setHardwareScalingLevel(0.5); // render at half resolution

// Dispose engine and release resources
engine.dispose();
.

Hardware Acceleration and Optimization

The Engine automatically detects hardware capabilities and adapts accordingly. You can also implement your own optimizations:

// Adjust performance based on FPS
engine.runRenderLoop(() => {
    const currentFps = engine.getFps();
    if (currentFps < 30) {
        // Reduce quality to improve performance
        engine.setHardwareScalingLevel(0.8);
    } else if (currentFps > 60) {
        // Improve quality if performance is good
        engine.setHardwareScalingLevel(1.0);
    }
    scene.render();
});

Babylon.js 7 introduces enhanced performance profiling tools, allowing developers to identify and address bottlenecks more effectively.

.

Scene Management

The Scene object is the container for all elements in your 3D world - meshes, lights, cameras, and more. It coordinates these elements and manages their interactions within the 3D space.

.

Scene Creation

Creating a scene is straightforward:

// Create a new scene
const scene = new BABYLON.Scene(engine);

// Optional: Set scene properties
scene.clearColor = new BABYLON.Color4(0.2, 0.2, 0.3, 1); // Set background color
scene.ambientColor = new BABYLON.Color3(0.3, 0.3, 0.3); // Set ambient light
.

Scene Architecture

The Scene class implements several important software engineering patterns:

The Scene class in Babylon.js implements several important software engineering patterns that facilitate effective 3D application development:

  • Observer Pattern: The scene utilizes a robust event system where objects emit events that other components can subscribe to. This enables loose coupling between components, as listeners can react to changes without direct dependencies. For example, scene.onBeforeRenderObservable allows code to execute before each frame renders without modifying the render loop itself. This pattern is extensively used throughout Babylon.js for everything from input handling to animation triggers.
  • Component System: Elements like meshes, lights, cameras, and materials are managed as components attached to the scene. Each component handles its specific functionality while the scene coordinates their interactions. This modular approach allows developers to add, remove, or modify scene elements independently. The scene maintains registries of these components (e.g., scene.meshes, scene.lights) and handles their lifecycle events appropriately.
  • Hierarchical Structure: Objects within the scene can form parent-child relationships creating a scene graph. Transformations applied to parent nodes cascade down to their children, allowing for complex object grouping and simplified manipulation of related elements. This hierarchy enables intuitive organization of scene elements and efficient transforms through matrix inheritance. The scene itself acts as the root of this hierarchy.
  • Rendering Pipeline: The scene controls a sophisticated rendering pipeline that determines how and when elements are drawn to the screen. This pipeline includes stages for shadow generation, post-processing effects, particle systems, and various render targets. Developers can customize this pipeline through scene rendering groups, layer masks, and custom render targets to achieve complex visual effects while maintaining performance.
  • State Management: The scene maintains the global state of the 3D environment, including active cameras, lights, environmental settings, and physics configuration. This centralized state management provides a single source of truth for the rendering system and ensures consistent behavior across the application.
  • Resource Management: The scene helps coordinate resource loading, tracking, and disposal. It provides mechanisms for asynchronous asset loading, texture management, and garbage collection of unused resources to prevent memory leaks.
.

Scene Lifecycle

The scene provides several key lifecycle hooks:

// Before render hook - called before each frame render
scene.onBeforeRenderObservable.add(() => {
    // Perform operations before rendering
});

// After render hook - called after each frame render
scene.onAfterRenderObservable.add(() => {
    // Perform operations after rendering
});

// Ready event - called when the scene is fully loaded
scene.onReadyObservable.add(() => {
    // Scene is ready, all resources loaded
});
.

Scene Management

For complex applications, you might manage multiple scenes:

// Create multiple scenes
const gameScene = new BABYLON.Scene(engine);
const uiScene = new BABYLON.Scene(engine);

// Switch between scenes
let activeScene = gameScene;

engine.runRenderLoop(() => {
    activeScene.render();
});

// Function to switch scenes
function switchToUIScene() {
    activeScene = uiScene;
}
.

Scene Graph

The scene maintains a hierarchical graph of all objects:

// Create a parent node
const parentNode = new BABYLON.TransformNode("parent", scene);

// Create child meshes
const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {}, scene);
sphere.parent = parentNode;

const box = BABYLON.MeshBuilder.CreateBox("box", {}, scene);
box.parent = parentNode;

// Transforming the parent affects all children
parentNode.position.y = 2;
parentNode.rotation.y = Math.PI / 4;
.

Scene Optimization

Babylon.js 7 includes enhanced scene optimization tools:

// Activate scene optimizer
const options = new BABYLON.SceneOptimizerOptions();
options.addOptimization(new BABYLON.HardwareScalingOptimization(0, 1));
options.addOptimization(new BABYLON.ParticlesOptimization(0.5));
options.addOptimization(new BABYLON.ShadowsOptimization(0.5));

const optimizer = new BABYLON.SceneOptimizer(scene, options);
optimizer.start();
.

Scene Serialization

Scenes can be serialized to JSON for saving and loading:

// Serialize scene to JSON
const serializedScene = BABYLON.SceneSerializer.Serialize(scene);
const sceneString = JSON.stringify(serializedScene);

// Save to localStorage or server...
localStorage.setItem('savedScene', sceneString);

// Later, load the scene
const loadedScene = new BABYLON.Scene(engine);
const savedData = localStorage.getItem('savedScene');
BABYLON.SceneLoader.Append("", "data:" + savedData, loadedScene);
.

The Render Loop

The render loop is the heartbeat of any Babylon.js application, responsible for continuously updating and rendering the scene at an optimal frame rate. Understanding how to properly implement and manage the render loop is essential for smooth performance.

.

Basic Render Loop

The simplest render loop implementation uses the Engine's runRenderLoop method:

engine.runRenderLoop(() => {
    scene.render();
});

This creates a continuous loop that renders the scene on every frame, automatically synchronized with the browser's refresh rate (typically 60fps) using requestAnimationFrame behind the scenes.

.

Render Loop Architecture

The render loop follows the game loop pattern common in game development, consisting of three primary phases:

  1. Update: Process logic, animations, physics, and other state changes
  1. Render: Draw the current state of the scene to the canvas
  1. Wait: Synchronize with the display refresh rate

Babylon.js handles this pattern automatically but allows customization at each stage.

.

Custom Logic in the Render Loop

You can add custom logic to the render loop for animations, game mechanics, or other time-based updates:

// Track time for smooth animations
let previousTime = 0;

engine.runRenderLoop(() => {
    const currentTime = performance.now();
    const deltaTime = (currentTime - previousTime) / 1000; // Convert to seconds
    previousTime = currentTime;
    
    // Update game objects based on elapsed time
    updateGameObjects(deltaTime);
    
    // Render the scene
    scene.render();
});

function updateGameObjects(deltaTime) {
    // Time-based animation example
    sphere.rotation.y += 1.0 * deltaTime; // Rotate 1 radian per second
    
    // Game logic updates
    updatePlayerPosition(deltaTime);
    checkCollisions();
    updateAI(deltaTime);
}

Using delta time (the time elapsed since the last frame) ensures animations run at consistent speeds regardless of frame rate.

.

Performance Monitoring

Babylon.js provides tools to monitor render loop performance:

// Display FPS counter
scene.debugLayer.show();

// Or track FPS programmatically
engine.runRenderLoop(() => {
    const fps = engine.getFps().toFixed();
    fpsCounter.textContent = fps + " FPS";
    
    scene.render();
});
.

Optimizing the Render Loop

For optimal performance, consider these strategies:

// Conditional rendering - only render when needed
let needsToRender = true;

engine.runRenderLoop(() => {
    if (needsToRender) {
        scene.render();
    }
});

// Pause rendering when tab is inactive
document.addEventListener('visibilitychange', () => {
    if (document.hidden) {
        engine.stopRenderLoop();
    } else {
        engine.runRenderLoop(() => scene.render());
    }
});

// Adaptive quality based on performance
engine.runRenderLoop(() => {
    const currentFps = engine.getFps();
    
    if (currentFps < 30) {
        // Reduce quality to maintain performance
        reduceSceneComplexity();
    }
    
    scene.render();
});
.

Multiple Scenes in the Render Loop

For applications with multiple scenes (e.g., a 3D world and a UI overlay):

engine.runRenderLoop(() => {
    // Render main game scene
    gameScene.render();
    
    // Render UI overlay scene (without clearing the canvas)
    uiScene.autoClear = false; // Don't clear previous scene
    uiScene.render();
});
.

Advanced Timing Control

For more precise control over timing:

// Fixed timestep simulation (useful for physics)
const FIXED_TIMESTEP = 1/60; // 60 updates per second
let accumulator = 0;

engine.runRenderLoop(() => {
    const currentTime = performance.now() / 1000;
    const deltaTime = currentTime - previousTime;
    previousTime = currentTime;
    
    // Add frame time to accumulator
    accumulator += Math.min(deltaTime, 0.1); // Cap delta time to prevent spiral of death
    
    // Run physics at fixed timestep for stability
    while (accumulator >= FIXED_TIMESTEP) {
        updatePhysics(FIXED_TIMESTEP);
        accumulator -= FIXED_TIMESTEP;
    }
    
    // Render at variable framerate
    scene.render();
});

This approach separates physics simulation (which often requires fixed timesteps) from rendering (which can run at variable frame rates). In Babylon.js 7, the render loop has been optimized to reduce overhead and improve performance, particularly on mobile devices and other performance-constrained platforms.

.

Cameras

Cameras define the viewpoint from which your scene is rendered. Babylon.js offers various camera types to suit different application needs, from traditional gaming perspectives to VR experiences.

.

Camera Fundamentals

All cameras share certain core properties:

// Core camera properties
camera.position = new BABYLON.Vector3(0, 5, -10); // Position in 3D space
camera.target = new BABYLON.Vector3(0, 0, 0);    // Look-at target
camera.fov = 0.8;                               // Field of view in radians
camera.minZ = 0.1;                              // Near clipping plane
camera.maxZ = 1000;                             // Far clipping plane

Field of View (FOV) controls the camera's viewing angle, measured in radians. A wider FOV (higher value) creates a fisheye-like effect showing more of the scene but with more distortion. A narrower FOV (lower value) creates a telescopic effect with less distortion but shows less of the scene. Typical values range from 0.5 to 1.2 radians (approximately 30° to 70°).

Near and Far Clipping Planes define the viewing frustum's boundaries:

  • The Near Clipping Plane (minZ) is the minimum distance from the camera at which objects become visible. Objects closer than this distance won't be rendered. It should be set as large as possible (typically 0.1-1.0) while still including all necessary objects to avoid precision issues.
  • The Far Clipping Plane (maxZ) is the maximum distance from the camera at which objects are still visible. Objects beyond this distance won't be rendered. Setting this value appropriately (typically 100-2000) can improve performance and depth buffer precision.
.

ArcRotateCamera

The ArcRotateCamera is one of the most versatile and widely used cameras in Babylon.js. It orbits around a target point, making it ideal for object viewing, 3D modeling applications, and orbital gameplay.

// Create ArcRotateCamera: name, alpha, beta, radius, target, scene
const camera = new BABYLON.ArcRotateCamera("camera", 
    Math.PI / 2, // Alpha (horizontal rotation)
    Math.PI / 4, // Beta (vertical angle)
    10,          // Radius (distance from target)
    new BABYLON.Vector3(0, 0, 0), // Target
    scene);

// Enable camera controls
camera.attachControl(canvas, true);

// Configure mouse/touch behavior
camera.inputs.attached.mousewheel.detachControl(canvas); // Disable zoom with mouse wheel
camera.inputs.addMouseWheel();                           // Re-add with custom settings
camera.wheelPrecision = 50;                              // Lower value = faster zoom

// Constrain camera movement
camera.lowerRadiusLimit = 5;   // Minimum zoom distance
camera.upperRadiusLimit = 20;  // Maximum zoom distance
camera.lowerBetaLimit = 0.1;   // Limit vertical rotation (lower)
camera.upperBetaLimit = Math.PI / 2.2; // Limit vertical rotation (upper)

// Camera inertia (smooth movement)
camera.inertia = 0.7; // Higher = more inertia

// Adjust rotation speed
camera.angularSensibilityX = 500; // Horizontal rotation sensitivity
camera.angularSensibilityY = 500; // Vertical rotation sensitivity
.

ArcRotateCamera Animation

Animate the camera for cinematic effects or guided tours:

// Animate the camera orbit
let alpha = 0;
scene.onBeforeRenderObservable.add(() => {
    alpha += 0.01;
    camera.alpha = alpha;
});

// Or use the animation system
const rotationAnimation = new BABYLON.Animation(
    "cameraRotation",
    "alpha",
    30, // frames per second
    BABYLON.Animation.ANIMATIONTYPE_FLOAT,
    BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);

const keyFrames = [];
keyFrames.push({ frame: 0, value: 0 });
keyFrames.push({ frame: 120, value: Math.PI * 2 });
rotationAnimation.setKeys(keyFrames);

camera.animations = [rotationAnimation];
scene.beginAnimation(camera, 0, 120, true); // true = loop
.

ArcRotateCamera Advanced Features

// Auto-rotation when idle
camera.useAutoRotationBehavior = true;
camera.autoRotationBehavior.idleRotationSpeed = 0.2;
camera.autoRotationBehavior.idleRotationWaitTime = 3000; // ms

// Bouncing behavior when hitting limits
camera.useBouncingBehavior = true;
camera.bouncingBehavior.transitionDuration = 150; // ms
.

FreeCamera

The FreeCamera (or Universal Camera) provides first-person movement, ideal for exploration and FPS-style games:

// Create FreeCamera: name, position, scene
const camera = new BABYLON.FreeCamera("camera", 
    new BABYLON.Vector3(0, 2, -10), // Position
    scene);

// Set camera direction
camera.setTarget(BABYLON.Vector3.Zero());

// Enable controls
camera.attachControl(canvas, true);

// Configure keyboard controls
camera.keysUp = [87];    // W key
camera.keysDown = [83];  // S key
camera.keysLeft = [65];  // A key
camera.keysRight = [68]; // D key

// Adjust movement speed
camera.speed = 0.5;
camera.angularSensibility = 1000; // Mouse look sensitivity

// Add WASD movement for FPS style controls
camera.inputs.addKeyboard();
.

FollowCamera

The FollowCamera tracks a target object while maintaining a specified offset, perfect for third-person games:

// Create target object
const player = BABYLON.MeshBuilder.CreateBox("player", {size: 1}, scene);

// Create FollowCamera
const camera = new BABYLON.FollowCamera("followCam", 
    new BABYLON.Vector3(0, 10, -10), // Initial position
    scene);

// Configure follow behavior
camera.lockedTarget = player;      // Target to follow
camera.radius = 10;                // Distance from target
camera.heightOffset = 5;           // Height above target
camera.rotationOffset = 180;       // Rotation around target in degrees
camera.cameraAcceleration = 0.05;  // Camera acceleration
camera.maxCameraSpeed = 10;        // Maximum speed of camera

// Animate the player to see the camera follow
scene.onBeforeRenderObservable.add(() => {
    player.position.x = Math.sin(Date.now() / 1000) * 5;
});
.

VR and XR Cameras

Babylon.js 7 includes enhanced support for Virtual Reality and Augmented Reality through the WebXR standard:

// Create default experience helper
const xrHelper = await scene.createDefaultXRExperienceAsync({
    floorMeshes: [ground] // Optional meshes to use as floor reference
});

// Check if XR is available
if (xrHelper.isSupported) {
    // Create button for VR entry
    const vrButton = document.createElement("button");
    vrButton.textContent = "Enter VR";
    vrButton.style.position = "absolute";
    vrButton.style.bottom = "10px";
    vrButton.style.right = "10px";
    document.body.appendChild(vrButton);
    
    vrButton.addEventListener("click", async () => {
        await xrHelper.enterXRAsync("immersive-vr", "local-floor");
    });
}
.

Camera Input Management

Customize camera controls for different devices and user preferences:

// Remove default inputs
camera.inputs.clear();

// Add only the inputs you need
camera.inputs.add(new BABYLON.ArcRotateCameraKeyboardMoveInput());
camera.inputs.add(new BABYLON.ArcRotateCameraMouseWheelInput());
camera.inputs.add(new BABYLON.ArcRotateCameraPointersInput());

// Create custom camera controls
class CustomCameraInput implements BABYLON.ICameraInput<BABYLON.ArcRotateCamera> {
    camera: BABYLON.ArcRotateCamera;
    
    getClassName(): string {
        return "CustomCameraInput";
    }
    
    getSimpleName(): string {
        return "custom";
    }
    
    attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
        // Custom control logic here
    }
    
    detachControl(element: HTMLElement): void {
        // Clean up event listeners
    }
}

// Add custom input
camera.inputs.add(new CustomCameraInput());

Babylon.js 7 includes enhanced camera features for better control and performance, making cameras more versatile across different device capabilities.

.

Physics in Babylon.js

Babylon.js supports realistic physics simulation through multiple physics engines. This allows you to create interactive environments with gravity, collisions, forces, and constraints that behave according to physical laws.

.

Physics Engines Overview

Babylon.js provides several physics engine options through a plugin architecture:

  • Cannon.js: Lightweight physics engine with good performance-to-accuracy balance
  • Oimo.js: Fast physics engine optimized for performance over accuracy
  • Ammo.js: Powerful engine (WebAssembly port of Bullet Physics) with advanced features
  • Havok: Commercial-grade physics with premium performance (requires license)

Each engine offers different trade-offs between performance, accuracy, feature set, and ease of use. For most projects, Cannon.js provides a good balance and is the recommended starting point.

// Import the physics engine (Cannon.js example)
// In HTML: <script src="https://cdn.babylonjs.com/cannon.js"></script>
// Or in modules: import * as CANNON from 'cannon';

// Enable physics with Cannon.js
const gravityVector = new BABYLON.Vector3(0, -9.81, 0);
const physicsPlugin = new BABYLON.CannonJSPlugin();
scene.enablePhysics(gravityVector, physicsPlugin);
.

Setting Up Physics

Configure the physics environment for your scene:

// Setup physics with Ammo.js
// First, ensure Ammo.js is loaded
// <script src="https://cdn.babylonjs.com/ammo.js"></script>

// Initialize Ammo
await Ammo();

// Create physics plugin
const gravityVector = new BABYLON.Vector3(0, -9.81, 0);
const physicsPlugin = new BABYLON.AmmoJSPlugin();

// Enable physics in the scene
scene.enablePhysics(gravityVector, physicsPlugin);

// Configure physics engine properties
scene.getPhysicsEngine().setSubTimeStep(0.01); // For more accurate simulation

// Set global collision margin (Ammo.js specific)
physicsPlugin.setDefaultNonContactCustomMaterial({
    friction: 0.3,
    restitution: 0.3
});
.

Physics Bodies

Add physics properties to meshes to make them interact with the physics world:

// Create a ground plane with physics
const ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 50, height: 50}, scene);
ground.physicsImpostor = new BABYLON.PhysicsImpostor(
    ground,
    BABYLON.PhysicsImpostor.BoxImpostor, // Use box collision for ground
    { mass: 0, restitution: 0.3, friction: 0.3 }, // mass 0 = static object
    scene
);

// Create a dynamic sphere
const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 2}, scene);
sphere.position.y = 10; // Start above ground
sphere.physicsImpostor = new BABYLON.PhysicsImpostor(
    sphere,
    BABYLON.PhysicsImpostor.SphereImpostor,
    { mass: 1, restitution: 0.7, friction: 0.1 }, // mass > 0 = dynamic object
    scene
);

// Create a dynamic box
const box = BABYLON.MeshBuilder.CreateBox("box", {size: 2}, scene);
box.position = new BABYLON.Vector3(0, 15, 0);
box.physicsImpostor = new BABYLON.PhysicsImpostor(
    box,
    BABYLON.PhysicsImpostor.BoxImpostor,
    { mass: 2, restitution: 0.2, friction: 0.4 },
    scene
);
.

Impostor Types

Different collision shapes for different needs:

Babylon.js provides several impostor types for different collision needs:

  • BoxImpostor: Simple box collision - efficient for rectangular objects
  • SphereImpostor: Spherical collision - most efficient, good for round objects
  • CylinderImpostor: Cylindrical collision - for tubes, pillars, etc.
  • MeshImpostor: Uses actual mesh geometry - most accurate but computationally expensive
  • HeightmapImpostor: Efficient for terrain - uses height data for collision
  • ConvexHullImpostor: Creates simplified collision from mesh vertices - good balance
  • ParticleImpostor: Point with mass but no rotation - for simple particles
  • NoImpostor: For compound objects with child impostors

For performance, always use the simplest collision shape that adequately represents your object.

// Complex mesh with accurate collision
const complexMesh = BABYLON.MeshBuilder.CreateTorusKnot("tk", {}, scene);
complexMesh.position.y = 10;

// Options for different collision types
// 1. Exact mesh collision (expensive)
complexMesh.physicsImpostor = new BABYLON.PhysicsImpostor(
    complexMesh,
    BABYLON.PhysicsImpostor.MeshImpostor,
    { mass: 3, restitution: 0.4 },
    scene
);

// 2. Convex hull (better performance)
complexMesh.physicsImpostor = new BABYLON.PhysicsImpostor(
    complexMesh,
    BABYLON.PhysicsImpostor.ConvexHullImpostor,
    { mass: 3, restitution: 0.4 },
    scene
);

// 3. Simple approximation (best performance)
complexMesh.physicsImpostor = new BABYLON.PhysicsImpostor(
    complexMesh,
    BABYLON.PhysicsImpostor.SphereImpostor,
    { mass: 3, restitution: 0.4 },
    scene
);
.

Compound Bodies

Create complex physics objects from multiple shapes:

// Create a compound object (e.g., a dumbbell)
const dumbbell = new BABYLON.Mesh("dumbbell", scene);

// Create the middle bar
const bar = BABYLON.MeshBuilder.CreateCylinder("bar", {
    height: 5,
    diameter: 0.5
}, scene);

// Create the weights
const leftWeight = BABYLON.MeshBuilder.CreateSphere("leftWeight", {
    diameter: 2
}, scene);
leftWeight.position.x = -2.5;

const rightWeight = BABYLON.MeshBuilder.CreateSphere("rightWeight", {
    diameter: 2
}, scene);
rightWeight.position.x = 2.5;

// Make the parts children of the main mesh
bar.parent = dumbbell;
leftWeight.parent = dumbbell;
rightWeight.parent = dumbbell;

// Position the dumbbell
dumbbell.position.y = 10;

// Create compound physics impostor
dumbbell.physicsImpostor = new BABYLON.PhysicsImpostor(
    dumbbell,
    BABYLON.PhysicsImpostor.NoImpostor, // Parent has no direct impostor
    { mass: 10 },
    scene
);

// Add child impostors
bar.physicsImpostor = new BABYLON.PhysicsImpostor(
    bar,
    BABYLON.PhysicsImpostor.CylinderImpostor,
    { mass: 0 }, // Child masses are ignored when part of compound
    scene
);

leftWeight.physicsImpostor = new BABYLON.PhysicsImpostor(
    leftWeight,
    BABYLON.PhysicsImpostor.SphereImpostor,
    { mass: 0 },
    scene
);

rightWeight.physicsImpostor = new BABYLON.PhysicsImpostor(
    rightWeight,
    BABYLON.PhysicsImpostor.SphereImpostor,
    { mass: 0 },
    scene
);
.

Forces and Impulses

Apply physics forces to objects in your scene:

// Apply a continuous force (newtons)
sphere.physicsImpostor.applyForce(
    new BABYLON.Vector3(0, 0, 10), // Force direction and magnitude
    sphere.getAbsolutePosition() // Application point
);

// Apply an impulse (instantaneous force)
box.physicsImpostor.applyImpulse(
    new BABYLON.Vector3(5, 0, 0), // Impulse direction and magnitude
    box.getAbsolutePosition() // Application point
);

// Set linear velocity directly
sphere.physicsImpostor.setLinearVelocity(new BABYLON.Vector3(0, 5, 0));

// Set angular velocity (rotation)
box.physicsImpostor.setAngularVelocity(new BABYLON.Vector3(0, 2, 0));

// Apply a torque (rotational force)
box.physicsImpostor.applyTorque(new BABYLON.Vector3(0, 10, 0));
.

Interactive Forces

Create user-controlled physics interactions:

// Add force on key press
scene.onKeyboardObservable.add((kbInfo) => {
    if (kbInfo.type === BABYLON.KeyboardEventTypes.KEYDOWN) {
        switch (kbInfo.event.key) {
            case "ArrowUp":
                sphere.physicsImpostor.applyImpulse(
                    new BABYLON.Vector3(0, 0, -10),
                    sphere.getAbsolutePosition()
                );
                break;
            case "ArrowDown":
                sphere.physicsImpostor.applyImpulse(
                    new BABYLON.Vector3(0, 0, 10),
                    sphere.getAbsolutePosition()
                );
                break;
            case "ArrowLeft":
                sphere.physicsImpostor.applyImpulse(
                    new BABYLON.Vector3(-10, 0, 0),
                    sphere.getAbsolutePosition()
                );
                break;
            case "ArrowRight":
                sphere.physicsImpostor.applyImpulse(
                    new BABYLON.Vector3(10, 0, 0),
                    sphere.getAbsolutePosition()
                );
                break;
            case " ": // Space key
                sphere.physicsImpostor.applyImpulse(
                    new BABYLON.Vector3(0, 10, 0), // Jump
                    sphere.getAbsolutePosition()
                );
                break;
        }
    }
});
.

Constraints and Joints

Connect physics objects with various types of joints:

// Create a hinge joint (like a door hinge)
const hingePivot = new BABYLON.Vector3(0, 5, 0);
const hingeAxis = new BABYLON.Vector3(0, 1, 0); // Rotate around Y axis
const hingeJoint = new BABYLON.PhysicsJoint(
    BABYLON.PhysicsJoint.HingeJoint, {
        mainPivot: hingePivot,
        connectedPivot: new BABYLON.Vector3(0, 0, 0),
        mainAxis: hingeAxis,
        connectedAxis: hingeAxis
    }
);

// Connect a box to a fixed anchor point
const anchor = BABYLON.MeshBuilder.CreateBox("anchor", {size: 0.5}, scene);
anchor.position = hingePivot;
anchor.physicsImpostor = new BABYLON.PhysicsImpostor(
    anchor,
    BABYLON.PhysicsImpostor.BoxImpostor,
    { mass: 0 }, // Fixed point (zero mass)
    scene
);

// Create a panel to act as a door
const door = BABYLON.MeshBuilder.CreateBox("door", {
    width: 0.5,
    height: 6,
    depth: 4
}, scene);
door.position = new BABYLON.Vector3(2, 5, 0);
door.physicsImpostor = new BABYLON.PhysicsImpostor(
    door,
    BABYLON.PhysicsImpostor.BoxImpostor,
    { mass: 5, friction: 0.5 },
    scene
);

// Connect the door to the anchor with a hinge joint
anchor.physicsImpostor.addJoint(door.physicsImpostor, hingeJoint);
.

Additional Joint Types

Other types of physical joints and constraints:

// Ball-and-socket joint (allows rotation in all directions)
const ballJoint = new BABYLON.PhysicsJoint(
    BABYLON.PhysicsJoint.BallAndSocketJoint, {
        mainPivot: new BABYLON.Vector3(0, 0, 0),
        connectedPivot: new BABYLON.Vector3(0, 2, 0)
    }
);

// Distance joint (maintains fixed distance between objects)
const distanceJoint = new BABYLON.PhysicsJoint(
    BABYLON.PhysicsJoint.DistanceJoint, {
        maxDistance: 5 // Maximum distance constraint
    }
);

// Prismatic joint (allows movement along one axis only)
const prismaticJoint = new BABYLON.PhysicsJoint(
    BABYLON.PhysicsJoint.PrismaticJoint, {
        mainAxis: new BABYLON.Vector3(1, 0, 0), // Movement along X axis
        connectedAxis: new BABYLON.Vector3(1, 0, 0)
    }
);

// Slider joint with limits
const sliderJoint = new BABYLON.PhysicsJoint(
    BABYLON.PhysicsJoint.SliderJoint, {
        mainAxis: new BABYLON.Vector3(0, 1, 0),
        connectedAxis: new BABYLON.Vector3(0, 1, 0)
    }
);
sliderJoint.setLimit(1, 5); // Min and max distance
.

Spring Systems

Create spring-like behavior with physics:

// Create a spring joint
const spring = new BABYLON.PhysicsJoint(
    BABYLON.PhysicsJoint.SpringJoint, {
        length: 5,          // Rest length
        stiffness: 50,      // Spring strength
        damping: 0.5,       // Damping factor
        collision: true     // Allow connected bodies to collide
    }
);

// Create anchor point
const springAnchor = BABYLON.MeshBuilder.CreateSphere("anchor", {diameter: 1}, scene);
springAnchor.position.y = 15;
springAnchor.physicsImpostor = new BABYLON.PhysicsImpostor(
    springAnchor,
    BABYLON.PhysicsImpostor.SphereImpostor,
    { mass: 0 }, // Fixed position
    scene
);

// Create suspended object
const bob = BABYLON.MeshBuilder.CreateSphere("bob", {diameter: 2}, scene);
bob.position.y = 10;
bob.physicsImpostor = new BABYLON.PhysicsImpostor(
    bob,
    BABYLON.PhysicsImpostor.SphereImpostor,
    { mass: 2, restitution: 0.6 },
    scene
);

// Connect with spring joint
springAnchor.physicsImpostor.addJoint(bob.physicsImpostor, spring);

// Visualize the spring connection with a line
const springLine = BABYLON.MeshBuilder.CreateLines("springLine", {
    points: [springAnchor.position, bob.position],
    updatable: true
}, scene);

// Update the line in the render loop
scene.onBeforeRenderObservable.add(() => {
    springLine = BABYLON.MeshBuilder.CreateLines("springLine", {
        points: [springAnchor.position, bob.position],
        instance: springLine
    });
});
.

Collision Detection & Events

Respond to physics collisions in your scene:

// Add collision event handler
sphere.physicsImpostor.onCollideEvent = (collider, collidedWith) => {
    // collider is the sphere's impostor
    // collidedWith is the other object's impostor
    
    // Change material on collision
    const material = sphere.material;
    material.diffuseColor = new BABYLON.Color3(1, 0, 0); // Turn red
    
    // Reset color after delay
    setTimeout(() => {
        material.diffuseColor = new BABYLON.Color3(1, 1, 1); // Back to white
    }, 500);
    
    // Play sound on collision
    const impactVelocity = collider.getLinearVelocity().length();
    if (impactVelocity > 1) {
        const collisionSound = new BABYLON.Sound(
            "collision",
            "sounds/collision.mp3",
            scene,
            null,
            { volume: Math.min(impactVelocity / 10, 1) }
        );
        collisionSound.play();
    }
};
.

Trigger Volumes

Create non-solid volumes that detect when objects enter them:

// Create a trigger zone
const triggerZone = BABYLON.MeshBuilder.CreateBox("trigger", {
    width: 10,
    height: 5,
    depth: 10
}, scene);
triggerZone.position.y = 2.5;
triggerZone.visibility = 0.2; // Semi-transparent
triggerZone.material = new BABYLON.StandardMaterial("triggerMat", scene);
triggerZone.material.diffuseColor = new BABYLON.Color3(0, 1, 0); // Green
triggerZone.material.alpha = 0.2;

// Make it a trigger volume (non-solid)
triggerZone.physicsImpostor = new BABYLON.PhysicsImpostor(
    triggerZone,
    BABYLON.PhysicsImpostor.BoxImpostor,
    { mass: 0, isTrigger: true },
    scene
);

// Track objects in the trigger zone
const objectsInZone = new Set();

// Handle collisions with the trigger
triggerZone.physicsImpostor.onCollideEvent = (collider, collidedWith) => {
    // Get the mesh associated with the colliding impostor
    const collidingMesh = collidedWith.object;
    
    // Track object entering the zone
    if (!objectsInZone.has(collidingMesh)) {
        objectsInZone.add(collidingMesh);
        console.log(`${collidingMesh.name} entered the trigger zone!`);
        
        // Change trigger color
        triggerZone.material.diffuseColor = new BABYLON.Color3(1, 0, 0);
    }
};

// Check if objects have left the zone
scene.onBeforeRenderObservable.add(() => {
    for (const obj of objectsInZone) {
        // Check if still intersecting
        if (!triggerZone.intersectsMesh(obj)) {
            objectsInZone.delete(obj);
            console.log(`${obj.name} left the trigger zone!`);
            
            // Change back to green if empty
            if (objectsInZone.size === 0) {
                triggerZone.material.diffuseColor = new BABYLON.Color3(0, 1, 0);
            }
        }
    }
});
.

Physics Raycasting

Use raycasting to detect physics objects:

// Cast a ray through the physics world
const origin = new BABYLON.Vector3(0, 10, -10);
const direction = new BABYLON.Vector3(0, -1, 1).normalize();
const length = 20;
const raycastResult = scene.getPhysicsEngine().raycast(
    origin,
    direction,
    length
);

// Check if we hit something
if (raycastResult.hasHit) {
    console.log(`Hit object: ${raycastResult.body.object.name}`);
    console.log(`Hit point: ${raycastResult.hitPointWorld}`);
    console.log(`Hit normal: ${raycastResult.hitNormalWorld}`);
    console.log(`Hit distance: ${raycastResult.hitDistance}`);
}

// Visualize the ray
const rayHelper = new BABYLON.RayHelper(new BABYLON.Ray(origin, direction, length));
rayHelper.show(scene);

// Interactive raycasting with mouse click
scene.onPointerDown = (evt, pickResult) => {
    if (pickResult.hit) {
        // Cast ray from camera through click point
        const ray = scene.createPickingRay(
            scene.pointerX,
            scene.pointerY,
            BABYLON.Matrix.Identity(),
            camera
        );
        
        const raycastResult = scene.getPhysicsEngine().raycast(
            ray.origin,
            ray.direction,
            100
        );
        
        if (raycastResult.hasHit) {
            // Apply force at hit point
            const hitBody = raycastResult.body;
            const hitPoint = raycastResult.hitPointWorld;
            const force = ray.direction.scale(50); // Force strength
            
            hitBody.applyForce(force, hitPoint);
        }
    }
};
.

Character Controllers

Implement physics-based character movement:

// Create a character with capsule physics
const characterHeight = 3;
const characterRadius = 0.5;

// Create body parts
const body = BABYLON.MeshBuilder.CreateCylinder("body", {
    height: characterHeight - 2 * characterRadius,
    diameter: characterRadius * 2
}, scene);

const head = BABYLON.MeshBuilder.CreateSphere("head", {
    diameter: characterRadius * 2
}, scene);
head.position.y = (characterHeight - 2 * characterRadius) / 2;
head.parent = body;

const feet = BABYLON.MeshBuilder.CreateSphere("feet", {
    diameter: characterRadius * 2
}, scene);
feet.position.y = -(characterHeight - 2 * characterRadius) / 2;
feet.parent = body;

// Position the character
body.position.y = characterHeight / 2;

// Add physics with capsule shape (cylinder + sphere caps)
body.physicsImpostor = new BABYLON.PhysicsImpostor(
    body,
    BABYLON.PhysicsImpostor.CylinderImpostor,
    { mass: 1, friction: 0.5, restitution: 0.3 },
    scene
);

// Variables for movement control
const moveDirection = new BABYLON.Vector3();
let isGrounded = false;
const jumpForce = 10;
const moveSpeed = 0.5;

// Input handling for character movement
const keyStatus = {};

scene.onKeyboardObservable.add((kbInfo) => {
    const key = kbInfo.event.key.toLowerCase();
    
    if (kbInfo.type === BABYLON.KeyboardEventTypes.KEYDOWN) {
        keyStatus[key] = true;
    } else if (kbInfo.type === BABYLON.KeyboardEventTypes.KEYUP) {
        keyStatus[key] = false;
    }
});

// Ground detection using raycasting
function checkGrounded() {
    const origin = body.getAbsolutePosition();
    const direction = new BABYLON.Vector3(0, -1, 0);
    const length = characterHeight / 2 + 0.1; // Slightly longer than character radius
    
    const raycastResult = scene.getPhysicsEngine().raycast(
        origin,
        direction,
        length
    );
    
    return raycastResult.hasHit;
}

// Character controller update
scene.onBeforeRenderObservable.add(() => {
    // Check if character is on ground
    isGrounded = checkGrounded();
    
    // Reset movement direction
    moveDirection.setAll(0);
    
    // Calculate move direction from input
    if (keyStatus["w"]) moveDirection.z += 1;
    if (keyStatus["s"]) moveDirection.z -= 1;
    if (keyStatus["a"]) moveDirection.x -= 1;
    if (keyStatus["d"]) moveDirection.x += 1;
    
    // Jump if on ground
    if (keyStatus[" "] && isGrounded) {
        body.physicsImpostor.applyImpulse(
            new BABYLON.Vector3(0, jumpForce, 0),
            body.getAbsolutePosition()
        );
    }
    
    // Normalize and apply movement
    if (moveDirection.length() > 0) {
        moveDirection.normalize();
        
        // Align with camera direction
        const cameraDirection = camera.getTarget().subtract(camera.position);
        cameraDirection.y = 0;
        cameraDirection.normalize();
        
        // Create rotation matrix from camera direction
        const rotationMatrix = BABYLON.Matrix.RotationY(
            Math.atan2(cameraDirection.x, cameraDirection.z)
        );
        
        // Transform movement direction relative to camera
        const transformedDirection = BABYLON.Vector3.TransformNormal(
            moveDirection,
            rotationMatrix
        );
        
        // Apply movement as impulse (only when on ground)
        if (isGrounded) {
            body.physicsImpostor.applyImpulse(
                transformedDirection.scale(moveSpeed),
                body.getAbsolutePosition()
            );
        }
    }
});
.

Advanced Physics Features

Babylon.js 7 includes support for advanced physics simulations:

.

Vehicle Physics

Create physics-based vehicles with wheel constraints:

// This example uses Ammo.js which supports advanced vehicle physics
// Note: Vehicle physics implementation depends on the physics engine used

// Create a vehicle chassis
const chassisWidth = 2;
const chassisHeight = 0.6;
const chassisLength = 4;

const chassis = BABYLON.MeshBuilder.CreateBox("chassis", {
    width: chassisWidth,
    height: chassisHeight,
    depth: chassisLength
}, scene);
chassis.position.y = 4;

// Create wheels
const wheelRadius = 0.4;
const wheelWidth = 0.3;
const wheelPositions = [
    new BABYLON.Vector3(chassisWidth/2, -chassisHeight/2, chassisLength/2 - wheelRadius), // front right
    new BABYLON.Vector3(-chassisWidth/2, -chassisHeight/2, chassisLength/2 - wheelRadius), // front left
    new BABYLON.Vector3(chassisWidth/2, -chassisHeight/2, -chassisLength/2 + wheelRadius), // rear right
    new BABYLON.Vector3(-chassisWidth/2, -chassisHeight/2, -chassisLength/2 + wheelRadius) // rear left
];

const wheels = [];
for (let i = 0; i < 4; i++) {
    const wheel = BABYLON.MeshBuilder.CreateCylinder(`wheel${i}`, {
        diameter: wheelRadius * 2,
        height: wheelWidth,
        tessellation: 24
    }, scene);
    wheel.rotation.z = Math.PI / 2; // Rotate to correct orientation
    wheel.position = chassis.position.add(wheelPositions[i]);
    wheels.push(wheel);
}

// Add physics
chassis.physicsImpostor = new BABYLON.PhysicsImpostor(
    chassis,
    BABYLON.PhysicsImpostor.BoxImpostor,
    { mass: 800, friction: 0.5, restitution: 0.2 },
    scene
);

// Configure vehicle physics (Ammo.js specific)
// Note: Implementation details vary by physics engine
if (scene.getPhysicsEngine().getPhysicsPluginName() === "AmmoJSPlugin") {
    const vehicle = new BABYLON.AmmoJSPlugin.Vehicle(chassis, scene);
    
    // Add wheels to vehicle
    for (let i = 0; i < 4; i++) {
        const isFront = i < 2;
        vehicle.addWheel(
            wheels[i],
            wheelPositions[i],
            new BABYLON.Vector3(0, -1, 0),  // Down direction
            new BABYLON.Vector3(0, 0, 1),   // Forward direction
            0.6,  // Suspension rest length
            wheelRadius,
            isFront // Steering wheel?
        );
    }
    
    // Configure vehicle properties
    vehicle.setSteeringValue(0.0);  // Initial steering
    vehicle.applyEngineForce(0.0);  // Initial engine force
    vehicle.setBrake(0.0);          // Initial brake force
    
    // Vehicle control with keyboard
    scene.onKeyboardObservable.add((kbInfo) => {
        const key = kbInfo.event.key;
        const isDown = kbInfo.type === BABYLON.KeyboardEventTypes.KEYDOWN;
        
        switch (key) {
            case "w":
                vehicle.applyEngineForce(isDown ? 1000 : 0);
                break;
            case "s":
                vehicle.applyEngineForce(isDown ? -500 : 0);
                break;
            case "a":
                vehicle.setSteeringValue(isDown ? 0.3 : 0);
                break;
            case "d":
                vehicle.setSteeringValue(isDown ? -0.3 : 0);
                break;
            case " ":
                vehicle.setBrake(isDown ? 50 : 0);
                break;
        }
    });
}
.

Ragdoll Physics

Create articulated character physics for realistic reactions:

// Create a simple ragdoll character
function createRagdoll(position) {
    // Create body parts
    const parts = {};
    
    // Torso
    parts.torso = BABYLON.MeshBuilder.CreateBox("torso", {
        width: 1, height: 1.5, depth: 0.5
    }, scene);
    parts.torso.position = position.clone();
    
    // Head
    parts.head = BABYLON.MeshBuilder.CreateSphere("head", {
        diameter: 0.7
    }, scene);
    parts.head.position = position.clone();
    parts.head.position.y += 1;
    
    // Upper arms
    parts.leftUpperArm = BABYLON.MeshBuilder.CreateCylinder("leftUpperArm", {
        height: 0.8, diameter: 0.25
    }, scene);
    parts.leftUpperArm.rotation.z = Math.PI / 2;
    parts.leftUpperArm.position = position.clone();
    parts.leftUpperArm.position.x -= 0.8;
    parts.leftUpperArm.position.y += 0.4;
    
    parts.rightUpperArm = BABYLON.MeshBuilder.CreateCylinder("rightUpperArm", {
        height: 0.8, diameter: 0.25
    }, scene);
    parts.rightUpperArm.rotation.z = Math.PI / 2;
    parts.rightUpperArm.position = position.clone();
    parts.rightUpperArm.position.x += 0.8;
    parts.rightUpperArm.position.y += 0.4;
    
    // Lower arms
    parts.leftLowerArm = BABYLON.MeshBuilder.CreateCylinder("leftLowerArm", {
        height: 0.8, diameter: 0.2
    }, scene);
    parts.leftLowerArm.rotation.z = Math.PI / 2;
    parts.leftLowerArm.position = position.clone();
    parts.leftLowerArm.position.x -= 1.6;
    parts.leftLowerArm.position.y += 0.4;
    
    parts.rightLowerArm = BABYLON.MeshBuilder.CreateCylinder("rightLowerArm", {
        height: 0.8, diameter: 0.2
    }, scene);
    parts.rightLowerArm.rotation.z = Math.PI / 2;
    parts.rightLowerArm.position = position.clone();
    parts.rightLowerArm.position.x += 1.6;
    parts.rightLowerArm.position.y += 0.4;
    
    // Upper legs
    parts.leftUpperLeg = BABYLON.MeshBuilder.CreateCylinder("leftUpperLeg", {
        height: 1, diameter: 0.3
    }, scene);
    parts.leftUpperLeg.position = position.clone();
    parts.leftUpperLeg.position.x -= 0.3;
    parts.leftUpperLeg.position.y -= 1;
    
    parts.rightUpperLeg = BABYLON.MeshBuilder.CreateCylinder("rightUpperLeg", {
        height: 1, diameter: 0.3
    }, scene);
    parts.rightUpperLeg.position = position.clone();
    parts.rightUpperLeg.position.x += 0.3;
    parts.rightUpperLeg.position.y -= 1;
    
    // Lower legs
    parts.leftLowerLeg = BABYLON.MeshBuilder.CreateCylinder("leftLowerLeg", {
        height: 1, diameter: 0.25
    }, scene);
    parts.leftLowerLeg.position = position.clone();
    parts.leftLowerLeg.position.x -= 0.3;
    parts.leftLowerLeg.position.y -= 2;
    
    parts.rightLowerLeg = BABYLON.MeshBuilder.CreateCylinder("rightLowerLeg", {
        height: 1, diameter: 0.25
    }, scene);
    parts.rightLowerLeg.position = position.clone();
    parts.rightLowerLeg.position.x += 0.3;
    parts.rightLowerLeg.position.y -= 2;
    
    // Add physics impostors to all parts
    for (const [name, part] of Object.entries(parts)) {
        part.physicsImpostor = new BABYLON.PhysicsImpostor(
            part,
            BABYLON.PhysicsImpostor.BoxImpostor, // Use box for better performance
            { mass: name === "torso" ? 3 : 1, friction: 0.5, restitution: 0.3 },
            scene
        );
    }
    
    // Create joints to connect the body parts
    // Neck joint
    const neckJoint = new BABYLON.PhysicsJoint(
        BABYLON.PhysicsJoint.BallAndSocketJoint, {
            mainPivot: new BABYLON.Vector3(0, 0.75, 0),
            connectedPivot: new BABYLON.Vector3(0, -0.35, 0)
        }
    );
    parts.torso.physicsImpostor.addJoint(parts.head.physicsImpostor, neckJoint);
    
    // Shoulder joints
    const leftShoulderJoint = new BABYLON.PhysicsJoint(
        BABYLON.PhysicsJoint.BallAndSocketJoint, {
            mainPivot: new BABYLON.Vector3(-0.5, 0.5, 0),
            connectedPivot: new BABYLON.Vector3(0.4, 0, 0)
        }
    );
    parts.torso.physicsImpostor.addJoint(parts.leftUpperArm.physicsImpostor, leftShoulderJoint);
    
    const rightShoulderJoint = new BABYLON.PhysicsJoint(
        BABYLON.PhysicsJoint.BallAndSocketJoint, {
            mainPivot: new BABYLON.Vector3(0.5, 0.5, 0),
            connectedPivot: new BABYLON.Vector3(-0.4, 0, 0)
        }
    );
    parts.torso.physicsImpostor.addJoint(parts.rightUpperArm.physicsImpostor, rightShoulderJoint);
    
    // Elbow joints
    const leftElbowJoint = new BABYLON.PhysicsJoint(
        BABYLON.PhysicsJoint.HingeJoint, {
            mainPivot: new BABYLON.Vector3(-0.4, 0, 0),
            connectedPivot: new BABYLON.Vector3(0.4, 0, 0),
            mainAxis: new BABYLON.Vector3(0, 0, 1),
            connectedAxis: new BABYLON.Vector3(0, 0, 1)
        }
    );
    parts.leftUpperArm.physicsImpostor.addJoint(parts.leftLowerArm.physicsImpostor, leftElbowJoint);
    
    const rightElbowJoint = new BABYLON.PhysicsJoint(
        BABYLON.PhysicsJoint.HingeJoint, {
            mainPivot: new BABYLON.Vector3(0.4, 0, 0),
            connectedPivot: new BABYLON.Vector3(-0.4, 0, 0),
            mainAxis: new BABYLON.Vector3(0, 0, 1),
            connectedAxis: new BABYLON.Vector3(0, 0, 1)
        }
    );
    parts.rightUpperArm.physicsImpostor.addJoint(parts.rightLowerArm.physicsImpostor, rightElbowJoint);
    
    // Hip joints
    const leftHipJoint = new BABYLON.PhysicsJoint(
        BABYLON.PhysicsJoint.BallAndSocketJoint, {
            mainPivot: new BABYLON.Vector3(-0.3, -0.75, 0),
            connectedPivot: new BABYLON.Vector3(0, 0.5, 0)
        }
    );
    parts.torso.physicsImpostor.addJoint(parts.leftUpperLeg.physicsImpostor, leftHipJoint);
    
    const rightHipJoint = new BABYLON.PhysicsJoint(
        BABYLON.PhysicsJoint.BallAndSocketJoint, {
            mainPivot: new BABYLON.Vector3(0.3, -0.75, 0),
            connectedPivot: new BABYLON.Vector3(0, 0.5, 0)
        }
    );
    parts.torso.physicsImpostor.addJoint(parts.rightUpperLeg.physicsImpostor, rightHipJoint);
    
    // Knee joints
    const leftKneeJoint = new BABYLON.PhysicsJoint(
        BABYLON.PhysicsJoint.HingeJoint, {
            mainPivot: new BABYLON.Vector3(0, -0.5, 0),
            connectedPivot: new BABYLON.Vector3(0, 0.5, 0),
            mainAxis: new BABYLON.Vector3(1, 0, 0),
            connectedAxis: new BABYLON.Vector3(1, 0, 0)
        }
    );
    parts.leftUpperLeg.physicsImpostor.addJoint(parts.leftLowerLeg.physicsImpostor, leftKneeJoint);
    
    const rightKneeJoint = new BABYLON.PhysicsJoint(
        BABYLON.PhysicsJoint.HingeJoint, {
            mainPivot: new BABYLON.Vector3(0, -0.5, 0),
            connectedPivot: new BABYLON.Vector3(0, 0.5, 0),
            mainAxis: new BABYLON.Vector3(1, 0, 0),
            connectedAxis: new BABYLON.Vector3(1, 0, 0)
        }
    );
    parts.rightUpperLeg.physicsImpostor.addJoint(parts.rightLowerLeg.physicsImpostor, rightKneeJoint);
    
    return parts;
}

// Create a ragdoll
const ragdoll = createRagdoll(new BABYLON.Vector3(0, 10, 0));

// Add button to apply explosion force
const explodeButton = document.createElement("button");
explodeButton.textContent = "Apply Force";
explodeButton.style.position = "absolute";
explodeButton.style.top = "10px";
explodeButton.style.left = "10px";
document.body.appendChild(explodeButton);

explodeButton.addEventListener("click", () => {
    // Apply random impulse to torso
    ragdoll.torso.physicsImpostor.applyImpulse(
        new BABYLON.Vector3(
            (Math.random() - 0.5) * 20,
            Math.random() * 10,
            (Math.random() - 0.5) * 20
        ),
        ragdoll.torso.getAbsolutePosition()
    );
});
.

Performance Optimization

Optimize physics for better performance:

// Configure physics engine for performance
const physicsEngine = scene.getPhysicsEngine();

// Set physics timestep (smaller = more accurate but slower)
physicsEngine.setTimeStep(1/60);

// Enable sub-stepping for more stable simulation
physicsEngine.setSubTimeStep(2); // 2 sub-steps per frame

// Performance techniques

// 1. Use simpler collision shapes
// Sphere and box impostors are much faster than mesh impostors
const complexMesh = BABYLON.MeshBuilder.CreateTorusKnot("complex", {}, scene);
complexMesh.position.y = 5;

// Use a bounding box instead of exact mesh shape
const boundingInfo = complexMesh.getBoundingInfo();
const dimensions = boundingInfo.boundingBox.extendSize.scale(2);
complexMesh.physicsImpostor = new BABYLON.PhysicsImpostor(
    complexMesh,
    BABYLON.PhysicsImpostor.BoxImpostor,
    { mass: 1, friction: 0.5, restitution: 0.3 },
    scene
);

// 2. Use sleep states for static objects
ground.physicsImpostor.sleep(); // Put a static object to sleep

// 3. Group small objects into compound objects
const smallObjectsParent = new BABYLON.Mesh("smallObjects", scene);
for (let i = 0; i < 10; i++) {
    const small = BABYLON.MeshBuilder.CreateBox(`small${i}`, {size: 0.2}, scene);
    small.position.x = i * 0.3 - 1.5;
    small.position.y = 2;
    small.parent = smallObjectsParent;
}

// Treat them as one physics object
smallObjectsParent.physicsImpostor = new BABYLON.PhysicsImpostor(
    smallObjectsParent,
    BABYLON.PhysicsImpostor.BoxImpostor,
    { mass: 2, friction: 0.5 },
    scene
);

// 4. Disable physics for distant objects
// Create a function to check distance from camera
const maxPhysicsDistance = 50; // Maximum distance for active physics

scene.onBeforeRenderObservable.add(() => {
    // Get active camera position
    const cameraPosition = scene.activeCamera.position;
    
    // Check all physics objects
    for (const mesh of scene.meshes) {
        if (mesh.physicsImpostor) {
            const distance = BABYLON.Vector3.Distance(mesh.position, cameraPosition);
            
            // Enable/disable physics based on distance
            if (distance > maxPhysicsDistance) {
                if (!mesh.physicsImpostor.isDisposed) {
                    // Store current state before disabling
                    mesh._physicsEnabled = false;
                    mesh._linearVelocity = mesh.physicsImpostor.getLinearVelocity().clone();
                    mesh._angularVelocity = mesh.physicsImpostor.getAngularVelocity().clone();
                    mesh.physicsImpostor.sleep(); // Put to sleep to reduce computation
                }
            } else if (mesh.hasOwnProperty("_physicsEnabled") && mesh._physicsEnabled === false) {
                // Re-enable physics and restore velocity
                mesh._physicsEnabled = true;
                mesh.physicsImpostor.wakeUp();
                if (mesh._linearVelocity) {
                    mesh.physicsImpostor.setLinearVelocity(mesh._linearVelocity);
                }
                if (mesh._angularVelocity) {
                    mesh.physicsImpostor.setAngularVelocity(mesh._angularVelocity);
                }
            }
        }
    }
});
.

Soft Bodies Physics

Some physics engines like Ammo.js support soft body physics for cloth, jelly-like objects, and deformable surfaces:

// Note: Soft body support depends on the physics engine
// This example uses Ammo.js which has soft body capabilities

// First ensure Ammo.js is properly loaded with soft body support
// <script src="https://cdn.babylonjs.com/ammo.js"></script>

// Create a soft body cloth
async function createSoftBodyCloth() {
    // Wait for Ammo initialization
    await Ammo();
    
    // Create a cloth mesh (plane with subdivisions)
    const cloth = BABYLON.MeshBuilder.CreateGround("cloth", {
        width: 5,
        height: 5,
        subdivisions: 20
    }, scene);
    cloth.position.y = 5;
    
    // Create material for visualization
    const clothMaterial = new BABYLON.StandardMaterial("clothMat", scene);
    clothMaterial.diffuseTexture = new BABYLON.Texture("textures/fabric.jpg", scene);
    clothMaterial.backFaceCulling = false;
    cloth.material = clothMaterial;
    
    // Physics initialization with Ammo.js
    scene.enablePhysics(new BABYLON.Vector3(0, -9.81, 0), new BABYLON.AmmoJSPlugin());
    
    // Configure soft body parameters
    const softBodyHelperOptions = {
        fixedPoints: [0, 1, 20, 21], // Fix the top corners (depends on subdivision)
        mass: 1,
        pressure: 100,
        stiffness: 0.1,
        friction: 0.8
    };
    
    // Create the soft body impostor with Ammo.js
    cloth.physicsImpostor = new BABYLON.PhysicsImpostor(
        cloth,
        BABYLON.PhysicsImpostor.SoftImpostor,
        softBodyHelperOptions,
        scene
    );
    
    // Add collision with other objects
    const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {
        diameter: 1
    }, scene);
    sphere.position = new BABYLON.Vector3(0, 2, 0);
    sphere.physicsImpostor = new BABYLON.PhysicsImpostor(
        sphere,
        BABYLON.PhysicsImpostor.SphereImpostor,
        { mass: 2, restitution: 0.7 },
        scene
    );
    
    // Apply wind force to the cloth
    let time = 0;
    scene.onBeforeRenderObservable.add(() => {
        time += scene.getEngine().getDeltaTime() / 1000;
        
        // Oscillating wind force
        const windStrength = 5 + Math.sin(time * 0.5) * 4;
        const windDirection = new BABYLON.Vector3(Math.sin(time * 0.2), 0, Math.cos(time * 0.2));
        
        // Apply wind to soft body
        cloth.physicsImpostor.applyForce(
            windDirection.scale(windStrength),
            cloth.position
        );
    });
}

// Call the function to create the soft body
createSoftBodyCloth();
.

Debugging Physics

Visualize and debug physics to diagnose issues:

// Enable physics debug visualization
const physicsViewer = new BABYLON.PhysicsViewer(scene);

// Show impostors for specific objects
physicsViewer.showImpostor(box.physicsImpostor, box);
physicsViewer.showImpostor(sphere.physicsImpostor, sphere);

// Visualize all physics impostors in the scene
scene.meshes.forEach(mesh => {
    if (mesh.physicsImpostor) {
        physicsViewer.showImpostor(mesh.physicsImpostor, mesh);
    }
});

// Add debugging UI
const debugLayer = new BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI");

const panel = new BABYLON.GUI.StackPanel();
panel.width = "220px";
panel.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT;
panel.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP;
debugLayer.addControl(panel);

// Button to toggle physics debug visibility
const showPhysicsButton = BABYLON.GUI.Button.CreateSimpleButton("showPhysics", "Toggle Physics Debug");
showPhysicsButton.width = "200px";
showPhysicsButton.height = "40px";
showPhysicsButton.color = "white";
showPhysicsButton.background = "green";
showPhysicsButton.onPointerUpObservable.add(() => {
    // Toggle all impostor renderers
    physicsViewer.isEnabled = !physicsViewer.isEnabled;
});
panel.addControl(showPhysicsButton);

// Button to pause/resume physics
const pausePhysicsButton = BABYLON.GUI.Button.CreateSimpleButton("pausePhysics", "Pause Physics");
pausePhysicsButton.width = "200px";
pausePhysicsButton.height = "40px";
pausePhysicsButton.color = "white";
pausePhysicsButton.background = "blue";
let physicsEnabled = true;
pausePhysicsButton.onPointerUpObservable.add(() => {
    if (physicsEnabled) {
        // Pause physics
        scene.getPhysicsEngine().setTimeStep(0);
        pausePhysicsButton.textBlock.text = "Resume Physics";
    } else {
        // Resume physics
        scene.getPhysicsEngine().setTimeStep(1/60);
        pausePhysicsButton.textBlock.text = "Pause Physics";
    }
    physicsEnabled = !physicsEnabled;
});
panel.addControl(pausePhysicsButton);

// Button to reset all physics objects
const resetPhysicsButton = BABYLON.GUI.Button.CreateSimpleButton("resetPhysics", "Reset Physics");
resetPhysicsButton.width = "200px";
resetPhysicsButton.height = "40px";
resetPhysicsButton.color = "white";
resetPhysicsButton.background = "red";
resetPhysicsButton.onPointerUpObservable.add(() => {
    // Reset all dynamic objects to their original positions
    scene.meshes.forEach(mesh => {
        if (mesh.physicsImpostor && mesh.physicsImpostor.mass > 0) {
            // Reset position and rotation
            if (mesh._originalPosition) {
                mesh.position = mesh._originalPosition.clone();
                mesh.rotationQuaternion = mesh._originalRotation ? 
                    mesh._originalRotation.clone() : new BABYLON.Quaternion();
                
                // Reset velocities
                mesh.physicsImpostor.setLinearVelocity(BABYLON.Vector3.Zero());
                mesh.physicsImpostor.setAngularVelocity(BABYLON.Vector3.Zero());
            }
        }
    });
});
panel.addControl(resetPhysicsButton);

// Store original positions for reset functionality
scene.meshes.forEach(mesh => {
    if (mesh.physicsImpostor) {
        mesh._originalPosition = mesh.position.clone();
        mesh._originalRotation = mesh.rotationQuaternion ? 
            mesh.rotationQuaternion.clone() : null;
    }
});

// Log physics activity
let collisionCount = 0;
scene.meshes.forEach(mesh => {
    if (mesh.physicsImpostor) {
        mesh.physicsImpostor.onCollideEvent = (collider, collidedWith) => {
            collisionCount++;
            console.log(`Collision #${collisionCount}: ${collider.object.name} hit ${collidedWith.object.name}`);
            
            // Get collision velocity
            const relVelocity = collider.getLinearVelocity().subtract(
                collidedWith.getLinearVelocity()
            ).length();
            
            console.log(`Impact velocity: ${relVelocity.toFixed(2)}`);
        };
    }
});
.

Common Physics Pitfalls

Avoid common issues when working with physics in Babylon.js:

1. Scale Issues

Problem: Non-uniform scaling can cause physics behavior to be unpredictable.

Solution: Use mesh.scaling.isEqual(BABYLON.Vector3.One()) to check for uniform scaling. If non-uniform scaling is needed, create a properly sized collision mesh as a child of the visual mesh.

// Bad practice: Non-uniform scaling with physics

box.scaling = new BABYLON.Vector3(1, 2, 1);

box.physicsImpostor = new BABYLON.PhysicsImpostor(...);

// Good practice: Use a separate collision mesh

const visualMesh = BABYLON.MeshBuilder.CreateBox("visual", {width: 1, height: 2, depth: 1}, scene);

const collisionMesh = BABYLON.MeshBuilder.CreateBox("collision", {size: 1}, scene);

collisionMesh.scaling = new BABYLON.Vector3(1, 2, 1);

collisionMesh.visibility = 0; // Invisible collision mesh

collisionMesh.physicsImpostor = new BABYLON.PhysicsImpostor(...);

2. Mesh Parenting Issues

Problem: Physics impostors may not work correctly with parented meshes.

Solution: Apply physics to the root mesh or use compound bodies for complex hierarchies.

3. Tunneling (Objects Passing Through Each Other)

Problem: Fast-moving objects may pass through thin objects due to discrete time stepping.

Solution: Use continuous collision detection or increase the number of substeps.

// Enable CCD (Continuous Collision Detection) for fast objects

// Note: Implementation depends on physics engine

sphere.physicsImpostor.physicsBody.setCcdMotionThreshold(1);

sphere.physicsImpostor.physicsBody.setCcdSweptSphereRadius(0.2);

4. Performance Issues with Many Objects

Problem: Too many physics objects can severely impact performance.

Solution: Use instancing, compound bodies, and LOD for physics.

5. Stability Issues with Constraints

Problem: Joints and constraints can become unstable, causing objects to jitter or explode.

Solution: Use appropriate constraint limits, damping, and ensure proper initialization.

// Add damping to stabilize physics objects

box.physicsImpostor.setLinearDamping(0.1);

box.physicsImpostor.setAngularDamping(0.1);

6. Collision Filtering Issues

Problem: Objects collide with things they shouldn't, or don't collide with things they should.

Solution: Use collision groups and masks to control what collides with what.

// Set collision groups (implementation varies by physics engine)

playerMesh.physicsImpostor.physicsBody.collisionFilterGroup = 2; // Player group

playerMesh.physicsImpostor.physicsBody.collisionFilterMask = 1; // Collide only with environment

enemyMesh.physicsImpostor.physicsBody.collisionFilterGroup = 4; // Enemy group

enemyMesh.physicsImpostor.physicsBody.collisionFilterMask = 1; // Collide only with environment

7. Initialization Order Issues

Problem: Physics behaviors depend on proper initialization order.

Solution: Always ensure physics engine is initialized before creating impostors, and impostors are created before joints/constraints.

8. Garbage Collection Pauses

Problem: Creating and destroying many physics objects can trigger garbage collection, causing stutters.

Solution: Reuse physics objects using object pooling, and dispose of unused objects properly.

// Proper disposal of physics objects

function cleanupPhysicsObject(mesh) {

if (mesh.physicsImpostor) {
    mesh.physicsImpostor.dispose();
    mesh.physicsImpostor = null;
}
mesh.dispose();

}

.

Meshes and Geometry

Meshes are the 3D objects that populate your scene. Babylon.js provides comprehensive tools for creating, manipulating, and optimizing meshes, from built-in primitive shapes to complex imported models.

.

Built-in Primitive Meshes

Babylon.js includes a variety of parametric shapes through the MeshBuilder class:

// Create a sphere
const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {
    diameter: 2,
    segments: 32,
    updatable: true  // Allow geometry to be modified later
}, scene);

// Create a box/cube
const box = BABYLON.MeshBuilder.CreateBox("box", {
    size: 2,         // Size in all dimensions
    width: 3,        // Override specific dimensions
    height: 1,
    depth: 2,
    faceColors: [    // Color each face differently
        new BABYLON.Color4(1,0,0,1),  // red front
        new BABYLON.Color4(0,1,0,1),  // green back
        BABYLON.Color4.Blue(),        // blue top
        BABYLON.Color4.White(),       // white bottom
        BABYLON.Color4.Yellow(),      // yellow right
        BABYLON.Color4.Cyan()         // cyan left
    ]
}, scene);

// Create a plane
const plane = BABYLON.MeshBuilder.CreatePlane("plane", {
    width: 10,
    height: 5,
    sideOrientation: BABYLON.Mesh.DOUBLESIDE  // Visible from both sides
}, scene);

// Create a cylinder
const cylinder = BABYLON.MeshBuilder.CreateCylinder("cylinder", {
    height: 3,
    diameterTop: 1,
    diameterBottom: 2,
    tessellation: 24,     // Number of segments around circumference
    subdivisions: 1       // Number of height segments
}, scene);

// Create a torus (donut)
const torus = BABYLON.MeshBuilder.CreateTorus("torus", {
    diameter: 5,
    thickness: 1,
    tessellation: 32
}, scene);

// Create a ground mesh
const ground = BABYLON.MeshBuilder.CreateGround("ground", {
    width: 100,
    height: 100,
    subdivisions: 20,  // Increases detail for height maps
    updatable: false
}, scene);
.

Mesh Transformations

Position, rotate, and scale meshes to arrange your scene:

// Position (translation)
box.position = new BABYLON.Vector3(0, 2, 0);  // Set absolute position
box.position.y += 1;                          // Relative position change

// Rotation (in radians)
cylinder.rotation = new BABYLON.Vector3(
    0,                // X-axis rotation (pitch)
    Math.PI / 4,      // Y-axis rotation (yaw)
    0                 // Z-axis rotation (roll)
);

// Alternative rotation with Quaternions (better for complex rotations)
const quaternion = BABYLON.Quaternion.RotationAxis(
    new BABYLON.Vector3(1, 1, 0).normalize(), // Axis to rotate around
    Math.PI / 3                              // Angle in radians
);
sphere.rotationQuaternion = quaternion;

// Scaling
torus.scaling = new BABYLON.Vector3(1.5, 1.5, 1.5);  // Uniform scaling
plane.scaling = new BABYLON.Vector3(2, 1, 1);       // Non-uniform scaling

// Apply multiple transformations with a matrix
const matrix = BABYLON.Matrix.Compose(
    new BABYLON.Vector3(1.2, 0.8, 1.2),                 // Scaling
    BABYLON.Quaternion.RotationYawPitchRoll(0.3, 0, 0), // Rotation
    new BABYLON.Vector3(2, 0, -3)                       // Translation
);
ground.setPivotMatrix(matrix);
.

Mesh Hierarchies

Create parent-child relationships between meshes:

// Create parent node
const parent = new BABYLON.TransformNode("parent", scene);
parent.position.y = 5;

// Make meshes children of the parent
sphere.parent = parent;
box.parent = parent;

// Position children relative to parent
sphere.position = new BABYLON.Vector3(2, 0, 0);  // 2 units right of parent
box.position = new BABYLON.Vector3(-2, 0, 0);    // 2 units left of parent

// Rotate the parent (affects all children)
parent.rotation.y = Math.PI / 4;
.

Imported Meshes

Load 3D models created in external applications:

// Load a single model (glTF or GLB format - recommended)
BABYLON.SceneLoader.ImportMesh(
    "",                           // Names of meshes to import (empty = all)
    "./models/",                  // Path to models
    "character.glb",              // Filename
    scene,                         // Scene to import into
    (meshes, particleSystems, skeletons, animationGroups) => {
        // Successfully imported
        const character = meshes[0];
        character.scaling = new BABYLON.Vector3(0.1, 0.1, 0.1);
        
        // Play animations if available
        if (animationGroups.length > 0) {
            animationGroups[0].start(true);  // true = loop
        }
    }
);

// Asynchronous loading with await
async function loadModel() {
    const result = await BABYLON.SceneLoader.ImportMeshAsync(
        "", "./models/", "scene.gltf", scene
    );
    
    // Process imported meshes
    for (const mesh of result.meshes) {
        mesh.checkCollisions = true;
    }
    
    // Handle animations
    if (result.animationGroups.length > 0) {
        const idleAnim = result.animationGroups.find(g => g.name === "Idle");
        if (idleAnim) idleAnim.start(true);
    }
}

// Load a complete scene (replaces current scene)
BABYLON.SceneLoader.Load("./models/", "environment.glb", engine, (newScene) => {
    // newScene is now the active scene
    newScene.createDefaultCameraOrLight(true, true, true);
});
.

Mesh Operations

Combine and modify meshes:

// Merge multiple meshes into one for better performance
const merged = BABYLON.Mesh.MergeMeshes(
    [box, sphere, cylinder],  // Meshes to merge
    true,                     // Dispose original meshes
    true,                     // Allow different materials
    undefined,                // Use default world matrix
    false,                    // Don't clone meshes
    true                      // Allow different vertex colors
);

// Create instances for repeated objects with shared geometry
const boxInstance = box.createInstance("boxInstance");
boxInstance.position.x = 5;

// Create many instances efficiently
for (let i = 0; i < 100; i++) {
    const instance = box.createInstance("box" + i);
    instance.position = new BABYLON.Vector3(
        Math.random() * 20 - 10,
        Math.random() * 5,
        Math.random() * 20 - 10
    );
    instance.rotation.y = Math.random() * Math.PI * 2;
}
.

Custom Geometry

Create custom meshes with vertex data:

// Create a custom mesh (triangle)
const customMesh = new BABYLON.Mesh("custom", scene);

// Define vertex data
const vertexData = new BABYLON.VertexData();

// Positions (3 vertices, each with x,y,z coordinates)
vertexData.positions = [
    0, 1, 0,    // Top vertex
    -1, -1, 0,  // Bottom left vertex
    1, -1, 0    // Bottom right vertex
];

// Indices (defines triangles using position indices)
vertexData.indices = [0, 1, 2]; // Counter-clockwise winding

// Normals (perpendicular vectors for lighting calculations)
vertexData.normals = [
    0, 0, 1,
    0, 0, 1,
    0, 0, 1
];

// UV coordinates for texturing
vertexData.uvs = [
    0.5, 0,   // Top vertex UV
    0, 1,     // Bottom left UV
    1, 1      // Bottom right UV
];

// Apply the vertex data to the mesh
vertexData.applyToMesh(customMesh);
.

Mesh Optimization

Optimize meshes for better performance:

// Freeze transformations (improves performance)
box.freezeWorldMatrix();

// Convert to a thin instance mesh (very efficient for many copies)
const matrix1 = BABYLON.Matrix.Translation(3, 0, 0);
const matrix2 = BABYLON.Matrix.Translation(-3, 0, 0);
const matrix3 = BABYLON.Matrix.Translation(0, 0, 3);

sphere.thinInstanceSetBuffer("matrix", [matrix1, matrix2, matrix3]);

// Level of Detail (LOD)
const highDetailSphere = BABYLON.MeshBuilder.CreateSphere("highDetail", {
    segments: 32,
    diameter: 2
}, scene);

const mediumDetailSphere = BABYLON.MeshBuilder.CreateSphere("mediumDetail", {
    segments: 16,
    diameter: 2
}, scene);

const lowDetailSphere = BABYLON.MeshBuilder.CreateSphere("lowDetail", {
    segments: 8,
    diameter: 2
}, scene);

// Add LOD levels
highDetailSphere.addLODLevel(30, mediumDetailSphere);  // Switch at 30 units distance
highDetailSphere.addLODLevel(60, lowDetailSphere);     // Switch at 60 units distance
highDetailSphere.addLODLevel(100, null);               // Hide beyond 100 units

// Only the highDetailSphere needs to be visible in the scene
mediumDetailSphere.isVisible = false;
lowDetailSphere.isVisible = false;
.

Mesh Advanced Features in Babylon.js 7

Babylon.js 7 introduces improved mesh features:

// Enable mesh tessellation (dynamic subdivision)
ground.enableTessellation = true;
ground.tessellationProperties = {
    maxSubdivisions: 8,   // Maximum subdivision level
    distanceFunction: (x, y, z, camera) => {
        // Custom function to determine subdivision based on distance
        const distanceToCamera = BABYLON.Vector3.Distance(
            new BABYLON.Vector3(x, y, z),
            camera.position
        );
        return Math.max(1, 8 - Math.floor(distanceToCamera / 10));
    }
};

// GPU instancing with custom attributes
const buffer = new Float32Array(4 * 100); // 100 instances, 4 values each
for (let i = 0; i < 100; i++) {
    // Custom color and scale for each instance
    buffer[i*4] = Math.random();   // R
    buffer[i*4+1] = Math.random(); // G
    buffer[i*4+2] = Math.random(); // B
    buffer[i*4+3] = 0.5 + Math.random() * 0.5; // Scale
}

sphere.thinInstanceSetBuffer("color", buffer, 4);

// Create vertex shader that uses the buffer
const shader = new BABYLON.ShaderMaterial(/* shader code that accesses custom attributes */);
sphere.material = shader;

These mesh capabilities in Babylon.js 7 provide powerful tools for creating and optimizing 3D objects, from simple primitives to complex imported models, with performance optimizations for both desktop and mobile platforms.

.

Materials and Textures

Materials define the visual appearance of meshes, controlling properties like color, reflectivity, and texture. Babylon.js offers a comprehensive material system that supports everything from simple colored surfaces to physically-based rendering.

.

Standard Material

The StandardMaterial is a versatile, performance-focused material for basic rendering needs:

// Create a standard material
const material = new BABYLON.StandardMaterial("material", scene);

// Basic color properties
material.diffuseColor = new BABYLON.Color3(1, 0, 0);    // Red diffuse (main color)
material.specularColor = new BABYLON.Color3(1, 1, 1);   // White specular (highlights)
material.emissiveColor = new BABYLON.Color3(0, 0, 0.2); // Slight blue emission
material.ambientColor = new BABYLON.Color3(0.1, 0.1, 0.1); // Ambient lighting contribution

// Shininess and specularity
material.specularPower = 32; // Sharpness of specular highlights (higher = sharper)

// Transparency
material.alpha = 0.8; // 80% opaque

// Apply material to a mesh
sphere.material = material;
.

PBR Materials

Physically Based Rendering (PBR) materials create realistic surfaces following physical principles of light interaction:

// Create a PBR material
const pbr = new BABYLON.PBRMaterial("pbr", scene);

// Base properties
pbr.albedoColor = new BABYLON.Color3(0.5, 0.5, 0.5); // Base color (like diffuse)
pbr.metallic = 0.7;  // 0 = dielectric (plastic), 1 = metal
pbr.roughness = 0.2; // 0 = smooth, 1 = rough

// Optional properties
pbr.subSurface.isTranslucencyEnabled = true;  // Enable subsurface scattering
pbr.subSurface.translucencyIntensity = 0.8;   // Intensity of scattering

// Apply material
sphere.material = pbr;
.

Material Examples

Here are examples of creating realistic materials with PBR:

// Create a material that looks like gold
const gold = new BABYLON.PBRMaterial("gold", scene);
gold.albedoColor = new BABYLON.Color3(1.0, 0.766, 0.336);
gold.metallic = 1.0;  // Fully metallic
gold.roughness = 0.1; // Fairly smooth
gold.environmentIntensity = 0.8; // Strength of reflections

// Create a glass material
const glass = new BABYLON.PBRMaterial("glass", scene);
glass.albedoColor = new BABYLON.Color3(0.85, 0.85, 0.9);
glass.alpha = 0.2; // Mostly transparent
glass.metallic = 0.0; // Not metallic
glass.roughness = 0.0; // Very smooth
glass.environmentIntensity = 0.9; // Strong reflections
glass.indexOfRefraction = 1.5; // Like real glass
glass.subSurface.isRefractionEnabled = true; // Enable refraction
.

Textures

Textures add detailed surface information to materials:

// Load a basic texture
const diffuseTexture = new BABYLON.Texture("textures/wood.jpg", scene);
material.diffuseTexture = diffuseTexture;

// Configure texture properties
diffuseTexture.uScale = 2; // Repeat texture horizontally 2 times
diffuseTexture.vScale = 2; // Repeat texture vertically 2 times
diffuseTexture.wrapU = BABYLON.Texture.MIRROR_ADDRESSMODE; // Mirror wrapping horizontally
diffuseTexture.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE;  // Clamp edges vertically
.

Multiple Texture Maps

Add additional texture maps for detailed surface information:

// Add additional texture maps
material.bumpTexture = new BABYLON.Texture("textures/woodNormal.png", scene); // Normal map
material.ambientTexture = new BABYLON.Texture("textures/woodAO.jpg", scene);  // Ambient occlusion
material.specularTexture = new BABYLON.Texture("textures/woodSpecular.jpg", scene); // Specular map

// Adjust normal map intensity
material.bumpTexture.level = 1.5; // Intensity of normal mapping
.

PBR Texture Workflow

Using textures with PBR materials for realistic rendering:

// Using textures with PBR materials
pbr.albedoTexture = new BABYLON.Texture("textures/metal/albedo.png", scene);
pbr.metallicTexture = new BABYLON.Texture("textures/metal/metallic.png", scene);
pbr.roughnessTexture = new BABYLON.Texture("textures/metal/roughness.png", scene);
pbr.normalTexture = new BABYLON.Texture("textures/metal/normal.png", scene);
pbr.ambientOcclusionTexture = new BABYLON.Texture("textures/metal/ao.png", scene);

// Using a single channel from a texture
pbr.useRoughnessFromMetallicTextureAlpha = false;
pbr.useRoughnessFromMetallicTextureGreen = true; // Use green channel for roughness
pbr.useMetallicFromMetallicTextureBlue = true;   // Use blue channel for metallic
.

Environment Mapping

Add environment reflections to materials:

// Environment mapping for reflections
const envTexture = new BABYLON.CubeTexture("textures/environment/skybox", scene);
pbr.environmentTexture = envTexture;
.

Procedural Textures

Generate textures programmatically:

// Create a wood procedural texture
const woodTexture = new BABYLON.WoodProceduralTexture("woodTex", 512, scene);
woodTexture.ampScale = 100.0;
woodTexture.woodColor = new BABYLON.Color3(0.49, 0.25, 0.08);
material.diffuseTexture = woodTexture;
.

Custom Procedural Textures

Create custom procedural textures with shaders:

// Create a custom procedural texture with a fragment shader
const customProceduralTexture = new BABYLON.ProceduralTexture(
    "customTex", 
    512, // Size
    "customShader", // Shader name
    scene,
    null,
    true, // Generate mipmaps
    true  // Is fragment only
);

// Set shader parameters
customProceduralTexture.setFloat("time", 0);
customProceduralTexture.setVector2("resolution", new BABYLON.Vector2(512, 512));

// Update time in the render loop
scene.onBeforeRenderObservable.add(() => {
    customProceduralTexture.setFloat("time", performance.now() / 1000);
});
.

Material Management

Efficiently manage multiple materials:

.

Material Instances and Cloning

// Clone a material
const material2 = material.clone("material2");
material2.diffuseColor = new BABYLON.Color3(0, 1, 0); // Change color to green

// Create material instances (shares resources but allows property overrides)
const materialInstance = material.instantiateForInstance();
materialInstance.diffuseColor = new BABYLON.Color3(0, 0, 1); // Blue variant
.

Advanced Material Effects

Create special visual effects with materials:

.

Fresnel Effects

// Fresnel effect (edge glow)
material.diffuseFresnelParameters = new BABYLON.FresnelParameters();
material.diffuseFresnelParameters.leftColor = BABYLON.Color3.White();
material.diffuseFresnelParameters.rightColor = BABYLON.Color3.Blue();
material.diffuseFresnelParameters.power = 2;
material.diffuseFresnelParameters.bias = 0.1;

// Emissive Fresnel (good for energy shields or holograms)
material.emissiveFresnelParameters = new BABYLON.FresnelParameters();
material.emissiveFresnelParameters.leftColor = BABYLON.Color3.Black();
material.emissiveFresnelParameters.rightColor = BABYLON.Color3.Green();
material.emissiveFresnelParameters.power = 4;
material.emissiveFresnelParameters.bias = 0.5;
.

Culling and Lighting Options

// Back face culling (don't render backside of mesh)
material.backFaceCulling = true;

// Two-sided material
material.backFaceCulling = false;
material.twoSidedLighting = true;

// Disable lighting calculation (useful for UI elements)
material.disableLighting = true;
.

Material Library

Babylon.js 7 includes pre-built materials for common effects:

.

Gradient Material

// Import the materials library
// In HTML: <script src="https://cdn.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
// Or in modules: import { GradientMaterial } from 'babylonjs-materials';

// Gradient material
const gradientMaterial = new BABYLON.GradientMaterial("gradient", scene);
gradientMaterial.topColor = BABYLON.Color3.Blue();
gradientMaterial.bottomColor = BABYLON.Color3.White();
gradientMaterial.offset = 0.5;
.

Fire Material

// Fire material
const fireMaterial = new BABYLON.FireMaterial("fire", scene);
fireMaterial.diffuseTexture = new BABYLON.Texture("textures/fire.png", scene);
fireMaterial.distortionTexture = new BABYLON.Texture("textures/distortion.png", scene);
fireMaterial.speed = 5.0;
.

Cell Shading Material

// Cell (toon) shading material
const cellMaterial = new BABYLON.CellMaterial("cell", scene);
cellMaterial.diffuseColor = new BABYLON.Color3(0.8, 0.4, 0.4);
cellMaterial.computeHighLevel = true;
cellMaterial.diffuseTexture = new BABYLON.Texture("textures/amiga.jpg", scene);
.

Node Material Editor

Babylon.js 7 includes an enhanced Node Material Editor for visual material creation:

// Load a material created in the Node Material Editor
BABYLON.NodeMaterial.ParseFromFileAsync("", "materials/custom.json", scene).then(nodeMaterial => {
    sphere.material = nodeMaterial;
    
    // Access and animate material properties
    const timeBlock = nodeMaterial.getBlockByName("Time");
    scene.onBeforeRenderObservable.add(() => {
        timeBlock.value = performance.now() / 1000;
    });
});

These material capabilities in Babylon.js 7 provide extensive control over visual appearance, from basic colored surfaces to complex physically-based materials with realistic light interaction.

.

Animation System

Babylon.js provides a powerful animation system that enables smooth transitions and movements for all scene objects. From simple property changes to complex character animations, the framework offers comprehensive tools for bringing your 3D world to life.

.

Basic Animation

Create animations by defining keyframes for any object property:

// Create a box to animate
const box = BABYLON.MeshBuilder.CreateBox("box", {size: 1}, scene);

// Create an animation for position
const animation = new BABYLON.Animation(
    "positionAnimation",   // Name
    "position.y",          // Property to animate
    30,                     // Frames per second
    BABYLON.Animation.ANIMATIONTYPE_FLOAT, // Type of value
    BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE // Loop mode
);

// Define keyframes
const keyFrames = [];
keyFrames.push({ frame: 0, value: 0 });     // Start at y=0
keyFrames.push({ frame: 30, value: 3 });    // Move to y=3 at frame 30
keyFrames.push({ frame: 60, value: 0 });    // Back to y=0 at frame 60

// Assign keyframes to animation
animation.setKeys(keyFrames);

// Attach animation to the box
box.animations = [animation];

// Start the animation
scene.beginAnimation(box, 0, 60, true); // true = loop
.

Easing Functions

Add natural motion with easing functions:

// Create an easing function
const easingFunction = new BABYLON.CircleEase();
easingFunction.setEasingMode(BABYLON.EasingFunction.EASINGMODE_EASEINOUT);

// Apply to animation
animation.setEasingFunction(easingFunction);

Available easing types: BABYLON.BackEase, BABYLON.BounceEase, BABYLON.CircleEase, BABYLON.CubicEase, BABYLON.ElasticEase, BABYLON.ExponentialEase, BABYLON.PowerEase, BABYLON.QuadraticEase, BABYLON.QuarticEase, BABYLON.QuinticEase, BABYLON.SineEase

.

Animation Blending

Smoothly transition between animations:

// Create a second animation
const animation2 = new BABYLON.Animation(
    "scaleAnimation",
    "scaling",
    30,
    BABYLON.Animation.ANIMATIONTYPE_VECTOR3,
    BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);

// Define keyframes for scaling
const scaleKeyFrames = [];
scaleKeyFrames.push({ frame: 0, value: new BABYLON.Vector3(1, 1, 1) });
scaleKeyFrames.push({ frame: 30, value: new BABYLON.Vector3(2, 0.5, 2) });
scaleKeyFrames.push({ frame: 60, value: new BABYLON.Vector3(1, 1, 1) });
animation2.setKeys(scaleKeyFrames);

// Start with position animation
const animatable = scene.beginAnimation(box, 0, 60, true);

// Later, blend to scale animation
setTimeout(() => {
    box.animations = [animation2];
    scene.beginAnimation(box, 0, 60, true, 1.0); // 1.0 = blend speed
}, 3000);
.

Animation Groups

Synchronize multiple animations:

// Create an animation group
const animationGroup = new BABYLON.AnimationGroup("myGroup");

// Add animations to the group
animationGroup.addTargetedAnimation(animation, box);
.

Adding Multiple Animations

// Create and add rotation animation
const rotationAnimation = new BABYLON.Animation(
    "rotationAnimation",
    "rotation.y",
    30,
    BABYLON.Animation.ANIMATIONTYPE_FLOAT,
    BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);

const rotationKeys = [];
rotationKeys.push({ frame: 0, value: 0 });
rotationKeys.push({ frame: 60, value: Math.PI * 2 });
rotationAnimation.setKeys(rotationKeys);

animationGroup.addTargetedAnimation(rotationAnimation, box);
.

Animation Group Control

// Control the animation group
animationGroup.play(true); // true = loop
// animationGroup.pause();
// animationGroup.stop();
// animationGroup.reset();

// Adjust speed
animationGroup.speedRatio = 0.5; // Half speed

// Animation group events
animationGroup.onAnimationEndObservable.add(() => {
    console.log("Animation group completed");
});
.

Skeletal Animation

Work with rigged character animations:

// Import a rigged model
BABYLON.SceneLoader.ImportMesh("", "models/", "character.glb", scene, (meshes, particleSystems, skeletons, animationGroups) => {
    const character = meshes[0];
    const skeleton = skeletons[0];
    
    // Play an animation by name
    const idleAnim = animationGroups.find(a => a.name === "Idle");
    const walkAnim = animationGroups.find(a => a.name === "Walking");
    const runAnim = animationGroups.find(a => a.name === "Running");
    
    if (idleAnim) idleAnim.play(true);
.

Animation Switching

Switch between animations with blending:

    // Later, switch animations with blending
    document.getElementById("walk").addEventListener("click", () => {
        idleAnim.stop();
        walkAnim.play(true);
        walkAnim.speedRatio = 1.0;
    });
    
    document.getElementById("run").addEventListener("click", () => {
        walkAnim.stop();
        runAnim.play(true);
        runAnim.speedRatio = 1.5;
    });
.

Bone Manipulation

Access skeleton bones for fine control:

    // Access skeleton bones for fine control
    const rightArm = skeleton.bones.find(b => b.name === "RightArm");
    if (rightArm) {
        // Override bone rotation
        rightArm.rotation.z += Math.PI / 8;
    }
});
.

Morph Targets

Animate between different mesh shapes:

// Import a model with morph targets
BABYLON.SceneLoader.ImportMesh("", "models/", "face.glb", scene, (meshes) => {
    const face = meshes[0];
    
    // Access morph targets
    const morphTargetManager = face.morphTargetManager;
    
    if (morphTargetManager) {
        // Get specific morph targets
        const smileTarget = morphTargetManager.getTarget(0);
        const frownTarget = morphTargetManager.getTarget(1);
        const blinkTarget = morphTargetManager.getTarget(2);
.

Animating Morph Targets

        // Animate morph target influences
        let time = 0;
        scene.onBeforeRenderObservable.add(() => {
            time += scene.getEngine().getDeltaTime() / 1000;
            
            // Smile-frown cycle
            smileTarget.influence = Math.sin(time * 0.5) * 0.5 + 0.5;
            frownTarget.influence = Math.sin(time * 0.5 + Math.PI) * 0.5 + 0.5;
            
            // Occasional blink
            blinkTarget.influence = Math.pow(Math.sin(time * 3), 16);
        });
    }
});
.

Animation Events

Trigger events at specific points in animations:

// Create an animation with events
const jumpAnimation = new BABYLON.Animation(
    "jumpAnimation",
    "position.y",
    60,
    BABYLON.Animation.ANIMATIONTYPE_FLOAT,
    BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);

const jumpKeys = [];
jumpKeys.push({ frame: 0, value: 0 });
jumpKeys.push({ frame: 30, value: 5 });
jumpKeys.push({ frame: 60, value: 0 });
jumpAnimation.setKeys(jumpKeys);
.

Adding Animation Events

// Add events at specific frames
jumpAnimation.addEvent(new BABYLON.AnimationEvent(
    10, // Frame number
    function() { 
        // Event callback
        console.log("Starting to jump"); 
        // Play sound
        const jumpSound = new BABYLON.Sound("jump", "sounds/jump.mp3", scene);
        jumpSound.play();
    }
));

jumpAnimation.addEvent(new BABYLON.AnimationEvent(
    50, // Frame number
    function() { 
        console.log("Landing"); 
        // Play landing sound
        const landSound = new BABYLON.Sound("land", "sounds/land.mp3", scene);
        landSound.play();
    }
));

// Apply and play
box.animations = [jumpAnimation];
scene.beginAnimation(box, 0, 60, true);
.

Procedural Animation

Create animations directly in code:

// Animate in the render loop
let time = 0;
scene.onBeforeRenderObservable.add(() => {
    time += scene.getEngine().getDeltaTime() / 1000;
    
    // Circular motion
    sphere.position.x = Math.cos(time) * 5;
    sphere.position.z = Math.sin(time) * 5;
    
    // Bobbing motion
    sphere.position.y = 2 + Math.sin(time * 2) * 0.5;
    
    // Continuous rotation
    box.rotation.y += 0.01;
    box.rotation.x = Math.sin(time) * 0.2;
});
.

Babylon.js 7 Animation Improvements

Babylon.js 7 includes enhanced animation capabilities:

.

Animation Curve Editor

// Animation curve editor integration
const curveEditor = new BABYLON.AnimationCurveEditor();
curveEditor.addAnimation(animation);
.

Animation State Machine

// Animation state machine
const stateMachine = new BABYLON.AnimationStateMachine("characterStates", scene);

// Define states
const idleState = new BABYLON.AnimationState("idle", idleAnim);
const walkState = new BABYLON.AnimationState("walk", walkAnim);
const runState = new BABYLON.AnimationState("run", runAnim);

// Add states to state machine
stateMachine.addState(idleState);
stateMachine.addState(walkState);
stateMachine.addState(runState);
.

State Transitions

// Define transitions between states
stateMachine.addTransition(idleState, walkState, "startWalking");
stateMachine.addTransition(walkState, runState, "startRunning");
stateMachine.addTransition(runState, walkState, "slowDown");
stateMachine.addTransition(walkState, idleState, "stop");

// Start with idle state
stateMachine.setCurrentState("idle");

// Later, trigger transitions
document.getElementById("walkButton").addEventListener("click", () => {
    stateMachine.trigger("startWalking");
});

document.getElementById("runButton").addEventListener("click", () => {
    stateMachine.trigger("startRunning");
});