The Web’s Most Unusual Sampler
Gabbie Piraino and I built a strange loop machine using React and Tone.js
Mixlr is live at this link (but the backend is hosted on a free Heroku dyno, so click it now to spin it up and then look at it again in a minute or two.) You can also take a look at the front and the backend on github.
A complex project like this has to start with a component hierarchy. Building out something like this in React without that would be pretty catastrophic. Our interface was divided into three main areas: the PlayerForm—the main area where users would tweak the samples and make music, the FileLibrary—from which the samples could be added to the PlayerForm, and the SavedSongs component—where users could save their songs or load their existing songs into the player for another listen or another remix.
Ultimately we wanted to fit a lot of details and parameters into the individual tracks on the PlayerForm. We fit in a title, along with controls for play-rate, pitch, volume, and where the sample starts and ends.
An empty track is gray and obviously deactivated, and an active track is brighter and draws the eye. The controls are mostly the default linear value selector for HTML—but for in and out, we wanted the draggable points to sit on the same timeline. Here, there are two sliders on top of each other, with transparent backgrounds. The “In” slider is green, and the “Out” slider is red. I think with another revision they could become arrows. As is, the indicator on the right gives the time (in seconds), and if the red bar is to the left of the green, the clip is playing in reverse.
Mixlr does not resemble a professional sample-slicer: it is more of a toy than a tool. One consequence of this is that it is hard to describe what everything does and how everything works. We chose more explicit labels for buttons to try to address this.
“Send to Player”
These are pretty long for button titles, but definitely beat a symbol, which in this context is really unlikely to be scanned and understood by a typical user.
ToneJS is an extremely useful library: it’s one of the best ways to allow a visitor to a webpage to control the audio, and it wraps the Web Audio API conveniently. Unfortunately, web audio is hard to bend into your desired shape. It’s not what the platform is designed for. But we can get something respectable by keeping in mind how our moving parts fit together. At first, we had trouble with ReactJS re-rendering components. With each re-render, the old ToneJS elements would continue to make sound and new ToneJS elements would be created, also making sound. ToneJS elements should be kept out of React components. ToneJS elements also don’t like having their state updated too frequently—some kind of debounce or limiting factor is necessary, so that an update to a parameter does not hit the element before it has updated that parameter according to a previous command.
We also ran into some difficulty on our backend. We started the project using PostgreSQL, but eventually realized it’s not the best choice for our purposes. Whenever a song is saved, we create a new saved song, and attach four track parameter objects to it (containing all of the parameters for all of the tracks for this song), each of which is also linked to a file (so that it can be recalled from our database when necessary). This means that clicking “Save Song” can make up to nine POST requests to our back end.
If I were to approach this again, I would use a NoSQL database, and each saved song would be one new nested object, with all necessary information stored on it. It’s less efficient in terms of space usage, but much more efficient in terms of recall and request usage.
We came away with a fun toy that makes weird music, or at the very least allows users to play with weird noises in a new way. In building it, I learned a ton about web audio, timing, and ToneJS, which I use in a lot of my projects today. In revisiting it with a little more knowledge, I learned a lot about how NoSQL databases work, and how to make efficient requests of a database.