How to play sound effects in React mobile web applications

Today I went searching for a JavaScript module that plays sound effects when a user taps on a React component. I didn't see any open-source packages that worked well for mobile browsers. Here's a simple home-made solution:

Example

<SoundEffect audioUrl="https://s3-us-west-2.amazonaws.com/codyromano/project-aurae/sound-effects/sound-effect-water-splash.mp3">
   <button>Click me for sound!</button>
</SoundEffect>

Code

import React from 'react';
import PropTypes from 'prop-types';

// Ensures that we don't replace existing onClick handlers
// on the wrapped child components
const wrapFn = (targetFn, wrapper) => (...args) => {
  wrapper(...args);
  return targetFn(...args);
};
const emptyFn = () => {};

/**
* A container that plays a sound effect when the user interacts
* with any of its child components.
*/
export class SoundEffect extends React.Component {
  static propTypes = {
    listenerType: PropTypes.oneOf(['onClick', 'onMouseDown']),
    onError: PropTypes.func
  };
  static defaultProps = {
    listenerType: 'onClick',
    onError: console.error.bind(console)
  };
  constructor(props) {
    super(props);
    this.playSound = this.playSound.bind(this);
  }
  playSound() {
    try {
      const audio = document.createElement('audio');
      audio.src = this.props.audioUrl;
      audio.play();
    } catch (error) {
      this.props.onError(error);
    }
  }
  render() {
    const { listenerType, children } = this.props;
    /* An alternative would be rendering <audio>. Though this is more declarative,
    it requires adding an additional wrapper in the DOM, and playback will stop
    when SoundEffect unmounts. It's a trade-off... */
    return React.Children.map(
      children,
      child => React.cloneElement(child, {
        [listenerType]: wrapFn(child.props.onClick || emptyFn, this.playSound)
      })
    );
  }
}

If you prefer to implement SoundEffect through a higher-order component (HOC), use this code:

export const withSound = (soundOptions = {}) => (Component) => (props) => (
  <SoundEffect {...soundOptions}>
    <Component {...this.props} />
  </SoundEffect>
);

For example:

const MyButton = () => (<button>Click here</button>);
export default withSound({ audioUrl: 'example.mp3'})(MyButton)
Cody Romano

Cody Romano

I'm a frontend software engineer in Amazon Lab126. This is my personal website for side projects & ramblings.

Read More