Installations
npm install use-sound
Developer
joshwcomeau
Developer Guide
Module System
CommonJS, ESM
Min. Node Version
Typescript Support
No
Node Version
20.11.1
NPM Version
10.2.4
Statistics
2,758 Stars
63 Commits
98 Forks
15 Watching
22 Branches
17 Contributors
Updated on 27 Nov 2024
Languages
JavaScript (64.29%)
TypeScript (35.71%)
Total Downloads
Cumulative downloads
Total Downloads
7,805,223
Last day
3.8%
15,084
Compared to previous day
Last week
5%
78,009
Compared to previous week
Last month
23%
318,565
Compared to previous month
Last year
26.6%
2,946,863
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
Dependencies
1
Peer Dependencies
1
Dev Dependencies
31
useSound
A React Hook for Sound Effects
The web needs more (tasteful) sounds!
- 👂 Lets your website communicate using 2 human senses instead of 1
- 🔥 Declarative Hooks API
- ⚡️ <1kb bytes (gzip) in your bundle! ~10kb loaded async.
- ✨ Built with Typescript
- 🗣 Uses a powerful, battle-tested audio utility: Howler.js
This library only works with React DOM, but @remigallego created an alternative for React Native! Check out react-native-use-sound.
Status
This project is “semi-maintained” 😅
I don't have the bandwidth right now to look into edge-case issues or help troubleshoot, but I plan on keeping it up-to-date with major React releases, and fixing issues that are both serious and common.
If you have ideas for features, or run into strange quirks, I thoroughly recommend forking the project and making it your own! It might seem intimidating, but the source isn't as complex as many other NPM packages; I defer all the hard audio work to Howler). If you've been using React for a while and are comfortable with hooks, you should feel right at home with this package's code.
Installation
Package can be added using yarn:
1yarn add use-sound
Or, use NPM:
1npm install use-sound
UMD build available on unpkg.
If your project uses TypeScript, you should also install the @types/howler
package as a dev dependency.
Demo
The tutorial includes many demos, as well as instructions for finding and preparing sound effects. It's a great place to start.
You can also view the storybook, which includes lots of quick examples.
Examples
Play sound on click
1import useSound from 'use-sound'; 2 3import boopSfx from '../../sounds/boop.mp3'; 4 5const BoopButton = () => { 6 const [play] = useSound(boopSfx); 7 8 return <button onClick={play}>Boop!</button>; 9};
Playing on hover
This demo only plays the sound while hovering over an element. The sound pauses when the mouse leaves the element:
NOTE: Many browsers disable sounds until the user has clicked somewhere on the page. If you're not hearing anything with this example, try clicking anywhere and trying again.
1import useSound from 'use-sound'; 2 3import fanfareSfx from '../../sounds/fanfare.mp3'; 4 5const FanfareButton = () => { 6 const [play, { stop }] = useSound(fanfareSfx); 7 8 return ( 9 <button onMouseEnter={() => play()} onMouseLeave={() => stop()}> 10 <span role="img" aria-label="trumpet"> 11 🎺 12 </span> 13 </button> 14 ); 15};
Increase pitch on every click
With the playbackRate
option, you can change the speed/pitch of the sample. This example plays a sound and makes it 10% faster each time:
1import useSound from 'use-sound';
2
3import glugSfx from '../../sounds/glug.mp3';
4
5export const RisingPitch = () => {
6 const [playbackRate, setPlaybackRate] = React.useState(0.75);
7
8 const [play] = useSound(glugSfx, {
9 playbackRate,
10 // `interrupt` ensures that if the sound starts again before it's
11 // ended, it will truncate it. Otherwise, the sound can overlap.
12 interrupt: true,
13 });
14
15 const handleClick = () => {
16 setPlaybackRate(playbackRate + 0.1);
17 play();
18 };
19
20 return (
21 <Button onClick={handleClick}>
22 <span role="img" aria-label="Person with lines near mouth">
23 🗣
24 </span>
25 </Button>
26 );
27};
Usage Notes
Importing/sourcing audio files
useSound
requires a path to an audio file, and it isn't obvious how to provide one in a React application.
Using create-react-app
, you can "import" an MP3 file. It will resolve to a dynamically-generated path:
1import someAudioFile from '../sounds/sound.mp3'; 2 3console.log(someAudioFile); // “/build/sounds/sound-abc123.mp3”
If you try to pull this trick in another React build system like Next.js, you may get an error like this:
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.
The problem is that Webpack (the bundler used under-the-hood to generate JS bundles) doesn't know how to process an MP3 file.
If you have access to the Webpack config, you can update it to use file-loader, which will create a dynamic, publicly-accessible path to the file.
Alternatively, most tools will give you a "public" (create-react-app, Next.js) or a "static" (Gatsby) folder. You can drop your audio files in there, and then use a string path.
The sound files you'll use with use-sound
follow the same rules as other static assets like images or fonts. Follow the guides for your meta-framework of choice:
⚠️ Async sound paths? ⚠️ If the URL to your audio file is loaded asynchronously, you might run into some problems. This probably isn't the right package for that usecase.
No sounds immediately after load
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.
Reactive configuration
Consider the following snippet of code:
1const [playbackRate, setPlaybackRate] = React.useState(0.75);
2
3const [play] = useSound('/path/to/sound', { playbackRate });
playbackRate
doesn't just serve as an initial value for the sound effect. If playbackRate
changes, the sound will immediately begin playing at a new rate. This is true for all options passed to the useSound
hook.
API Documentation
The useSound
hook takes two arguments:
- A URL to the sound that it wil load
- A config object (
HookOptions
)
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.
HookOptions
When calling useSound
, 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
hook for multiple sound effects. See “Sprites” below.
[delegated]
refers to the fact that any additional argument you pass in HookOptions
will be forwarded to the Howl
constructor. See "Escape hatches" below for more information.
The play
function
When calling the hook, you get back a play function as the first item in the tuple:
1const [play] = useSound('/meow.mp3'); 2// ^ 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 a PlayOptions
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 toHookOptions
. 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 inHookOptions
. In general you should prefer to do it throughHookOptions
, this is an escape hatch.
ExposedData
The hook produces a tuple with 2 options, the play function and an ExposedData
object:
1const [play, exposedData] = useSound('/meow.mp3'); 2// ^ What we're talking about
Name | Value |
---|---|
stop | function ((id?: string) => void) |
pause | function ((id?: string) => void) |
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.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 the Howler documentation to learn more about how to use it. Note that this will benull
for the first few moments after the component mounts.
Advanced
Sprites
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 the Drum Machine story for an example.
For sprites, we'll need to define a SpriteMap
. It looks like this:
1const spriteMap = { 2 laser: [0, 300], 3 explosion: [1000, 300], 4 meow: [2000, 75], 5};
SpriteMap
is an object. The keys are the id
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 HookOptions:
1const [play] = useSound('/path/to/sprite.mp3', { 2 sprite: { 3 laser: [0, 300], 4 explosion: [1000, 300], 5 meow: [2000, 75], 6 }, 7});
To play a specific sprite, we'll pass its id
when calling the play
function:
1<button 2 onClick={() => play({id: 'laser'})} 3>
Escape hatches
Howler is a very powerful library, and we've only exposed a tiny slice of what it can do in useSound
. We expose two escape hatches to give you more control.
First, any unrecognized option you pass to HookOptions
will be delegated to Howl
. You can see the full list of options in the Howler docs. Here's an example of how we can use onend
to fire a function when our sound stops playing:
1const [play] = useSound('/thing.mp3', { 2 onend: () => { 3 console.info('Sound ended!'); 4 }, 5});
If you need more control, you should be able to use the sound
object directly, which is an instance of Howler.
For example: Howler exposes a fade
method, which lets you fade a sound in or out. You can call this method directly on the sound
object:
1const Arcade = () => { 2 const [play, { sound }] = useSound('/win-theme.mp3'); 3 4 return ( 5 <button 6 onClick={() => { 7 // You win! Fade in the victory theme 8 sound.fade(0, 1, 1000); 9 }} 10 > 11 Click to win 12 </button> 13 ); 14};
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
license file detected
Details
- Info: project has a license file: LICENSE:0
- Info: FSF or OSI recognized license: MIT License: LICENSE:0
Reason
Found 7/30 approved changesets -- score normalized to 2
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
security policy file not detected
Details
- Warn: no security policy file detected
- Warn: no security file to analyze
- Warn: no security file to analyze
- Warn: no security file to analyze
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
project is not fuzzed
Details
- Warn: no fuzzer integrations found
Reason
branch protection not enabled on development/release branches
Details
- Warn: branch protection not enabled for branch 'main'
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
- Warn: 0 commits out of 7 are checked with a SAST tool
Reason
85 existing vulnerabilities detected
Details
- Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92
- Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw
- Warn: Project is vulnerable to: GHSA-whgm-jr23-g3j9
- Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw
- Warn: Project is vulnerable to: GHSA-qwcr-r2fm-qrc7
- Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg
- Warn: Project is vulnerable to: GHSA-x9w5-v3q2-3rhw
- Warn: Project is vulnerable to: GHSA-w8qv-6jwh-64r5
- Warn: Project is vulnerable to: GHSA-pxg6-pf52-xh8x
- Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275
- Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c
- Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq
- Warn: Project is vulnerable to: GHSA-phwq-j96m-2c2q
- Warn: Project is vulnerable to: GHSA-ghr5-ch3p-vcr6
- Warn: Project is vulnerable to: GHSA-r9p9-mrjm-926w
- Warn: Project is vulnerable to: GHSA-434g-2637-qmqr
- Warn: Project is vulnerable to: GHSA-49q7-c7j4-3p7m
- Warn: Project is vulnerable to: GHSA-977x-g7h5-7qgw
- Warn: Project is vulnerable to: GHSA-f7q4-pwc6-w24p
- Warn: Project is vulnerable to: GHSA-fc9h-whq2-v747
- Warn: Project is vulnerable to: GHSA-6h5x-7c5m-7cr7
- Warn: Project is vulnerable to: GHSA-rv95-896h-c2vc
- Warn: Project is vulnerable to: GHSA-qw6h-vgh9-j6wx
- Warn: Project is vulnerable to: GHSA-ww39-953v-wcq6
- Warn: Project is vulnerable to: GHSA-vfrc-7r7c-w9mx
- Warn: Project is vulnerable to: GHSA-7wwv-vh3v-89cq
- Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj
- Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37
- Warn: Project is vulnerable to: GHSA-78xj-cgh5-2h22
- Warn: Project is vulnerable to: GHSA-2p57-rm9w-gvfp
- Warn: Project is vulnerable to: GHSA-896r-f27r-55mw
- Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h
- Warn: Project is vulnerable to: GHSA-76p3-8jx3-jpfq
- Warn: Project is vulnerable to: GHSA-3rfm-jhwj-7488
- Warn: Project is vulnerable to: GHSA-hhq3-ff78-jv3g
- Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw
- Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9
- Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm
- Warn: Project is vulnerable to: GHSA-4wx3-54gh-9fr9
- Warn: Project is vulnerable to: GHSA-r6rj-9ch6-g264
- Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv
- Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3
- Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h
- Warn: Project is vulnerable to: GHSA-r683-j2x4-v87g
- Warn: Project is vulnerable to: GHSA-5fw9-fq32-wv5p
- Warn: Project is vulnerable to: GHSA-rp65-9cf3-cjxr
- Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9
- Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j
- Warn: Project is vulnerable to: GHSA-566m-qj78-rww5
- Warn: Project is vulnerable to: GHSA-hwj9-h5mp-3pm3
- Warn: Project is vulnerable to: GHSA-7fh5-64p2-3v2j
- Warn: Project is vulnerable to: GHSA-wvhm-4hhf-97x9
- Warn: Project is vulnerable to: GHSA-h4hr-7fg3-h35w
- Warn: Project is vulnerable to: GHSA-gj77-59wh-66hg
- Warn: Project is vulnerable to: GHSA-hqhp-5p83-hx96
- Warn: Project is vulnerable to: GHSA-3949-f494-cm99
- Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp
- Warn: Project is vulnerable to: GHSA-5q6m-3h65-w53x
- Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6
- Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm
- Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw
- Warn: Project is vulnerable to: GHSA-44c6-4v22-4mhx
- Warn: Project is vulnerable to: GHSA-4x5v-gmq8-25ch
- Warn: Project is vulnerable to: GHSA-m6fv-jmcg-4jfg
- Warn: Project is vulnerable to: GHSA-hxcc-f52p-wc94
- Warn: Project is vulnerable to: GHSA-cm22-4g7w-348p
- Warn: Project is vulnerable to: GHSA-g4rg-993r-mgx7
- Warn: Project is vulnerable to: GHSA-4rq4-32rv-6wp6
- Warn: Project is vulnerable to: GHSA-64g7-mvw6-v9qj
- Warn: Project is vulnerable to: GHSA-vx3p-948g-6vhq
- Warn: Project is vulnerable to: GHSA-4wf5-vphf-c2xc
- Warn: Project is vulnerable to: GHSA-jgrx-mgxx-jf9v
- Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3
- Warn: Project is vulnerable to: GHSA-9m6j-fcg5-2442
- Warn: Project is vulnerable to: GHSA-hh27-ffr2-f2jc
- Warn: Project is vulnerable to: GHSA-rqff-837h-mm52
- Warn: Project is vulnerable to: GHSA-8v38-pw62-9cw2
- Warn: Project is vulnerable to: GHSA-hgjh-723h-mx2j
- Warn: Project is vulnerable to: GHSA-jf5r-8hm2-f872
- Warn: Project is vulnerable to: GHSA-wr3j-pwj9-hqq6
- Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7
- Warn: Project is vulnerable to: GHSA-6fc8-4gx4-v693
- Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q
- Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh
- Warn: Project is vulnerable to: GHSA-p9pc-299p-vxgp
Score
2
/10
Last Scanned on 2024-11-18
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn MoreOther packages similar to use-sound
@raffaelesgarro/vue-use-sound
🔊 A Vue composable for playing sound effects
@viveljs/use-sound
``` Warning !! This is only for TypeScript Howl issue workaround. Will only push for this update. Please watch http://github.com/joshwcomeau/use-sound for updates. ``` # useSound
com.quarter-games.sound
This is easy to use sound manager
react-native-sound
React Native module for playing sound clips on iOS, Android, and Windows