Cameras and Player Controls in Babylon.js
The camera in a 3D game does more than render the scene. It defines the player's relationship with the game world. A first-person camera puts the player inside the action. A third-person camera gives spatial awareness and lets the player see their character. A fixed camera creates cinematic tension. The control scheme tied to that camera determines whether the game feels responsive or sluggish, precise or floaty. Getting cameras and controls right is one of the most important parts of game development, and Babylon.js provides the tools to get there without building everything from scratch.
Step 1: Choose a Camera Type
The FreeCamera is the standard first-person camera. It responds to WASD or arrow keys for movement and mouse movement for looking around. Create it with a position and attach it to the canvas: new BABYLON.FreeCamera("cam", new BABYLON.Vector3(0, 1.8, -5), scene). The Y position of 1.8 approximates a standing eye height in meters. The camera's target property sets the initial look direction.
The ArcRotateCamera orbits around a target point, making it ideal for third-person views, model viewers, and strategy games. It uses alpha (horizontal angle), beta (vertical angle), and radius (distance) to position itself relative to its target. The user rotates by dragging and zooms with the scroll wheel. You can limit all three parameters to prevent the camera from going underground, zooming too far out, or orbiting past certain angles.
The FollowCamera tracks a target mesh from behind with configurable height offset, distance, and rotation speed. It smoothly follows the target as it moves and turns, which works well for racing games, platformers, and any game where the character moves through the world and the camera should keep up. Set camera.radius for distance, camera.heightOffset for how far above the target, and camera.rotationOffset for the angle behind the character.
The UniversalCamera is a hybrid that combines FreeCamera movement with gamepad support. It automatically detects connected gamepads and maps their axes to camera movement and rotation. This makes it useful when you want a single camera class that handles keyboard, mouse, touch, and gamepad input without switching between camera types.
Step 2: Configure Camera Properties
Every camera has a speed property that controls how fast it moves in response to keyboard input. The default is 2.0, which is often too fast for indoor scenes and too slow for large outdoor environments. Adjust this based on the scale of your world. A character walking through a house might use 0.5, while a spaceship flying through an asteroid field might use 50.
The angularSensibility property controls mouse sensitivity. Higher values mean slower rotation, lower values mean faster rotation. The default is 2000, which is reasonable for desktop but may need adjustment for different screen sizes and player preferences. Some games expose this as a settings option, which is straightforward since it is a single numeric property.
Clipping planes determine how close and how far the camera renders. The minZ property sets the near clip plane (default 0.1), and maxZ sets the far clip plane (default 10000). Objects closer than minZ or farther than maxZ are not rendered. Setting minZ too small causes z-fighting (flickering surfaces), while setting maxZ too large wastes depth buffer precision. For most games, minZ of 0.1 and maxZ of 1000 provides a good balance.
Field of view (fov) defaults to 0.8 radians (about 45 degrees vertical). First-person games typically use a wider FOV of 1.0 to 1.2 radians (57 to 69 degrees) to increase peripheral vision. Racing games sometimes go even wider. Adjust FOV based on how much of the world the player needs to see at once.
Step 3: Enable Pointer Lock
For first-person games, you need the mouse cursor to disappear so mouse movement directly controls the camera rotation without the cursor hitting the edge of the screen. The Pointer Lock API provides this. Babylon.js integrates with it through the camera's attachControl method and the scene.onPointerDown observable.
Request pointer lock when the player clicks the canvas: canvas.requestPointerLock(). Browsers require this to be triggered by a user gesture (a click or key press) for security reasons. You cannot lock the pointer automatically on page load. When locked, mouse movement events report movementX and movementY deltas instead of absolute positions, which is exactly what camera rotation needs.
Handle the pointer lock exit gracefully. Players can press Escape to release the pointer lock at any time. Listen for the pointerlockchange event on the document to detect when the lock is released, and pause the game or show a menu so the player is not stuck with an unresponsive camera. This is a standard pattern in browser-based first-person games.
Step 4: Add Gamepad Support
The Gamepad class in Babylon.js wraps the browser's Gamepad API with a cleaner interface. Use scene.gamepadManager to detect when gamepads connect and disconnect. The onGamepadConnectedObservable fires when a controller is plugged in or reconnected. The manager automatically maps standard gamepad layouts (Xbox, PlayStation, generic) to consistent axis and button indices.
For the UniversalCamera, gamepad support is built in. The left stick controls movement (forward/back/strafe) and the right stick controls camera rotation. Button mappings can be customized through the camera's gamepadAngularSensibility and gamepadMoveSensibility properties. For custom input beyond camera control, read gamepad state directly with gamepad.leftStick and gamepad.rightStick for axis values, and check button states with gamepad.onButtonDownObservable.
Dead zones are important for gamepad input. Analog sticks rarely rest at exactly zero, so small values should be ignored to prevent the camera from drifting when the player is not touching the stick. Babylon.js handles this with a default dead zone, but you may need to adjust it based on the controller hardware and player feedback.
Step 5: Implement Touch Controls
Mobile web games need on-screen touch controls since there is no keyboard or mouse. Babylon.js includes the VirtualJoystick class, which renders a translucent joystick on the canvas that responds to touch input. Create two instances, one on the left side for movement and one on the right for camera rotation, and map their output to your character controller.
The VirtualJoystick provides deltaPosition values that represent how far the player has dragged from the center. Map these to character movement speed and direction. For the camera joystick, map the delta to camera rotation amounts. The joystick handles multi-touch automatically, so the movement stick and camera stick work independently when the player touches both sides of the screen simultaneously.
Touch zones offer an alternative to visible joysticks. Define regions of the canvas where touch events trigger specific actions. The bottom-left quarter might be a movement zone, while the right half might be a look zone. Single taps in certain areas can trigger jumps, attacks, or interactions. This approach feels more natural on touchscreens because it does not require the player to hit a specific joystick graphic.
Step 6: Handle Custom Input
The ActionManager attaches behaviors to meshes and the scene. Register actions for events like OnPickTrigger (click on a mesh), OnIntersectionEnterTrigger (two meshes overlap), or OnKeyDownTrigger (key press). Actions can execute code, interpolate properties, or play sounds. This is useful for interaction prompts (press E to open door) and trigger zones (entering a room starts a cutscene).
For more flexible input handling, use the Observable system. scene.onKeyboardObservable fires for every key press and release with the key code and event type. scene.onPointerObservable fires for mouse and touch events with position, button, and pick information. You can filter these observables to only respond to specific keys or pointer events, and you can add multiple observers for different game systems that all need the same input.
Building a proper input manager for a game means mapping physical inputs (key codes, button indices, stick axes) to logical actions (move forward, jump, attack, interact). This abstraction lets you support rebindable controls, multiple input devices, and accessibility settings without changing your game logic. The game code checks "is the jump action active?" rather than "is the space bar pressed?", and the input manager handles the mapping between the two.
Choose the camera type that matches your genre, configure its properties for the scale and feel of your world, and build an input abstraction layer that separates physical controls from game actions. Supporting keyboard, gamepad, and touch from the start makes your game accessible to the widest audience.