Git Product home page Git Product logo

use-sound's Introduction

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

Minified file size License: MIT NPM version Code of Conduct

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:

yarn add use-sound

Or, use NPM:

npm install use-sound

UMD build available on unpkg.


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

import useSound from 'use-sound';

import boopSfx from '../../sounds/boop.mp3';

const BoopButton = () => {
  const [play] = useSound(boopSfx);

  return <button onClick={play}>Boop!</button>;
};

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.

import useSound from 'use-sound';

import fanfareSfx from '../../sounds/fanfare.mp3';

const FanfareButton = () => {
  const [play, { stop }] = useSound(fanfareSfx);

  return (
    <button onMouseEnter={() => play()} onMouseLeave={() => stop()}>
      <span role="img" aria-label="trumpet">
        🎺
      </span>
    </button>
  );
};

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:

import useSound from 'use-sound';

import glugSfx from '../../sounds/glug.mp3';

export const RisingPitch = () => {
  const [playbackRate, setPlaybackRate] = React.useState(0.75);

  const [play] = useSound(glugSfx, {
    playbackRate,
    // `interrupt` ensures that if the sound starts again before it's
    // ended, it will truncate it. Otherwise, the sound can overlap.
    interrupt: true,
  });

  const handleClick = () => {
    setPlaybackRate(playbackRate + 0.1);
    play();
  };

  return (
    <Button onClick={handleClick}>
      <span role="img" aria-label="Person with lines near mouth">
        🗣
      </span>
    </Button>
  );
};

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:

import someAudioFile from '../sounds/sound.mp3';

console.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:

const [playbackRate, setPlaybackRate] = React.useState(0.75);

const [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 from 0 to 1, where 1 is full volume and 0 is comletely muted.
  • playbackRate is a number from 0.5 to 4. 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 the play 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 the PlayOptions, see below
  • sprite allows you to use a single useSound 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:

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 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 the soundEnabled boolean passed to HookOptions. 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 in HookOptions. In general you should prefer to do it through HookOptions, this is an escape hatch.

ExposedData

The hook produces a tuple with 2 options, the play function and an ExposedData object:

const [play, exposedData] = useSound('/meow.mp3');
//                ^ 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 like stop, except it can be resumed from the same point. Unless you know you'll want to resume, you should use stop; pause hogs resources, since it expects to be resumed at some point.
  • duration is the length of the sample, in milliseconds. It will be null 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 underlying Howl instance. See the Howler documentation to learn more about how to use it. Note that this will be null 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:

const spriteMap = {
  laser: [0, 300],
  explosion: [1000, 300],
  meow: [2000, 75],
};

SpriteMap is an object. The keys are the ids 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:

Waveform visualization showing how each sprite occupies a chunk of time, and is labeled by its start time and duration

We can pass our SpriteMap as one of our HookOptions:

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 its id when calling the play function:

<button
  onClick={() => play({id: 'laser'})}
>

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:

const [play] = useSound('/thing.mp3', {
  onend: () => {
    console.info('Sound ended!');
  },
});

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:

const Arcade = () => {
  const [play, { sound }] = useSound('/win-theme.mp3');

  return (
    <button
      onClick={() => {
        // You win! Fade in the victory theme
        sound.fade(0, 1, 1000);
      }}
    >
      Click to win
    </button>
  );
};

use-sound's People

Contributors

blasterpistol avatar curt-tophatter avatar dependabot[bot] avatar dotlouis avatar drsensor avatar gvozd avatar jlengstorf avatar joshwcomeau avatar kaelhem avatar mrbfrank avatar myagoo avatar nquinlan avatar remigallego avatar tamoshaytisv avatar trevorblades avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

use-sound's Issues

Feature Request: Howler escape hatch

I've implemented use-sound in a recent project and been really happy with how easy the library has made it.

However, for this project, I need to get Howler's audio context (Howler.ctx), but right now the library doesn't ever expose its instance of Howler.

I'm wondering if you'd support a PR to add an escape hatch for Howler proper? If so, do you have a way you'd prefer to see it done, as I'm fairly new to the React ecosystem, and don't know the most React-ey way to go about exposing it in a hook.

Thanks for a great project!

Hot-reloading crashes when using sprites

When using sprites, hot reloading does not work anymore.
When react calls commitHookEffectListMount after hot reloading, this gets called in use-sound

React__default.useEffect(function () {
    if (sound) {
      sound.volume(volume);
      sound.rate(playbackRate);
    } // A weird bug means that including the `sound` here can trigger an
    // error on unmount, where the state loses track of the sprites??
    // No idea, but anyway I don't need to re-run this if only the `sound`
    // changes.
    // eslint-disable-next-line react-hooks/exhaustive-deps

  }, [volume, playbackRate]);

where sound.rate(playbackRate); fails in howler because self._sprite is undefined TypeError: self._sprite[sound._sprite] is undefined.

Your comment suggest you're already aware of this issue. This is more a inconvinience than anything serious, so i created this issue just to keep track of this bug.

3.0.0 breaks saying "Must use import to load ES Module"

Full error in Next.js (10.1.3)
Server Error
Error: Must use import to load ES Module: /Users/path/to/node_modules/use-sound/dist/index.js
require() of ES modules is not supported.
require() of /Users/path/to/node_modules/use-sound/dist/index.js from /Users/path/to/.next/server/pages/landing.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /Users/path/to/node_modules/use-sound/package.json.

FWIW:
It starts working as expected when I remove "type": "module" from the package.json

Release notes

Hi,

Could you add release notes to explain the changes in versions?
Also, npm is listing use-sound at v3.0.1, but your master branch is still at v2.1.1, this is a bit confusing. Could you explain your versioning strategy if not following semver ?

Thanks for maintaining this package, it's great!

[Question] How to bundle Howler?

Loading Howler asynchronously does not work for me.

How can I bundle Howler as a normal dependency like every other library too and ship it with my code?

Idea: Context provider for sounds theme

For use like this

import {spriteToTheme} from 'use-sound';
const soundTheme = {
    beep: 'theme1/beepFile.mp3',
    boop: ['theme1/boopFile.mp3', {
      playbackRate: 2,
      volume: 0.5,
    }],
    ...spriteToTheme(['theme1/sprite.webm', 'theme1/sprite.mp3'], {
      sprite: {
        badaBums: [0, 350],
      },
      playbackRate: 0.5,
    }),
    spriteNamespace: [// or maybe directly, without spriteToTheme ?
      ['theme1/sprite.webm', 'theme1/sprite.mp3'], {
        sprite: {
          badaBums2: [0, 350],
        },
        playbackRate: 0.5,
      }
    ]
};
const soundTheme2 = {
    beep: 'theme2/beepFile.mp3'
};
import {SoundThemeProvider} from 'use-sound';
<SoundThemeProvider theme={soundTheme || soundTheme2}>
  <ComponentWithSound />
</SoundThemeProvider>
const ComponentWithSound = () => {
  const [play] = useSound('beep');// use sound effect name from theme, instead of url
  const [play2] = useSound('badaBums');// use sound effect from spriteToTheme
  const [play3] = useSound('spriteNamespace/badaBums2');// use sound effect from directly defined sprite, by his namespace
  return <button onClick={play}>Boop!</button>;
};

The AudioContext was not allowed to start

I get a warning on page load even when I haven't actually called play() yet. It seems like this is an issue in howler because they call new AudioContext() immediately.

Is it possible to somehow workaround this in use-sound?

Update sound url on user interaction

Was wondering if there was a way to set the URL of useSound after render. I'm currently able to do it by creating a wrapper to pass down the url when I get it from cloudinary

Was thinking something like --

export default function SoundButton({ audio }) {
  // data returns the url from cloudinary after sending the public ID in the useEffect
  const [getAudio, data] = useAudio({ cloud_name: 'your-cloud-name' });

  React.useEffect(() => {
    // before this runs, useSound will take in the initial "undefined" value of data
    // if you pass it immediately, thus not being able to do anything
    getAudio({
      public_id: audio
    });
  }, []);

  // send no audio initially
  const [play] = useSound()


  // play(data) performs some type of update to useSound to reinitialize -- possibly a deps arr instead??
  // couple options for the API
  return <button onClick={() => play(data)}>Play</button>
}

Happy to help if this sounds possible! Thanks again for this hook, totally jazzed me to create my personal site finally.

How to use with Next.js?

Hi, l'm trying to use this in TypeScript with Next.js. I'm getting,

TS2307: Cannot find module '../../../public/sfx/correct-answer/test.mp3' or its corresponding type declarations.

I know my path is right. I don't know if I can get a webpack file-loader configured but even if I did, would I still get this TypeScript error?

Your library looks really cool and I'd like to use it so would appreciate any help. I couldn't find much by searching the web.

Path for URL doesn't play sound unless I import it

Hello!
Firstly, this appears to be such a wonderful tool. Thank you guys for developing it. My issue is that when I try to play a sound using the relative path (useSound('sound.mp3'), the sound does not play. When I import it on the top like "import sound from './sound.mp3', and useSound(sound), in that case, it works. I am planning to work with a few hundred audio clips so it's a bit messy to import every single one. I am also relatively new to React so I might be missing something. Any help would be awesome!

Add Caching

Is it possible to re-use the same sound data across multiple instances of the useSound hook when the URL is exactly the same?

I have many instances of the hook on the page, and tons of requests are being sent. Sure, there's caching and the server returns a 304 Not Modified, but I'm actually triggering my server's abuse rate limiting measures from how many times this request is being made.

Screen Shot 2020-06-04 at 4 45 35 PM

Thoughts?

Question: How capable is this?

Not sure how to ask, but how far can one go with this hook? Could it power an entire music streaming application, like a Spotify style app, or is it really more geared toward a little sound effect here and there and nothing more? Just curious how you conceptualize it in your mind...it feels like the latter from your post, but I thought I’d ask you first before reaching any conclusions :-)

Vanilla JS version?

Hi, firstly... this is amazing! Really well done. Are there any plans to release a vanilla JS version that doesn't require React or some other framework? Would love to use this on a side-project I'm working on but it's non-react.

Thanks

Unable to load audio files, browser requests "0.js"

I've tried both importing the mp3s via webpack, and referring to them by public URL. I've verified I can access the public files via a browser directly, and webpack seems to pick up the mp3s via the other method without issue.

However when using the hook, eg:
import sfxApplauseLight from "../audio/applause-lite.mp3";
...
const [sfxOneLineWon, sfxOneLineWonData] = useSound(sfxApplauseLight);

OR

const [sfxOneLineWon, sfxOneLineWonData] = useSound('/audio/applause-lite.mp3');

I get the error:

app.js:106 Uncaught (in promise) ChunkLoadError: Loading chunk 0 failed.
(error: http://localhost:3000/game/7/0.js)
    at Function.requireEnsure [as e] (http://localhost:3000/app.js:106:26)
    at eval (webpack-internal:///./node_modules/use-sound/dist/use-sound.esm.js:87:25)
    at commitHookEffectListMount (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:19731:26)
    at commitPassiveHookEffects (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:19769:11)
    at HTMLUnknownElement.callCallback (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:188:14)
    at Object.invokeGuardedCallbackDev (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:237:16)
    at invokeGuardedCallback (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:292:31)
    at flushPassiveEffectsImpl (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:22853:9)
    at unstable_runWithPriority (webpack-internal:///./node_modules/scheduler/cjs/scheduler.development.js:653:12)
    at runWithPriority$1 (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:11039:10)

It seems like no matter what I pass as the argument to useSound, url or import, it seems to try to request "0.js" which fails (since it doesn't exist, and also it gets an unauthorised response).

Unsure what I'm missing here but it must be something!

Compatible 2.0.1 -> 2.0.2 version breaks require support in webpack build

Our package.json uses the compatible flag:

"use-sound": "^2.0.1",

Upon running the built module, we get the error:

± |feat-pixi-canvas-fallback {3} U:2 ✗| → node dist/server.js
node:internal/modules/cjs/loader:1125
      throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath);
      ^

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /home/ljp/dev/pdm/ruthless-server/node_modules/use-sound/dist/index.js
require() of ES modules is not supported.
require() of /home/ljp/dev/pdm/ruthless-server/node_modules/use-sound/dist/index.js from /home/ljp/dev/pdm/ruthless-server/dist/server.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /home/ljp/dev/pdm/ruthless-server/node_modules/use-sound/package.json.

    at new NodeError (node:internal/errors:278:15)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1125:13)
    at Module.load (node:internal/modules/cjs/loader:973:32)
    at Function.Module._load (node:internal/modules/cjs/loader:813:14)
    at Module.require (node:internal/modules/cjs/loader:997:19)
    at require (node:internal/modules/cjs/helpers:92:18)
    at Object.use-sound (/home/ljp/dev/pdm/ruthless-server/dist/server.js:5224:18)
    at __webpack_require__ (/home/ljp/dev/pdm/ruthless-server/dist/server.js:5291:42)
    at eval (webpack-internal:///./app/src/notification/components/NotificationSystem.js:11:67)
    at Module../app/src/notification/components/NotificationSystem.js (/home/ljp/dev/pdm/ruthless-server/dist/server.js:906:1) {
  code: 'ERR_REQUIRE_ESM'
}

Though the NotificationSystem.js module uses import syntax and works on a dev server, in the build it fails I guess because of some webpack internal using a require statement instead.

I believe this is due to this change #71 d7d9ef9

Now, this likely points to a lurking issue in my webpack config which can probably be fixed, and a quick workaround is just to specifically use 2.0.1 as the package version, however I'm not sure that changing that flag (type: "module") is truly a compatible, minor change (as well, I feel that that particular change should have bumped the version number which was changed a couple of commits prior).

rerender for each instance of use-sound

When using multiple use-sound instances, the page re-renders its content depending on how many hooks are used.

F.e.:

const Page = () => {
  const sound1 = useSound(sample1)
  const sound2 = useSound(sample2)
  const sound3 = useSound(sample3)

  console.log('render')
  return (<>hello</>)
}

will log render like 14 times. See this codesandbox.

Is this the expected behaviour?

How can i loop ?

Hello, there is a way to loop an mp3 ?
I tried to see the documentation of Howler but i don't understand how to integrate it.

Stopping sound in sprite by id does not work

If a sprite is used in the hook:

const [play, { stop, isPlaying }] = useSound(sprite, {
    sprite: {
      SHEEP_L: [43000, 1306.1224489795932],
      WINNING: [58000, 2351.020408163265]
    }
});

and a sound is played by id
play({ id: "WINNING" });

that sound can't be stopped by using stop(id)
stop("WINNING")

Only stopping the whole sprite works (by not supplying the id in the stop method)

See this project for example: https://codesandbox.io/s/epic-hodgkin-s25zu

to fix crashes with <sprite>

you wrote in the example:

To play a specific sprite, we'll pass its id when calling the play function:

<button
onClick={() => play('laser')}

but must be onClick={() => play({ id: 'laser' })}

forceSoundEnabled option is ambiguous

In order to implement a mute button, and given the current state of my component tree, I wanted to do the following as I expected forceSoundEnabled to be a forced version of soundEnabled :

const [play] = useSound('whatever.mp3', {soundEnabled: true})

play({
  forceSoundEnabled: false,
})
// Sound is played 😟

It appears that forceSoundEnabled only do something if soundEnabled is false (see: https://github.com/joshwcomeau/use-sound/blob/master/src/index.ts#L91)

edit: The example here seems dumb since I call play with forceSoundEnabled set to false, in real life, forceSoundEnabled is computed from a prop.

Loops?

Are loops possible?
How?

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function

Hey interesting issue - I'm redirecting a user after a sound and it looks like potentially a useEffect within use-sound isn't returning a way to unmount. Thoughts?

  const [play,] = useSound(beepSfx)
  const history = useHistory()

  const onBeep = useCallback(() => play())

  const onCreate = useCallback(() => {
    onBeep()
    history.push(ROUTES.MANAGE_BRANDS)
  })

Console errors:

index.js:1 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
    in WelcomeSelectPage (created by Context.Consumer)
  | console.<computed> | @ | index.js:1
-- | -- | -- | --
  | r | @ | react_devtools_backend.js:6
  | printWarning | @ | react-dom.development.js:88
  | error | @ | react-dom.development.js:60
  | warnAboutUpdateOnUnmountedFiberInDEV | @ | react-dom.development.js:23192
  | scheduleUpdateOnFiber | @ | react-dom.development.js:21200
  | dispatchAction | @ | react-dom.development.js:15682
  | (anonymous) | @ | index.ts:105
  | (anonymous) | @ | howler.js:1856
  | setTimeout (async) |   |  
  | _emit | @ | howler.js:1855
  | _ended | @ | howler.js:1920
  | setTimeout (async) |   |  
  | playWebAudio | @ | howler.js:843
  | play | @ | howler.js:855
  | (anonymous) | @ | index.ts:103
  | (anonymous) | @ | select.js:37

Line 37 is onBeep declaration

Make Howler a peer dependency.

Can we make Howler a peer dependency?
So people can upgrade to the latest howler without the need for you to upgrade the version every time.

TS2688: Cannot find type definition file for 'howler'.

After I npm install use-sound, my gatsby build is complaining about this line:

node_modules/use-sound/dist/types.d.ts:1:23 - error TS2688: Cannot find type definition file for 'howler'.

1 /// <reference types="howler" />
                        ~~~~~~

node_modules/use-sound/dist/types.d.ts:20:12 - error TS2304: Cannot find name 'Howl'.

20     sound: Howl | null;
              ~~~~

I think perhaps you need to move @types/howler from devDependencies to just plain old dependencies?

Update sprite object based on state values

Package version: 1.2.0
React version: 16.12.0

I'm loading a sprite from my database and everything works as expected for setting the sprite URL based on a state value. However, the spriteMap is not updating when the data loads and state changes. If I hardcode the spriteMap ids and times everything works. Is it possible to change the sprite object values after defining them when first calling useSound?

Here is a CSB of what I'm trying to get working: https://codesandbox.io/s/great-agnesi-s4x6x?file=/src/App.js

how to run this project ?

i tried npm start but it just showing Compiled successfully how to open this site in local server ?which port is using it

On IOS, after switching activities, sounds are not played anymore

I did not encountered the issue myself and I tried to gather as much informations as possible.

When switching activities on IOS (tested on 13.4.1), sounds does not play anymore. The only found workeround so far is to refresh the page.

So far it seems to affect all IOS users, the issue was encountered on my app but also on https://joshwcomeau.com/

Everything works fine on android.

Works in local not in Amplify Deployment

It does not work after I deploy my react application but it works fine in local.

I use use-sound from within a useEffect as it looks at an array of messages.

What do I do?

Help, how to use with React + Typescript

Hello,

I would love to use this amazing tool, but I'm not able to use with React+Typescript (I've just started to learn them). Tried multiple times, but I've encountered with 2 main error.

  1. I'm not able to import from my folder the mp3 as I get this TS error : Cannot find module or its corresponding type declarations.ts(2307)

  2. type 'PlayFunction' is not assignable to type 'MouseEventHandler'

I hope someone can help me

Thanks! :) @joshwcomeau

IDM download sound

This is your sample code , If we have installed Internet Download Manager (IDM) in our machine, it display download dialog when useSound executed, How we can prevent it?

import useSound from 'use-sound';

import boopSfx from '../../sounds/boop.mp3';

const BoopButton = () => {
  const [play] = useSound(boopSfx);

  return <button onClick={play}>Boop!</button>;
};

Allow multiple sources as an array

This project is very cool, but I have noticed that you force the sources to be an array directly in src/index.ts
Example
src: [url],
This actually disables one of Howler's features to allow multiple sources. It would be neat if you could allow us to pass an array or a string, as intended!

initial playbackRate and volume has no effect

If I set an initial playbackRate or volume other than 1 and never change them, it will have no effect.
Instead I must use the play options as follow play({playbackRate: 0.5}).

This is because there is no sound when the following effect run for the first (and in my case, also last) time: https://github.com/joshwcomeau/use-sound/blob/master/src/index.ts#L74

The workaround is to use the play options instead.

Anyway, great work and great blog post !
I'm working on a side project that will make good use of sounds (I hope) thanks to you !

Add tests

This is a great tool. Please consider adding tests so future development is easier for those of us who may want to add functionality.

Sounds choppy with useEffect

Hey Josh, thanks for making this brilliant library, I was trying to play the MP3 on mount with useEffect but it sounds like playing with multiple times. What do you think caused the issue?

Sound played with delay on phone

Sound played with delay on phone

I noticed that the sound is reproduced perfectly, at the right times on the PC; on the contrary on smartphones it is reproduced with a slight delay.

Sounds working locally but not after deploy

My sounds are working locally, but not after I deploy...

Browser: Chrome
Setup: create-react-app
File formats: tried mp3 and m4a
File paths: relative
Stored in public directory?: no

isPlaying does not work correct

I'm starting the sound on page load directly:

const [play, {stop, isPlaying}] = useSound(filePath);
  useEffect(() => {
    play();
    return () => {
      stop();
    };
  }, [play, stop]);
console.log(isPlaying)

isPlaying is logging at some point true even if there is no sound playing as the play() set the isPlaying always to true.

setIsPlaying(true);

In the chrome console is seee the following error: The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page..

My current workaround is to use the events from howler which looks to work fine:

const [isPlaying, setIsPlaying] = useState(false);
const [, {sound}] = useSound(filePath);
useEffect(() => {
  sound?.on('play', () => setIsPlaying(true));
  sound?.on('stop', () => setIsPlaying(false));
  sound?.on('end', () => setIsPlaying(false));
  sound?.on('pause', () => setIsPlaying(false));
}, [sound]);

Race condition with url

If you have the url change very quickly after the first call to useSound() (in my case from an empty string to the real url), then the hook will keep playing the old one.

This is because useOnMount to load howler is called with the first url value it sees, and if loading howler completes only after I changed the url, the howler instance is created with the old url and not updated.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.