• Stars
    star
    130
  • Rank 277,575 (Top 6 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created almost 9 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Middleware for playing audio / sound effects using Howler.js

Redux Sounds

build status npm version Coverage Status

Redux middleware that lets you easily trigger sound effects on actions. Makes it completely trivial to do so, by adding a meta property to any action:

export function danceMoves() {
  return {
    type: 'DANCE_MOVES',
    meta: {
      sound: {
        play: 'groovyMusic'
      }
    }
  };
}

Uses Howler.js under the hood, which uses Web Audio API when available, with a graceful fallback to HTML5 Audio.

Installation

Preferred: NPM

npm i -S redux-sounds

Also available: UMD

UMD builds are also available, for single-file usage or quick hacking in a JSbin. Simply add dist/redux-sounds.js or dist/redux-sounds.min.js to your file in a <script> tag. The middleware will be available under ReduxSounds.

Setup

soundsMiddleware works similarly to other Redux middleware, and can be pre-loaded with sound data.

Here's an example setup:

/* configure-store.js */

import { createStore, combineReducers, applyMiddleware } from 'redux';
import soundsMiddleware from 'redux-sounds';

import { gameReducer } from '../reducers/game-reducer';

// Our soundsData is an object. The keys are the names of our sounds.
const soundsData = {
  // If no additional configuration is necessary, we can just pass a string  as the path to our file.
  endTurn: 'https://s3.amazonaws.com/bucketName/end_turn.mp3',

  // Alternatively, we can pass a configuration object.
  // All valid howler.js options can be used here.
  winGame: {
    src: [
      'https://s3.amazonaws.com/bucketName/win_game.mp3',
      'https://s3.amazonaws.com/bucketName/win_game.wav'
    ],
    volume: 0.75
  },

  // Audio sprites are supported. They follow the Howler.js spec.
  // Each sprite has an array with two numbers:
  //   - the start time of the sound, in milliseconds
  //   - the duration of the sound, in milliseconds
  jumps: {
    src: ['https://s3.amazonaws.com/bucketName/jumps.mp3'],
    sprite: {
      lowJump: [0, 1000],
      longJump: [1000, 2500],
      antiGravityJump: [3500, 10000]
    }
  }
};

// Pre-load our middleware with our sounds data.
const loadedSoundsMiddleware = soundsMiddleware(soundsData);

// Use as you would any other middleware.
const store = createStore(gameReducer, applyMiddleware(loadedSoundsMiddleware));
// (Using the condensed createStore released in Redux v3.1.0)

Howler has much more advanced capabilities, including specifying callbacks to run when the sound has completed (or failed to complete), looping sounds, fading in/out, and much more. See their documentation for the complete list.

Usage

Once your store is created, dispatching actions that trigger sounds is simple.

Using the convention established in the rafScheduler Middleware example, a new meta property can be attached to actions. This meta property should have a sound key, and its value should be that of a registered sound.

Continuing from our example above, we have 5 possible sounds: endTurn, winGame, and 3 flavors of jumps.

The Howler methods other than play follow almost the same API as Howler:

Howler

sound.fade(1, 0, 1000, id);
sound.stop(id);

redux-sounds action

// fade
meta: {
  sound: {
    fade: ['endTurn', 1, 0, 1000];
  }
}

// stop
meta: {
  sound: {
    stop: 'endTurn';
  }
}

For sprites, separate the sound name from the sprite name with a period (.). A couple examples of the actions we can dispatch:

/* game-actions.js */

export function endTurn() {
  return {
    type: 'END_TURN',
    meta: {
      sound: {
        play: 'endTurn'
      }
    }
  };
}

export function lowJump() {
  return {
    type: 'LOW_JUMP',
    meta: {
      sound: {
        play: 'jumps.lowJump'
      }
    }
  };
}

export function lowJumpStop() {
  return {
    type: 'LOW_JUMP_STOP',
    meta: {
      sound: {
        stop: 'jumps.lowJump'
      }
    }
  };
}

export function lowJumpFade() {
  return {
    type: 'LOW_JUMP_STOP',
    meta: {
      sound: {
        fade: ['jumps.lowJump', 0, 1, 2]
      }
    }
  };
}

Note: It is worth noting that it is unlikely that you'll need to create new actions for your sound effects; You'll probably want to just add meta properties to pre-existing actions, so that they play a sound in addition to whatever else they do (change the reducer state, trigger other middleware, etc).
Also Note: When a sound is playing multiple times at once, Howler methods (stop, fade, pause, etc.) will apply to all playing instances

Adding more sounds via actions

/* add-sounds-actions.js */
const coinSoundsData = {
  heavyCoin: 'https://s3.amazonaws.com/bucketName/gold_coin.mp3',
  lightCoin: {
    src: 'https://s3.amazonaws.com/bucketName/gold_coin.mp3', // just lower volume
    volume: 0.75
  },
  randomCoins: {
    src: ['https://s3.amazonaws.com/bucketName/coin_collection.mp3'],
    sprite: {
      one: [0, 1000],
      two: [1000, 2500],
      three: [3500, 10000]
    }
  }
};

export function addCoinSounds() {
  return {
    type: 'ADD_COIN_SOUNDS',
    meta: {
      sound: {
        add: coinSoundsData
      }
    }
  };
}

Callbacks

The Howler callbacks onplay, onstop, and onend are currently supported

const winPopup = {
  type: 'SHOW_WIN_POPUP',
  payload: 'You won'
};

const soundsData = {
  randomCoins: {
    src: ['https://s3.amazonaws.com/bucketName/coin_collection.mp3'],
    sprite: {
      one: [0, 1000],
      two: [1000, 2500],
      three: [3500, 10000]
    },
    onend: (id, dispatch) => dispatch(winPopup)
  }
};

Playlist

It is preferable to have your playlist merged as one continuous sounds. If that's not possible, you can dispatch a "playlist" action like the example below.

/* playlist-action.js */
export function playlistSounds() {
  return {
    type: 'PLAYLIST_SOUNDS',
    meta: {
      sound: {
        list: [
          { play: 'jumps.lowJump' },
          { play: 'jumps.longJump' },
          { play: 'endTurn' }
        ]
      }
    }
  };
}

Troubleshooting

Unregistered sound

When you dispatch an action with a meta.sound property, redux-sounds looks for a registered sound under that name. If it cannot find one, you'll get a console warning:

The sound 'foo' was requested, but redux-sounds doesn't have anything registered under that name.

To understand why this is happening, let's examine the link between registering a sound when the store is created, and triggering a sound when an action is dispatched.

When you create your store, you pass in an object like so:

const soundsData = {
  foo: 'path/to/foo.mp3'
};

The keys in that object must correspond to the value specified when you dispatch your action:

dispatch({
  type: 'ACTION_NAME',
  meta: {
    sound: { play: 'foo' }
  }
});

Make sure these two values are the same!

Invalid sprite

When your meta.sound value has a period in it (eg. foo.bar), redux-sounds breaks it apart so that the first half is the sound name, and the second half is the sprite name.

If redux-sounds has a sound registered under 'foo', but that sound has no specified sprite for 'bar', you get a console warning that resembles:

The sound 'foo' was found, but it does not have a sprite specified for 'bar'.

It is possible that there is a typo, either in the meta.sound property or the sprite data passed in on initialization.

A missingSoundData error throws when I try loading the page.

Unlike other middleware, you cannot simply pass it to Redux as-is:

import soundsMiddleware from 'redux-sounds';

// DON'T do this:
const store = createStore(rootReducer, applyMiddleware(soundsMiddleware));

The reason for this is that before the store can be registered, you need to pass it data about which sounds it needs to handle.

You must first invoke soundsMiddleware with a soundsData object:

import soundsMiddleware from 'redux-sounds';
import { soundsData } from '../data/sounds';

// Important step:
const loadedSoundsMiddleware = soundsMiddleware(soundsData);

const store = createStore(rootReducer, applyMiddleware(loadedSoundsMiddleware));

Tests

To run: npm run test

Using Mocha for test-running, Chai Expect for assertions, and Istanbul for test coverage.

While test coverage is high, because the tests run in Node, and Node has no ability to play sounds, the actual output is not tested.

Because I've delegated these duties to Howler, though, I don't feel too bad about that. I check to make sure the right info is passed to Howler at the right time; Howler's tests can take it from there.

Planned functionality

Got ideas for must-have functionality? Create an issue and let's discuss =)

Contributions

Contributors welcome! Please discuss additional features with me before implementing them, and please supply tests along with any bug fixes.

License

MIT

More Repositories

1

react-flip-move

Effortless animation between DOM changes (eg. list reordering) using the FLIP technique.
JavaScript
4,080
star
2

guppy

🐠A friendly application manager and task runner for React.js
JavaScript
3,268
star
3

use-sound

A React Hook for playing sound effects
JavaScript
2,614
star
4

waveforms

An interactive, explorable explanation about the peculiar magic of sound waves.
JavaScript
1,430
star
5

panther

Discover artists through an infinite node graph
JavaScript
919
star
6

new-component

⚛ ⚡ CLI utility for quickly creating new React components. ⚡ ⚛
JavaScript
699
star
7

redux-vcr

📼 Record and replay user sessions
JavaScript
585
star
8

key-and-pad

🎹 Fun experiment with the Web Audio API 🎶
JavaScript
361
star
9

Tello

🐣 A simple and delightful way to track and manage TV shows.
JavaScript
329
star
10

tinkersynth

An experimental art project. Create unique art through serendipitous discovery.
JavaScript
282
star
11

beatmapper

A 3D editor for creating Beat Saber maps
JavaScript
271
star
12

blog

OLD VERSION of the joshwcomeau.com blog. Kept for historical purposes.
JavaScript
236
star
13

dark-mode-minimal

JavaScript
170
star
14

react-retro-hit-counter

🆕 Go back in time with this 90s-style hit counter.
JavaScript
162
star
15

dream-css-tool

JavaScript
119
star
16

react-collection-helpers

A suite of composable utility components to manipulate collections.
JavaScript
106
star
17

redux-favicon

Redux middleware that displays colourful notification badges in the favicon area.
JavaScript
105
star
18

nice-index

Atom package to rename `index.js` files to their parent directory names
CoffeeScript
82
star
19

react-europe-talk-2018

JavaScript
64
star
20

fakebook

A front-end Facebook clone, built with React and Redux
JavaScript
52
star
21

talk-2019

Slides for my 2019 talk, "Saving the Web 16ms at a Time"
JavaScript
52
star
22

understanding-react

Daily exploration of the React source code
42
star
23

talon-commands

Python
38
star
24

return-null

My React Europe 2017 lightning talk
JavaScript
35
star
25

explorable-explanations-with-react

JavaScript
35
star
26

word_dojo

JavaScript
18
star
27

react-boston-2018

My ReactBoston 2018 talk, The Case for Whimsy (Extended mix)
JavaScript
16
star
28

netlify-serverless-demo

JavaScript
16
star
29

css-for-js-flow-layout

HTML
14
star
30

whimsical-mail-client

JavaScript
14
star
31

ColourMatch

Search by Colour. Find photos with matching palettes.
CSS
12
star
32

talk-2020-react-europe

JavaScript
10
star
33

react-europe-workshop-confetti

JavaScript
10
star
34

plot

Experiments in pen plotting and generative art
JavaScript
9
star
35

sandpack-bundler-beta

JavaScript
8
star
36

react-play-button

JavaScript
7
star
37

react-europe-workshop-travel-site

JavaScript
7
star
38

deployed-screensaver

JavaScript
7
star
39

Uncover

📚 Aggregate new releases from your favourite authors. Built with Vuejs and Node
Vue
6
star
40

redux-vcr-todomvc

ReduxVCR integrated into TodoMVC.
JavaScript
5
star
41

react-letter-animation

A take on Mike Bostock's General Update Pattern, using React Flip Move.
JavaScript
5
star
42

Perseus

Gather info about your stargazers. Uses the GitHub GraphQL API
JavaScript
5
star
43

gatsby-preview-demo

Gatsby starter for a Contentful project.
JavaScript
5
star
44

words-with-strangers-redux

A universal redux version of my Meteor attempt at Words with Friends (online scrabble).
JavaScript
4
star
45

leitner

Keep track of your position in the 64-day Leitner calendar
JavaScript
4
star
46

empowered-development-with-gatsby

My Gatsby Days LA 2020 talk!
HTML
4
star
47

react-floaters

Spring-based scroll animation experiment with React.js
JavaScript
4
star
48

datocms-Gatsby-Portfolio-Website-demo

CSS
4
star
49

tetris

A simple tetris clone, in React and Redux, using Redux Saga
JavaScript
4
star
50

katas

A bunch of CodeWars challenge solutions. Part of an ongoing blogging effort at https://medium.com/@joshuawcomeau
JavaScript
4
star
51

react-europe-workshop-twitter-like

JavaScript
4
star
52

joshbot

The Discord bot for my Course Platform's community.
JavaScript
3
star
53

unlikely-friends

Don't mind me. Experiments with Gatsby themes
JavaScript
3
star
54

dont_eat_here_toronto

A Chrome extension that displays Toronto DineSafe restaurant inspection stuff on Yelp restaurant pages.
JavaScript
3
star
55

script-search

Find code used on the world's top sites
Python
3
star
56

basilica

JavaScript
3
star
57

yger

🚀⚡️ Blazing fast blog built with Gatsby and Cosmic JS 🔥
JavaScript
3
star
58

gatsby-dark-mode

CSS
2
star
59

Mars-Rover-HTML

An HTML/CSS Mars Rover simulation
CSS
2
star
60

generic-portfolio

An example of a generic portfolio (what NOT to do)
HTML
2
star
61

ember-todo

Don't mind me! Just a toy app to familiarize myself with Ember
JavaScript
2
star
62

mono-gatsby-apps

CSS
2
star
63

Aracari

A simple-as-possible budgeting web app. Because I suck at budgeting.
JavaScript
2
star
64

temp-project-wordle

JavaScript
2
star
65

AngelHack_rando

1st Place @ AngelHack TO. Built in 24h.
Ruby
2
star
66

tree-shake-test

JavaScript
2
star
67

gatsby-personalization

CSS
2
star
68

ssr-repro

CSS
2
star
69

react-fluid-window-events

React component for smooth, efficient resize/scroll handling.
JavaScript
2
star
70

Percentext

a jQuery plugin that lets you style text elements by width.
JavaScript
2
star
71

RequestKittens

The only API ridiculous enough to let you find cats by emotion.
JavaScript
2
star
72

book-demo

Demo of Git fundamentals
2
star
73

HungryBelly

An extension of the winning 24-hour project created for AngelHackTO
Ruby
1
star
74

art

Generative art experiments
JavaScript
1
star
75

elevator-simulator

WIP
JavaScript
1
star
76

RAFT

Utility for efficient, organized window-level event handlers
JavaScript
1
star
77

CLYWmparison_blogembed

A Yoyo Comparison tool, used by Caribou Lodge Yoyo Works
JavaScript
1
star
78

TicTacToe

JavaScript
1
star
79

TeeVee

A simple Meteor app to help me keep track of which episodes of TV shows I've seen.
JavaScript
1
star
80

RequestKittensDocs

The documentation / sales site for the RequestKittens API
JavaScript
1
star
81

foodshow

A silly weekend project, using the Unsplash API to display a food slideshow.
JavaScript
1
star
82

egghead-optimized-images-1

HTML
1
star
83

react-simple-canvas

React components that replicate the SVG interface, but renders to an HTML5 Canvas
JavaScript
1
star
84

munsell-colors

JavaScript
1
star
85

joshwcc

My portfolio/blog. Nowhere close to done yet.
Ruby
1
star
86

egghead-optimized-images-2

HTML
1
star
87

learn-webgl

Experiments for education with WebGL. Don't mind me.
JavaScript
1
star
88

Crowdfunder

A Kickstarter clone. Bitmaker Labs final assignment.
Ruby
1
star
89

MEAN_stack_starter

A ready-to-go initialized MEAN stack with tons of customizations.
CSS
1
star
90

egghead-videos

JavaScript
1
star
91

pixelminer

An idle game (à la cookie clicker), built to help me experiment with flowtype.
JavaScript
1
star
92

huddle

A Meteor app that aims to help patients have better access to their medical files, and get second opinions from physicians on the platform.
CSS
1
star
93

joshwcc_ver2

Attempt #2 at the joshw.cc portfolio site.
Ruby
1
star
94

Some-new-project

1
star
95

Advent-of-Code-2016

JavaScript
1
star
96

confetti-temp

JavaScript
1
star
97

redux-server-persist

JavaScript
1
star
98

Tori

Twitter, but for haikus.
JavaScript
1
star
99

classroom-q

Gatsby experimentation
CSS
1
star
100

fntest

CSS
1
star