BabylonJS - Playing Sounds & Music
Babylon.js - Playing Sounds and Music
Section titled “Babylon.js - Playing Sounds and Music”Audio is a critical component for creating immersive games and applications. Babylon.js integrates with the browser’s Web Audio API, providing a Sound class to easily load, play, and manage sound effects and background music. You can trigger sounds based on events (like collisions, button clicks, character actions) or play ambient tracks.
The Babylon.js sound engine supports common audio formats like MP3, WAV, Ogg, etc. (support depends on the browser). It offers features like looping, autoplay, volume control, playback rate adjustment, and spatial audio (positional sound).
Creating and Playing a Sound
Section titled “Creating and Playing a Sound”You create a Sound object by providing a name, the URL to the audio file, the scene, an optional callback function that executes when the sound is loaded and ready to play, and an options object.
import * as BABYLON from '@babylonjs/core';
// Assuming 'scene' is defined
// Define sound optionsconst soundOptions = { loop: true, // Repeat the sound indefinitely autoplay: true, // Start playing as soon as loaded volume: 0.8 // Volume level (0.0 to 1.0+) // playbackRate: 1.0 // Speed of playback};
// Callback function executed when sound is readyconst soundReadyCallback = () => { console.log("Background music ready and playing."); // You could potentially call music.play() here if autoplay was false};
// Create the sound objectconst backgroundMusic = new BABYLON.Sound( "bgMusic", // Name for the sound "sounds/ambient_track.mp3", // Path to the audio file scene, // The scene to attach to soundReadyCallback, // Function to call when ready soundOptions // Options object);Parameters and Options
Section titled “Parameters and Options”name: A unique string identifier for the sound.url: The path or URL to the audio file.scene: The Babylon.js scene instance.readyToPlayCallback: (Optional) A function called once the sound has loaded and decoded.options: (Optional) An object containing properties like:-
loop(boolean): Whether the sound should loop.
-
autoplay(boolean): Whether the sound should play automatically when ready.
-
volume(number): The volume (0.0 to 1.0 typical range, can go higher).
-
playbackRate(number): The speed at which the sound plays (1.0 is normal speed).
-
spatialSound(boolean): Enables 3D spatialization (positional audio). Defaults to false.
-
distanceModel(string): How volume attenuates with distance (‘linear’, ‘inverse’, ‘exponential’). Default ‘linear’. Used with spatialSound.
-
maxDistance(number): The distance beyond which the sound is no longer audible (for ‘linear’ model) or significantly attenuated. Default 100. Used with spatialSound.
-
rolloffFactor(number): How quickly the sound attenuates for ‘inverse’ and ‘exponential’ models. Default 1.
-
refDistance(number): Reference distance for volume reduction calculations. Default 1.
-
panningModel(string): Algorithm for stereo panning (‘equalpower’ or ‘HRTF’). Default ‘equalpower’.
-
useCustomAttenuation(boolean): Allows defining a custom attenuation function.
-
customAttenuationFunction(function):(currentVolume, currentDistance, maxDistance, refDistance, rolloffFactor) => newVolume.
Make sure you have a sounds folder in your project directory containing the specified audio files (e.g., ambient_track.mp3, explosion.wav).
Demo: Background Music
Section titled “Demo: Background Music”<!DOCTYPE html><html><head> <meta charset="utf-8"/> <title>Babylon.js - Background Music</title> <script src="https://cdn.babylonjs.com/babylon.js"></script> <style> html, body { overflow: hidden; width: 100%; height: 100%; margin: 0; padding: 0; } #renderCanvas { width: 100%; height: 100%; touch-action: none; } #info { position: absolute; top: 10px; left: 10px; color: white; background-color: rgba(0,0,0,0.5); padding: 5px; } </style></head><body> <canvas id="renderCanvas"></canvas> <div id="info">Loading Scene and Sound...</div> <script type="text/javascript"> const canvas = document.getElementById("renderCanvas"); const engine = new BABYLON.Engine(canvas, true); const infoDiv = document.getElementById("info");
const createScene = function () { const scene = new BABYLON.Scene(engine); scene.clearColor = new BABYLON.Color4(0, 0.1, 0.2, 1.0);
const camera = new BABYLON.ArcRotateCamera("Camera", -Math.PI / 2, Math.PI / 2.5, 5, BABYLON.Vector3.Zero(), scene); camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
// Simple box to look at const box = BABYLON.MeshBuilder.CreateBox("box", {}, scene);
// --- Sound Setup --- const soundOptions = { loop: true, autoplay: true, volume: 0.5 };
infoDiv.textContent = "Loading sound: sounds/ambient_track.mp3"; let backgroundMusic = null; try { backgroundMusic = new BABYLON.Sound("bgMusic", "sounds/ambient_track.mp3", scene, () => { // Sound is ready to play (or already playing due to autoplay) infoDiv.textContent = "Scene Ready. Background Music Playing."; console.log("Music loaded and ready."); }, soundOptions );
// Handle potential loading/decoding errors after creation backgroundMusic.onErrorObservable.add((error) => { console.error("Error loading/playing sound:", error); infoDiv.textContent = "Error loading sound. Check console."; });
} catch (error) { console.error("Failed to initialize sound:", error); infoDiv.textContent = "Failed to initialize sound. Check console and file path."; } // ---------------------
return scene; };
const scene = createScene();
engine.runRenderLoop(function () { scene.render(); });
window.addEventListener("resize", function () { engine.resize(); }); </script></body></html>Output Description (Background Music)
Section titled “Output Description (Background Music)”This demo displays a simple box. When the page loads, it attempts to load the ambient_track.mp3 file from the sounds folder. If successful, the music starts playing automatically in a loop at 50% volume, and the text in the top-left corner updates to “Scene Ready. Background Music Playing.”. If the sound file cannot be loaded or decoded, an error message appears in the text overlay and the browser console.
Triggering Sounds Manually
Section titled “Triggering Sounds Manually”Often, you want to play sounds in response to specific events, not automatically. Set autoplay: false and call the play() method on the Sound object when needed. You might want to ensure the sound is loaded before calling play, often done inside the readyToPlayCallback or by checking the isReady() status.
// Example: Play sound on clicklet gunshotSound = null;
gunshotSound = new BABYLON.Sound("gunshot", "sounds/explosion.wav", scene, () => { // Sound is ready, enable playing it window.addEventListener("mousedown", (evt) => { if (evt.button === 0 && gunshotSound) { gunshotSound.play(); } }); console.log("Gunshot sound ready.");}, { autoplay: false });
// Example: Play sound on key presswindow.addEventListener("keydown", (evt) => { // Example: Play on space bar (keyCode 32) if (evt.keyCode === 32 && gunshotSound && gunshotSound.isReady()) { gunshotSound.play(); }});Controlling Playback
Section titled “Controlling Playback”The Sound object provides methods to control playback:
sound.play(time?: number): Starts playback, optionally from a specific time (in seconds).sound.pause(): Pauses playback.sound.stop(): Stops playback and resets the position to the beginning.sound.setVolume(volume: number, time?: number): Sets the volume, optionally fading over a duration.sound.setPlaybackRate(rate: number): Changes the playback speed.sound.currentTime: Gets the current playback position (in seconds).
Sound Events
Section titled “Sound Events”You can react when a sound finishes playing using the onended event handler or the onEndedObservable:
// Using the observable (recommended)sound.onEndedObservable.add(() => { console.log("Sound finished playing."); // Maybe trigger another sound or game event});
// Using the older onended property/*sound.onended = () => { console.log("Sound finished playing.");};*/Spatial (3D) Sound
Section titled “Spatial (3D) Sound”Spatial sound makes audio appear to originate from a specific location in the 3D scene. The volume and panning (left/right balance) change as the listener (camera) moves relative to the sound source. To use spatial sound, set spatialSound: true in the options.
const spatialSoundOptions = { loop: false, autoplay: false, spatialSound: true, distanceModel: "exponential", // Sound fades more naturally over distance maxDistance: 50, // Becomes very quiet beyond 50 units rolloffFactor: 2};
const explosionSound = new BABYLON.Sound("explosion", "sounds/explosion.wav", scene, null, spatialSoundOptions);Attaching Sound to a Mesh
Section titled “Attaching Sound to a Mesh”For spatial sounds, you often want the sound’s position to follow a moving object. Use the attachToMesh() method to link the sound’s position to a mesh’s position.
// Assuming 'movingBox' is a mesh in your scene// and 'explosionSound' is a spatial sound
explosionSound.attachToMesh(movingBox);
// Now, if movingBox moves, the perceived source of explosionSound moves with it.// You can play the sound when needed:// movingBox.actionManager = new BABYLON.ActionManager(scene);// movingBox.actionManager.registerAction(// new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPickTrigger, () => {// explosionSound.play();// })// );Global Volume Control
Section titled “Global Volume Control”You can control the master volume for all sounds managed by the Babylon.js audio engine:
// Set global volume to 70%BABYLON.Engine.audioEngine.setGlobalVolume(0.7);
// Get the global volumeconst currentGlobalVolume = BABYLON.Engine.audioEngine.getGlobalVolume();Demo: Spatial Sound Attached to Mesh
Section titled “Demo: Spatial Sound Attached to Mesh”<!DOCTYPE html><html><head> <meta charset="utf-8"/> <title>Babylon.js - Spatial Sound</title> <script src="https://cdn.babylonjs.com/babylon.js"></script> <style> html, body { overflow: hidden; width: 100%; height: 100%; margin: 0; padding: 0; } #renderCanvas { width: 100%; height: 100%; touch-action: none; } #info { position: absolute; bottom: 10px; left: 10px; color: white; background-color: rgba(0,0,0,0.5); padding: 5px; } </style></head><body> <canvas id="renderCanvas"></canvas> <div id="info">Click the box to play spatial sound</div> <script type="text/javascript"> const canvas = document.getElementById("renderCanvas"); const engine = new BABYLON.Engine(canvas, true); const infoDiv = document.getElementById("info");
const createScene = function () { const scene = new BABYLON.Scene(engine); scene.clearColor = new BABYLON.Color4(0.2, 0.1, 0.1, 1.0);
// Listener position is camera position scene.audioEnabled = true; // Ensure audio is enabled
const camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 4, Math.PI / 3, 20, BABYLON.Vector3.Zero(), scene); camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
const box = BABYLON.MeshBuilder.CreateBox("box", { size: 2 }, scene); const boxMat = new BABYLON.StandardMaterial("boxMat", scene); boxMat.diffuseColor = new BABYLON.Color3(1, 0.5, 0.2); // Orange box.material = boxMat;
// --- Spatial Sound Setup --- let explosionSound = null; const spatialSoundOptions = { loop: false, autoplay: false, spatialSound: true, distanceModel: "exponential", maxDistance: 30, rolloffFactor: 1.5 };
try { explosionSound = new BABYLON.Sound("explosion", "sounds/explosion.wav", scene, () => { console.log("Explosion sound ready."); infoDiv.textContent = "Click the box."; }, spatialSoundOptions);
// Attach sound to the box explosionSound.attachToMesh(box);
// Play sound on click using Action Manager box.actionManager = new BABYLON.ActionManager(scene); box.actionManager.registerAction( new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPickTrigger, () => { if (explosionSound && explosionSound.isReady()) { explosionSound.play(); infoDiv.textContent = "Playing sound..."; explosionSound.onEndedObservable.addOnce(() => { infoDiv.textContent = "Click the box."; }); } }) );
} catch (error) { console.error("Failed to initialize sound:", error); infoDiv.textContent = "Error loading sound. Check console."; } // --------------------------
// Animate box movement let angle = 0; scene.onBeforeRenderObservable.add(() => { angle += 0.01; box.position.x = 5 * Math.sin(angle); box.position.z = 5 * Math.cos(angle); });
return scene; };
const scene = createScene(); engine.runRenderLoop(function () { scene.render(); }); window.addEventListener("resize", function () { engine.resize(); }); </script></body></html>Output Description (Spatial Sound Demo)
Section titled “Output Description (Spatial Sound Demo)”This demo shows an orange box moving in a circular path. An explosion sound (explosion.wav from the sounds folder) is loaded as a spatial sound and attached to the box. When you click the box, the explosion sound plays. As the box moves and as you rotate the camera, you should notice the sound’s perceived location and volume changing based on its position relative to the camera (the listener).