react-hook-sound
v0.0.9
Published
A React hook for playing sounds.
Readme
react-hook-sound
A React hook for playing sounds.
Uses Howler.js under the hood.
One thing it adds on top of Howler.js is an optional per-second rate limit for each sound.
Not an exhaustive wrapper around Howler.js. Only implements features as needed.
Install
npm install react-hook-soundUsage
To keep the hook simple, useCreateSoundPlayer only cares about the config object you pass in on mount. Dynamically changing the config object will not change the sounds; it's not supported.
import { useCreateSoundPlayer } from "react-hook-sound";
function Component() {
const {
playSound,
stopSound,
globalVolume,
setGlobalVolume,
globalMuted,
setGlobalMuted,
} = useCreateSoundPlayer({
sounds: {
chime: {
src: ["/chime.mp3"],
// Play at 50% of the global volume
volume: 0.5,
},
bell: {
src: ["/bell.mp3"],
// Only 10 bells can be played per second
rateLimit: 10,
},
},
});
return (
<div>
{/* Global volume control */}
<label>
<input
type="range"
value={globalVolume}
onChange={(e) => setGlobalVolume(Number(e.target.value))}
/>
<span>{globalVolume}</span>
</label>
<button
onClick={() =>
playSound("chime", {
// Play at 25% of the global volume this one time
volume: 0.25,
})
}
>
Play chime
</button>
<button onClick={() => playSound("bell")}>Play bell</button>
</div>
);
}The intended usage is for you to pass in a static config object that maps all of the sounds you want to play in a top-level component like App.tsx or main.tsx, and then pass down the returned instance (or parts of it) to child components.
Option 1: Using createSoundPlayerContext() (Recommended)
The easiest way to share sounds across your app is with createSoundPlayerContext(), which creates a context provider and hook for you:
// sound.tsx
import { createSoundPlayerContext, Config } from "react-hook-sound";
type SoundKey = "click" | "win" | "lose";
const soundConfig: Config<SoundKey> = {
sounds: {
click: { src: ["/sounds/click.mp3"] },
win: { src: ["/sounds/win.mp3"] },
lose: { src: ["/sounds/lose.mp3"] },
},
};
export const { SoundPlayerProvider, useSoundPlayer } =
createSoundPlayerContext(soundConfig);
// App.tsx
import { SoundPlayerProvider } from "./sound";
function App() {
return (
<SoundPlayerProvider>
<YourComponents />
</SoundPlayerProvider>
);
}
// AnyComponent.tsx
import { useSoundPlayer } from "./sound";
function AnyComponent() {
const { playSound } = useSoundPlayer();
return <button onClick={() => playSound("click")}>Click me</button>;
}Option 2: Manual Context Setup
Alternatively, you can manually wire up context yourself:
// main.tsx
const SoundContext = createContext<ReturnType<typeof useCreateSoundPlayer> | null>(
null
);
const SoundProvider = ({
sound,
children,
}: {
children: React.ReactNode;
sound: ReturnType<typeof useCreateSoundPlayer>;
}) => {
return (
<SoundContext.Provider value={sound}>{children}</SoundContext.Provider>
);
};
const Root = () => {
const sound = useCreateSoundPlayer({
sounds: {
chime: { src: ["/chime.mp3"] },
},
});
return (
<SoundProvider sound={sound}>
<App />
</SoundProvider>
);
};
// Child.tsx
const ChildComponent = () => {
const { playSound } = useContext(SoundContext);
return <button onClick={() => playSound("chime")}>Play chime</button>;
};Type safety
Here's how you can configure your sounds in one file and then export a common type for use in other files so that playSound can only be called with valid sound keys.
// sound.ts
import { useCreateSoundPlayer, Config, SoundPlayer } from "react-hook-sound";
type SoundKey = "bet" | "win";
export type MySoundPlayer = SoundPlayer<SoundKey>;
export const soundConfig: Config<SoundKey> = {
sounds: {
bet: {
src: ["/bet.mp3"],
},
win: {
src: ["/win.mp3"],
},
},
};Now we can use soundConfig in our root component and pass playSound to a child:
// App.tsx
import { soundConfig, MySoundPlayer } from "./sound";
const App = () => {
const { playSound } = useCreateSoundPlayer(soundConfig);
return <Child playSound={playSound} />;
};
// Child.tsx
const Child = ({ playSound }: { playSound: MySoundPlayer["playSound"] }) => {
return (
<button
onClick={() =>
// playSound knows that "bet" is a valid sound key
playSound("bet")
}
>
Play bet
</button>
);
};Volume Controls
The hook provides two levels of volume control:
- Global volume: Affects all sounds played by the hook (
setGlobalVolume) - Sound-specific volume: Set in the sound config or in
playSoundoptions
These volumes multiply together. For example, with globalVolume: 0.8 and playing a sound with volume: 0.5, the final volume will be 0.8 × 0.5 = 0.4 (40%).
Limitations
- Not Dynamic: Sound configurations cannot be changed after mounting
- Not a Complete Wrapper: Only implements a subset of Howler.js functionality
- TypeScript Required: This library ships as TypeScript; it's not compiled to JavaScript
