Creating a Sound Matrix

As a hobbyist musician, I love programming projects that bridge the gap between music and technology. A Sound Matrix is a series of squares that, if enabled, play sounds as the beat loops. Let’s build one using React!

Getting Started

For a starting point, we’ll rely on create-react-app. If you don’t have it installed, run:

npm install -g create-react-app

This globally installs create-react-app, a tool that assists in the initial configuration of a React project. Create a new application and cd to the new directory using the commands below.

create-react-app sound-matrix --use-npm
cd sound-matrix

Create-react-app is incredibly convenient, but provides more than we need for creating a sound matrix. Remove the contents of the src directory and create a new blank index.js file.

rm -f src/* && touch src/index.js

For styling, we’ll use Bootstrap CSS. A CDN link for Bootstrap can be found here and should be added to the header of public/index.html.

Hello World

To confirm housekeeping was completed successfully, add the following to src/index.js. This only outputs a Hello World button, but ensures that React is building correctly and Bootstrap styling is applied.

To run React, use the terminal command npm run start. If you would like to stop your app, control c. Once started, the following should be displayed when visiting http://localhost:3000.

React and State

In React, most interactivity is driven by state. When state changes, React renders again to display the update. Consider the following:

An interval runs to adjust state to the opposite value every 500ms. The render function displays a button with its color being dictated by this.state.example. For added visibility, console.log() is called each render.

Stateless vs Stateful Components

Stateful components, like the example above, contain state within the component’s scope and modify state as needed.

Stateless components, on the other hand, expect to be provided read-only values through props. Props typically contain data to display or references to functions for handling actions, such as a click.

Creating the SoundSquare Component

For our Sound Matrix, state will live in the stateful SoundMatrix component. However, a stateless SoundSquare component will be used to display only the current version of state as passed through props.

Since stateless components only rely on values obtained through props, their construction can be light weight. Below is a SoundSquare function component which should be added to src/index.js outside the existing SoundMatrix component. This renders a stylized square based on props provided.

To render SoundSquares, modify SoundMatrix to the following. This creates three instances the component, each with different prop values.

Depending on the props, conditional logic within SoundSquare renders different color squares.

Adding State

Now that squares can render, let’s make them dynamic by including state. Add a constructor to SoundMatrix with the following code.

Constructors run before a component mounts and mainly initialize local state. The above constructor defines all the state values for this project, but focus only on the matrix state parameter for now.

When the constructor runs, state will build and obtain a value for matrix from the buildMatrixState function. Since this has not been defined, let’s create it.

buildMatrixState

buildMatrixState calculates the total amount of squares required and returns an object with unique keys for each square. For each square key, a squareDetails object is created, containing vital information about the square.

buildMatrixState should be added outside the SoundMatrix component. The commented lines should remain commented and will be addressed later.

Here is a console visualization of the matrix[key] = {details} structure.

Building Rows and Squares

Although square details now exist within state, state is not currently being rendered.

Below are a pair of recursive functions that build squares in rows. rowBuilder builds rows and calls squareBuilder to build squares. Within squareBuilder, SoundSquare components are created with the necessary props.

These two functions should be added to the SoundMatrix component after the constructor and before render.

Rendering rowBuilder

With rowBuilder kicking off the process to create all the required rows and squares, including it in SoundMatrix’s render now displays rows and squares as defined by this.state.rowCount and this.state.rowLength.

Modify SoundMatrix’s render to match the following.

When rendered, the squares are displayed.

Handling Clicks

Squares are now being rendered, but no action occurs when clicked.

When SoundSquare components are created by squareBuilder, a handleClick prop of this.handleClick is included. However, because this.handleClick does not currently exist within SoundMatrix’s scope, props.handleClick within SoundSquare is undefined. Once this.handleClick is defined, the SoundSquare will know how to handle click actions.

Also, uncomment the this.handleClick line in SoundMatrix’s constructor. Doing so binds this’ scope, allowing state to be correctly referenced by the handleClick function.

Now, squares correctly register clicks and change color when enabled.

Create the Beat and Loop it

To add life to the Sound Matrix, we need a beat that repeats continuously.

Below, the togglePlaying function determines if playing is enabled. If so, an interval is created and beat loops indefinitely, incrementing or resetting when applicable. If not, the interval is cleared and the beat reset.

This should be added to the SoundMatrix component after the constructor and before render. The this.togglePlaying line should also be uncommented.

Including a Play Button

With beat added, all that’s left is to add a play button for control. Replace render with the following.

When playing, the sound matrix now continuously loops, with the yellow line signifying the current beat.

Adding Sounds

The last step is to hook up the sounds.

The sounds used in this demo can be found here. After downloading, save them to your src/ folder and import them by adding the following to the top of your index.js file after the React imports.

After importing, we create a sounds object which maps a row number to a given audio file.

When squareDetails are built by buildMatrixState, the row is determined and a new Audio is created using the sounds[row] reference. There are three lines which must be uncommented from buildMatrixState to allow for this to occur.

Additionally, squareBuilder and SoundSquare each contain a line which needs to be uncommented.

Uncommenting this line creates a sound prop with the value of square.sound, which was just defined by the lines uncommented in buildMatrixState.

Here, SoundSquare will play() the associated audio file if the square is active and the beat matches.

Make some Music

After uncommenting, the Sound Matrix is fully operational. To increase the size of the matrix, adjust the rowCount and rowLength. Keep in mind, you may need to include additional sounds references for new audio files.

If your matrix is having issues, retrace the steps in this guide, check for React errors, or look at the completed source code on my GitHub.

Conclusions

Hopefully you enjoyed building a Sound Matrix and learned more about React along the way.

Enjoy posts like these? Follow me on Twitter @andepaulj

Solutions engineer, music enthusiast, dog dad