Electron Crossfading Magic

Remember that sweet, timeless site twoyoutubevideosandamotherfuckingcrossfader.com ?

It was *so* legit

Probably not.

But if you do, it’s probably because you were too lazy, cheap or just not quite awesome enough to use a real DJ setup to solve your abrupt song transition problems.

For my latest Turing School project, my classmate Nick Chambers and I built an Electron app that does just this, but with your local files.

We call it CK5. A homage to the lighting designer of the jam band Phish.

Being music lovers ourselves, we were super excited to use our JavaScript skills to make a desktop app. The best part about electron is that it has access to all the files on a user’s desktop computer, so naturally, we wanted to create a player for our own music collections.

When you use the Electron library, you’re actually free to use most any other JavaScript framework. For this project, we chose React for our view layer, and Redux for our state tree.

Thankfully, as with most things code, someone has already done a lot of heavy lifting for you. So, for our boilerplate setup, we used Chentsulin’s repo https://github.com/chentsulin/electron-react-boilerplate/ . It had plenty of recent commits so we decided it was a safe bet.

Now onto the interesting parts — the code.

The first challenge we faced was to create an algorithm for our crossfader.

After tinkering with a few things — we ultimately decided to go with this implementation.

Where our audio component was the crossfader, like so

render() {
return (
<div className={styles.container}>
<PlayBoxContainer volume={this.state.audio1Volume} audioIndex = {1}/>
<input className={styles.crossfader} id="crossfader" min={-100} max={100} onChange={()=>{this.updateVolumes()}} ref="crossfader" type="range"/>
<PlayBoxContainer volume={this.state.audio2Volume} audioIndex = {2}/>
</div>
);
}

When you adjust the slider,

updateVolumes()

gets called and then goes to work.

updateVolumes() {
if(this.refs.crossfader.value >= 0){
this.setState({
audio1Volume: (1-this.refs.crossfader.value/100),
audio2Volume: 1
})
} else {
this.setState({
audio1Volume: 1 ,
audio2Volume: (1+this.refs.crossfader.value/100)
})
}
}

So, it behaves such that if the slider is moved towards the left of center, audio1 stays at 1 while audio2 steadily decreases, and visa-versa when you go right of center. This algorithm correctly behaves just like a real crossfader. Yeah!

Another interesting part of our project was the asynchronous nature of reading files for their song data.

Thankfully, one of our fellow Turing students, Jeff Duke and Ben Godfrey, created an NPM module to help us along.

We used it in this in our Redux Action function to read our files.

fs.readdir(file[0], (err, files) => {
for (let indiv of files) {
folder.filenames.push(indiv)
}

folder.filepaths = folder.filenames.map((file)=>{
return folder.folderpath +'/'+ file
})
let promiseArray = folder.filepaths.map((file) => createSongObject(file))
Promise.all(promiseArray).then((songData) => {
dispatch(createPlaylistAction(playerIndex, songData));
}).catch(
console.log('Not all files could be read')
);
})

The cool thing about Promise.all is that is implicitly returns an array of all the data you received. Sweet!

Overall, it’s clear that Electron is a super awesome, and useful library.

Like what you read? Give Matt Kaufman a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.