- Notifications
You must be signed in to change notification settings - Fork28
🔊 A Vue composable for playing sound effects
License
vueuse/sound
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
- 👂 Lets your websitecommunicate using 2human senses instead of 1
- 🔥 Built withVue Composition API
- ✅ SupportsVue 2 & 3 usingvue-demi
- 🚚 SupportsNuxt 2 & 3 using
@vueuse/sound/nuxt
- ⚡️<1kb bytes (gzip) in yourbundle!~10kb loadedasync.
- ✨ Built withTypeScript
- 🗣 Uses a powerful, battle-tested audio utility:Howler.js
If you want to take aquick look at the composable in effect, you should visit the🌍demo.
This package is aVue version of theuseSound React hook byjoshwcomeau.
Package can be added usingyarn:
yarn add @vueuse/sound
Or, use NPM:
npm install @vueuse/sound
This is the most basic example of how fast you can implement sounds in your app using @vueuse/sound.
<template><button @click="play">Play a sound</button></template><script>import{ useSound}from'@vueuse/sound'importbuttonSfxfrom'../assets/sounds/button.mp3'exportdefault{setup(){const{ play}=useSound(buttonSfx)return{ play,}},}</script>
This example is shown in thedemo.
This example is shown in thedemo.
For the user's sake, browsers don't allow websites to produce sound until the user has interacted with them (eg. by clicking on something). No sound will be produced until the user clicks, taps, or triggers something.
useSound
takes advantage of this: because we know that sounds won't be needed immediately on-load, we can lazy-load a third-party dependency.
useSound
will add about 1kb gzip to your bundle, and will asynchronously fetch an additional package after load, which clocks in around 9kb gzip.
If the user does happen to click with something that makes noise before this dependency has been loaded and fetched, it will be a no-op (everything will still work, but no sound effect will play). In my experience this is exceedingly rare.
Consider the following snippet of code:
constplaybackRate=ref(0.75)const{ play}=useSound('/path/to/sound',{ playbackRate})
playbackRate
doesn't just serve as aninitial value for the sound effect. IfplaybackRate
changes, the sound will immediately begin playing at a new rate. This is true for all options passed to theuseSound
composable.
TheuseSound
composable takes two arguments:
- A URL to the sound that it wil load
- A config object (
ComposableOptions
)
It produces an array with two values:
- A function you can call to trigger the sound
- An object with additional data and controls (
ExposedData
)
When calling the function to play the sound, you can pass it a set of options (PlayOptions
).
Let's go through each of these in turn.
When callinguseSound
, you can pass it a variety of options:
Name | Value |
---|---|
volume | number |
playbackRate | number |
interrupt | boolean |
soundEnabled | boolean |
sprite | SpriteMap |
[delegated] | — |
volume
is a number from0
to1
, where1
is full volume and0
is comletely muted.playbackRate
is a number from0.5
to4
. It can be used to slow down or speed up the sample. Like a turntable, changes to speed also affect pitch.interrupt
specifies whether or not the sound should be able to "overlap" if theplay
function is called again before the sound has ended.soundEnabled
allows you to pass a value (typically from context or redux or something) to mute all sounds. Note that this can be overridden in thePlayOptions
, see belowsprite
allows you to use a singleuseSound
composable for multiple sound effects. See“Sprites” below.
[delegated]
refers to the fact that any additional argument you pass inComposableOptions
will be forwarded to theHowl
constructor. See "Escape hatches" below for more information.
When calling the composable, you get back a play function as the first item in the tuple:
const{ play}=useSound('/meow.mp3')// ^ What we're talking about
You can call this function without any arguments when you want to trigger the sound. You can also call it with aPlayOptions
object:
Name | Value |
---|---|
id | string |
forceSoundEnabled | boolean |
playbackRate | number |
id
is used for sprite identification. See“Sprites” below.forceSoundEnabled
allows you to override thesoundEnabled
boolean passed toComposableOptions
. You generally never want to do this. The only exception I've found: triggering a sound on the "Mute" button.playbackRate
is another way you can set a new playback rate, same as inComposableOptions
. In general you should prefer to do it throughComposableOptions
, this is an escape hatch.
The composable produces a tuple with 2 options, the play function and anExposedData
object:
const[play,exposedData]=useSound('/meow.mp3')// ^ What we're talking about
Name | Value |
---|---|
stop | function ((id?: string) => void) |
pause | function ((id?: string) => void) |
isPlaying | boolean |
duration | number (or null) |
sound | Howl (or null) |
stop
is a function you can use to pre-emptively halt the sound.pause
is likestop
, except it can be resumed from the same point. Unless you know you'll want to resume, you should usestop
;pause
hogs resources, since it expects to be resumed at some point.isPlaying
lets you know whether this sound is currently playing or not. When the sound reaches the end, or it's interrupted withstop
orpaused
, this value will flip back tofalse
. You can use this to show some UI only while the sound is playing.duration
is the length of the sample, in milliseconds. It will benull
until the sample has been loaded. Note that for sprites, it's the length of the entire file.sound
is an escape hatch. It grants you access to the underlyingHowl
instance. See theHowler documentation to learn more about how to use it. Note that this will benull
for the first few moments after the component mounts.
An audio sprite is a single audio file that holds multiple samples. Instead of loading many individual sounds, you can load a single file and slice it up into multiple sections which can be triggered independently.
There can be a performance benefit to this, since it's less parallel network requests, but it can also be worth doing this if a single component needs multiple samples. See theDrum Machine component for an example.
For sprites, we'll need to define aSpriteMap
. It looks like this:
constspriteMap={laser:[0,300],explosion:[1000,300],meow:[2000,75],}
SpriteMap
is an object. The keys are theid
s for individual sounds. The value is a tuple (array of fixed length) with 2 items:
- The starting time of the sample, in milliseconds, counted from the very beginning of the sample
- The length of the sample, in milliseconds.
This visualization might make it clearer:
We can pass our SpriteMap as one of our ComposableOptions:
const{ play}=useSound('/path/to/sprite.mp3',{sprite:{laser:[0,300],explosion:[1000,300],meow:[2000,75],},})
To play a specific sprite, we'll pass itsid
when calling theplay
function:
<button @click="play({id: 'laser'})">
Howler is a very powerful library, and we've only exposed a tiny slice of what it can do inuseSound
. We expose two escape hatches to give you more control.
First, any unrecognized option you pass toComposableOptions
will be delegated toHowl
. You can see thefull list of options in the Howler docs. Here's an example of how we can useonend
to fire a function when our sound stops playing:
const{ play}=useSound('/thing.mp3',{onend:()=>{console.info('Sound ended!')},})
If you need more control, you should be able to use thesound
object directly, which is an instance of Howler.
For example: Howlerexposes afade
method, which lets you fade a sound in or out. You can call this method directly on thesound
object:
<template><button @click={sound.fade(0,1,1000)}> Click to win</button></template><script>import{ useSound}from'@vueuse/sound'exportdefault{setup(){const{ play, sound}=useSound('/win-theme.mp3')return{ sound}}}</script>
If you are using Vite, you should add the following to your defineConfig options in vite.config.js:
optimizeDeps:{exclude:['vue-demi']}
If you use Nuxt 2, you must have@nuxt/bridge setup in your project.
Once you installed it, add@vueuse/sound/nuxt
dependency to your project.
Add@vueuse/sound/nuxt
to themodules
section of yournuxt.config
:
defineNuxtConfig({modules:['@vueuse/sound/nuxt']})
Configure your sounds 🥁:
defineNuxtConfig({sound:{sounds:{back:{src:"/back.wav",options:{volume:0.25}}}}})
You can also automatically scan an generate typings for your sounds inpublic/sounds
directory by usingscan feature:
defineNuxtConfig({sound:{sounds:{scan:true}}})
Then move your sounds intopublic/sounds
directory, and get autocompletion onuseSound({url})
.
All thecredit behind thisidea goes toJosh W. Comeau.
Thedocumentation of this package has only been updated forVue Composition API instead ofReact Hooks.
If youlike this package, considerfollowing me onGitHub and onTwitter.
About
🔊 A Vue composable for playing sound effects