Guitar Effects, Streaming Music, and ReactJS. A project for Flatiron School.

Chris Houghton
10 min readMar 7, 2022

This project called TubeScreamer I bring together React, Redux, Web Audio API, Tuna.js , Spotify API, Express, Axios and much more to create a not at all comprehensive just scratching the surface but still powerful guitar effects pedal board that allows you to stream your favorite songs off Spotify and rock it with your favorite artists.

I really wanted to do something with music for this one, I knew there had to be a way to plug my guitar into my laptop and rock away. I figured it out and am proud to unveil my Tube Screamer app, named after an iconic overdrive pedal.

Building with React

React

I began my project with the React Framework, React is a free and open-source front-end JavaScript library for building user interfaces based on UI components, maintained by meta. Components let you split the UI into independent, reusable pieces, and think about each piece in isolation.

Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called “props”) and return React elements describing what should appear on the screen.

basically a component is just that, a chunk of the UI, broken down to individual and manageable chunks of code.

import React, { Component } from 'react'export default class Test extends Component {  render() {    return <div>test</div>  }  }

I used a practice that programmers over time have developed when creating React apps, a convention where Presentation and Container components are created to compliment each other. A presentation component presents the data supplied by the container component and is concerned with how the UI looks. A Container component is where all of the logic and data is stored and manipulated then passed to the presentational component to display.

The Tube Screamer app is divided into three elements, the pedal board, the spotify player, and the search bar.

A user can search for a song, a list of songs will be displayed for the user to select and play. Then having rigged up their guitar to the computer the user can then make use of the guitar effects and play with the chosen song. In doing so this app becomes a learning tool, a cheap guitar amp, and a form of entertainment.

First Create React App

npm init react-app tube-screamer

Then build the authorization to Spotify API to get those sweet jams.

Get music from Spotify

Spotify API

Based on simple REST principles, the Spotify Web API endpoints return JSON metadata about music artists, albums, and tracks, directly from the Spotify Data Catalogue. Spotify Web API also provides access to user related data, like playlists and music that the user saves in the Your Music library. Such access is enabled through selective authorization, by the user.

To do we go to https://www.spotify.com and set up an account, then to https://developer.spotify.com/dashboard/, login and create a new app, follow the prompts and then get redirected to the dashboard and get the Client ID and Client Secret.

I set these up in an .env file located in the main file of the app

REACT_APP_CLIENT_ID=93938349fakecjf0f8f7vjf9fIDf7fjf8f90f
REACT_APP_CLIENT_SECRET=9d8fufnotf9f9f8889real7d8d9f0f0f9f8f8
//Access the id and secret inside the react app with
process.env.REACT_APP_CLIENT_ID;

And then we have to create a Authorization URL that takes us to spotify API where we can login to Spotify and then return to the app with a code in the URL localhost:3000/?code=AQBZFymaQge5etc…

//retrieve the code
const code = new URLSearchParams(window.location.search).get('code')

Spotify Web API will only accept requests from server side, the Web API docs use the Express Framework to speak to Spotify Web API so thats what I used as well.

Express

Express is a minimal and flexible Node.js based web application framework that provides a robust set of features for web and mobile applications. Express is built on top of Node.js, a Javascript environment with libraries to make it easy to write software, where Express extends Node specifically to make web servers easy to write.

To put plainly Express is being used as a server that will send and receive data from Spotify Web API, this is as easy as

npm install express
const express = require('express')
const server = express();
//because I made the server a completely separate project
server.listen(8008);
//as in 808 bass drop, not relevant, just a fun music reference

So then in the app we make a call to the Express server with

Axios

Axios is a Javascript library used to make HTTP requests from node.js or XMLHttpRequests from the browser and it supports the Promise API that is native to JS ES6. It can be used intercept HTTP requests and responses and enables client-side protection against Cross-site request forgery. It also has the ability to cancel requests.

I chose Axios to retrieve the response from the server as I wanted to venture out with using different tools as opposed to using fetch() , not that I have anything against fetch I was curious when I came across it in my research of this project.

Axios and Fetch are relatively the same although Axios provides easy to implement functions right out of the box e.g.

  • Response timeout
  • Backward compatibility
  • Automatic JSON data transformation
  • HTTP interceptors
  • Download progress
  • Simultaneous requests
  • Protection against XSFR

All of these can also be implemented with Fetch but will require due diligence before deciding which is right for you application.

To begin using Axios

npm install axios
import axios from 'axios'

Then create a getAuth() function that uses Axios to call to our server. Axios will .post to ‘http://localhost:8008/login' with the url code that Spotify sent for getting the access Token.

In the authorizationComponent of React we run the getAuth() function inside componentDidMount() so we don’t end up with undefined state and errors due to Axios.post returning a promise.

We are also using Spotify Web API Node, this is a universal wrapper/client for the Spotify Web API that runs on Node.js. It comes with helper functions for communicating with Spotify like .authorizationCodeGrant(code) for sending the code to Spotify and receiving an access token and then .setAccessToken(data.body.access_token) for storing access token.

npm install spotify-web-api-node --save
const SpotifyWebApi = require('spotify-web-api-node');

I used this to make authorization from Spotify API much easier. First by making an instance of SpotifyWebApi class with the client id, client secret, and redirect url object passed as ‘credentials’ parameter and then calling the SpotifyWebApi class method .authorizationCodeGrant() passing the code variable as a parameter, Spotify will then respond with the access token.

componentDidMount() {
if(code){
this.getAuth()
}
}
getAuth = () => {
axios.post('http://localhost:8008/login', {
code // code from url sent to server to send to Spotify
})
.then(response => {
let res = response.data
this.setState({
expiresIn: res.expiresIn,
accessToken: res.accessToken,
refreshToken: res.refreshToken})
})
.catch(err => {
console.log(err)
window.location = '/'
})
}
server.post('/login', (req, res) => {
const code = req.body.code
console.log(code)
spotifyApi = new SpotifyWebApi({
clientId: process.env.REACT_APP_CLIENT_ID,
clientSecret: process.env.REACT_APP_CLIENT_SECRET,
redirectUri: 'http://localhost:3000/'
})
spotifyApi.authorizationCodeGrant(code)
.then( data => {
res.json({
expiresIn: data.body.expires_in,
accessToken: data.body.access_token,
refreshToken: data.body.refresh_token
})
spotifyApi.setAccessToken(data.body.access_token);
spotifyApi.setRefreshToken(data.body.refresh_token);
}).catch(error => {
console.log(error)
res.sendStatus(400)
})
});

After the access token is received I set up a Redux state container, with a manageSpotify reducer, sending the access token to the Redux state.

Redux

Redux is a predictable state container for JavaScript apps. It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test. ... You can use Redux together with React, or with any other view library.

Redux takes away the responsibility from individual components to manage state, and pass it around all over your app through props.

I set up the reducer to manage the access token, the selected track and the search results from the SearchBar component which I will cover later on.

//manageSpotify Reducerexport default function manageSearch(state = {}, action) {
switch(action.type) {
case "SEARCH_RESULTS":
return {...state, searchQuery: action.results}
case "SELECT_TRACK":
delete state.searchQuery
return {...state, selectTrack: action.trackUri}
case "ACCESS_TOKEN":
return {...state, accessToken: action.accessToken}
default:
return state
}
}

So now we have our access code and it’s stored in Redux state and our server has access via

spotifyApi.setAccessToken(data.body.access_token);. 

The next step in this project is to search a song, get results, select track and play it.

Search, Select, Play

Here I created a search bar using React Bootstrap <Stack> and <Form.Control/>

<Form onSubmit={(event) => this.handleOnSubmit(event)}>
<Stack direction="horizontal" gap={3}>
<Form.Control
className="me-auto"
placeholder="Search Songs"
value={this.state.searchInput}
onChange={(e) => this.setState({searchInput:
e.target.value})}/>
<Button variant="secondary" type="submit">Submit</Button
</Stack>
</Form>

Upon entering a search parameter, using Axios, a post is sent to the server with the search query, the server sends a request to Spotify Api

//Call to server 
handleOnSubmit(event) {
event.preventDefault();
if(!this.state.searchInput){
console.log('Look up some stuff')
} else {
axios.post('http://localhost:8008/search', {
searchQuery: this.state.searchInput
})
.then(response => {
this.props.searchQuery(
response.data.map(track => {
return {
artist: track.artists[0].name,
artistUri: track.artists[0].uri,
album: track.album.name,
albumUri: track.album.uri,
name: track.name,
imgUrl: track.album.images[2].url,
trackUri: track.uri
}
})
)
})
.catch(err => {
console.log(err)
})
this.setState({
searchInput: '',
});
}
//The server call to Spotifyserver.post('/search', (req, res) => {
const searchQuery = req.body.searchQuery
console.log(searchQuery)
spotifyApi
.searchTracks(searchQuery)
.then(response => {
res.json(response.body.tracks.items)
})
.catch(err => {
console.log(err)
res.sendStatus(400)
})

Once Spotify sends the results to the server and the server sends them to the app the results are stored in the Redux store.

const mapDispatchToProps = (dispatch) => {
return {
searchQuery: results => dispatch({type: "SEARCH_RESULTS",
results})
}
}

From the searchResults component the search results get displayed in a drop down menu, the user can then select a track to play.

const mapStateToProps = (state) => {
return {
results: state.searchQuery
}
}
<TrackSearchResults results={this.props.results} selectTrack={this.props.selectTrack} />

After the user selects a song, a Spotify URI code for the song is then saved in the Redux store and the trackSearch results are removed from the store to create a clean slate for the next search. The track URI is then sent to the Spotify Web Player.

For the player I chose to use react-spotify-web-playback library, a Spotify player with Spotify’s Web Playback SDK for React.

We pull the access token and song URI from the Redux store

const mapStateToProps = (state) => {
return {
accessToken: state.accessToken,
trackUri: state.selectTrack,
}
}
export default connect(mapStateToProps)(PlayerContainer)

And then send them to the player through props, the react-spotify-web-playback is a handy package that does all the legwork with Spotify. It sends the access token with the song URI code and in return plays the song. nice.

npm i react-spotify-web-playbackimport SpotifyPlayer from 'react-spotify-web-playback'export default function player({accessToken, trackUri}) {
return <div className="fixed-bottom">
<SpotifyPlayer
autoPlay={true}
token={accessToken}
uris={trackUri ? [trackUri] : []}
styles={{
activeColor: '#fff',
bgColor: '#333',
color: '#fff',
loaderColor: '#fff',
sliderColor: '#1cb954',
trackArtistColor: '#ccc',
trackNameColor: '#fff',
}}
/>
</div>;
}

We can now search for, select, and play any song in Spotify’s extensive library.

Next I’m going to cover the guitar and effects interface I created for the play along portion of the Tube Screamer App using the Web Audio API and Tuna.js

Guitar effects pedal board

Web Audio API

Web Audio API provides a powerful and versatile system for controlling audio on the Web, allowing developers to choose audio sources, add effects to audio, create audio visualizations, apply spatial effects (such as panning) and much more.

Using the Web Audio API you can create and process sounds in any web application, right inside the browser.

I only just touch upon the Web Audio API, it is a really interesting tool to play around with and it’s already installed in your browser. Basically it allows a developer to create a source for ‘noise’ whether an oscillator, mic, or in our case a guitar. Create ‘nodes’ that the input or sound source runs through to change the sound and then create a destination like local speakers to play it.

// you begin by creating a audioContext const audioContext = new AudioContext();

Here I brought in Tuna.js to play with

Tuna.js

An audio effects library for the Web Audio API, it has already built effects using the Web Audio API. Inside Tuna is a small library of effects to choose from, these effects are created by connecting together many different nodes to create different guitar effects.

  • Overdrive
  • Filter
  • Cabinet
  • Delay
  • Convolver (Reverb)
  • Compressor
  • Wah Wah
  • Tremolo
  • Phaser
  • Chorus
  • Bitcrusher
  • Moog Filter
  • Ping Pong Delay
  • Panner
  • Gain

Using Tuna.js I create the guitar effects/pedals

const effect = new Tuna(audioContext);createPedals = (effect) => {
const nodeTypes = ['Gain', 'Chorus', 'Compressor', 'Delay',
'Filter', 'Overdrive', 'Panner', 'Phaser', 'Tremolo', 'WahWah'];
return nodeTypes.map( newEffect => {
return new effect[newEffect]({ bypass: true })
})
}

I create my audio input and get the guitar, this requests access to the input.

getGuitar = () => {
return navigator.mediaDevices.getUserMedia({
audio: {
echoCancellation: false,
autoGainControl: false,
noiseSuppression: false,
latency: 0
}
})
}

Then connect all the components together. We await the .getGuitar() before we do anything else, if we didn’t have the ‘input’ the rest of the program would break because getting .getGuitar() is a promise, due to its async nature the rest of the program would continue and break because each component of the pedal board must be connected together with an input/source, node, output/destination to function properly.

Also the audioContext must be .resume(),

A newly-created AudioContext will always begin in the suspended state, and a state change event will be fired whenever the state changes to a different state.

which is a state of ‘running’, to connect it to the input, nodes, output.

setupContext = async (pedals, audioContext) => {
const guitar = await this.getGuitar()
if (audioContext.state === 'suspended') {
await audioContext.resume()
}
const source = audioContext.createMediaStreamSource(guitar) let previousNode = source
pedals.forEach(node => {
previousNode.connect(node)
previousNode = node
})
previousNode.connect(audioContext.destination)
console.log('success')
}

Notice audioContext.createMediaStreamSource(guitar) guitar as a streaming source/input. Connects to the first node/effect, which then connects to the next node and the next. So on until all nodes are connected and finally connects to the output/speakers through audioContext.destination, which connects to the default browser output i.e. speakers.

The pedals are then put into state and passed as props to the Effects component.

<Effects pedals={this.state.pedals}/>

Now we have an input/source that can be played through a few different effects/nodes on through to a destination/output i.e. default speakers/external speaker, etc.

Caveats

There are some conditions to using this application, you would have to have a premium membership to Spotify to stream music and a way to plug guitar into computer. The former being straight forward just go to spotify.com and click on premium in the nav bar to select a plan, although this isn’t a requirement to use the guitar effects on the dashboard. For the latter the easiest way to play guitar through computer is call an audio interface which is a tool that converts an analog signal to a digital signal, I use the m-audio m-track solo. I believe you may also be able to use a jack adapter, though I have not tested it.

The git page for this code is https://github.com/lahb2434/tube-screamer and https://github.com/lahb2434/tube-screamer-authorization-server.

--

--