Creating immersion within a web application is hard. Unless a lot of effort is put into the UI/UX design, these apps end up feeling very flat and lifeless. As such, even the smallest feature that adds an organic touch can vastly improve the "feel" of the app.
If your application is very audio-centric (a game for instance), one such feature you can easily add is spatial audio. Giving your sound effect an origin within the application can make the whole thing feel bigger. Let's take a quick look at how this can be achieved using a JavaScript
audio library called Howler
.
I won't go into details about how Howler
itself works, but you can read up on the subject in their docs here. For now, all you need to know is that we use the Howl
constructor to instantiate a simple sound effect object, and that this object takes an optional parameter called stereo
.
The stereo
parameter accepts a number ranging anywhere between -1 and 1, which corresponds to the left/right channel bias for the stereo sound (-1 being full left, 1 being full right). For this example, we simply want to play a sound effect when the mouse is clicked, and we want it to feel as though that sound originates from the cursor.
Below is the basic setup for use in a React
component. This will play the specified sound effect normally whenever the mouse is clicked within the component.
import { useEffect } from "react";
import { Howl } from "howler";
import mySound from "./sounds/mySound.webm"; // our fictitious audio file, replace this with whatever sound you want to play
const MyComponent = () => {
let component;
useEffect(() => {
const handleClick = (e) => {
const sound = new Howl({ src: mySound }); // instantiate a new Howl here, passing it the path to our sound effect
sound.play(); // as soon as the object is created, we can play the sound effect
};
component && component.addEventListener("click", handleClick); // once the component has been rendered and saved to a variable, add the EventListener
return () => {
component && component.removeEventListener("click", handleClick); // if the component is removed, remove the EventListener
};
}, [component]);
return (
<div
style={{ width: "100vw", height: "100vh" }} // adding the styling ensures that our component will cover the entire viewport
ref={(el) => (component = el)} // save the rendered element to a ref variable we can manipulate
/>
);
};
export default MyComponent;
Now, to figure out where the sound is coming from, we need to do some simple calculations based on coordinates of the cursor in relation to the width of the component. We will do so by adding the following function to the top of the useEffect
callback.
const getStereoBias = (mouseX) => {
const w = component.clientWidth; // grab the component's width
const bias = -((Math.round(w / 2) - mouseX) / w) * 2; // calculate a value of -1 to 1 based on the cursor position within the component
return bias;
};
And finally, we will use this function whenever a sound effect is generated to tell Howler
where the sound is coming from by modifying the handleClick
function as follows.
const handleClick = (e) => {
const stereoBias = getStereoBias(e.clientX); // calculate the "position" of the sound's origin
const sound = new Howl({ src: mySound, stereo: stereoBias }); // instantiate a new Howl here, passing it the path to our sound effect and stereo bias "position"
sound.play(); // as soon as the object is created, we can play the sound effect
};
And just like that, whenever our sound effect is played, it will follow the cursor around the screen (handy for things like particle effects in games)!
To view a fleshed-out example of this concept in action, check out my Domain Destroyer Demo project.
If you make something cool like this, drop it in the comments, I'd love to see what you come up with!
Top comments (2)
So I'm trying to create a virtual town and I want to use spatial audio with streams such people close to each othe in the online town hear each other's streams and if they're far apart the dont, is this achievable with howler?
It depends how exactly your project is set up, but I'm sure you could make something work. Howler actually includes a full 3D spacial audio plugin for games and such.
You can check that out here