Screen Recording 2023-10-30 at 12.16.51 PM.mov
Originally my plan for the midterm was to make a drawing machine, but I decided I didn’t have enough time to get the parts to implement it in time, so decided to try something with software instead, and perhaps return to the drawing machine as a final.
I have been thinking about how listening to music changes the way I experience time; I find myself using the music as a marker for passing time. I think about how many songs fit into my commute, or my time between classes. Other people I’ve talked to about this do it, too; my roommate talked about timing her showers to be “about four songs long.”
Inspired by this, I decided to make a timer that works by generating a playlist whose songs add up to a particular length. After doing some searching of different music databases, I decided to use Spotify’s Web API because it has a large database of songs, allows for creating playlists, and seemed to have a good amount of documentation.
To begin with, I needed to set up access to the API. I followed the steps here to make an app through their system and get a client key, then tried a few sample API calls from the command line.
Once I saw those worked, it was time to bring things into the browser. I based my approach on this tutorial article, using a page on my personal website instead of a new application. Since I wasn’t using the same web framework, it took a bit of modification, but I got it to work in the end:
Screen Recording 2023-10-30 at 2.36.51 AM.mov
My first idea was to use a genre as a starting point and then go through search queries results to build a data structure of possible song-length combinations until it found a match. This would use the Search function of the API to find songs (https://developer.spotify.com/documentation/web-api/reference/search). According to the documentation, track information returned from this query should include the duration of each track in ms, which seemed promising.
for(const track of items) {
console.log("new item. list length: ", durationsList.length, "max value", durationsList.length > 0 && durationsList[durationsList.length - 1]);
// if track is longer than our playlistDuration, just skip it
const {duration_ms} = track;
if(duration_ms > maxDuration) {
continue;
}
// if track is in range of our playlistDuration, just return it
if(duration_ms > minDuration ) {
return {
duration: duration_ms,
trackList: [track]
};
}
// otherwise, check it against the other durations we have so far
// get the maximum value that would still be below the playlist duration with this track;
// no sense in searching above that
const maxComponentDuration = maxDuration - duration_ms;
const maxIndex = binarySearchIndex(durationsList, maxComponentDuration);
for(let i = maxIndex; i >= 0; i -= 1) {
if(i >= durationsList.length) continue; // skip for the first
const {duration, trackList} = durationsList[i];
const combinedDuration = duration_ms + duration;
// if the combined duration is in the range, return it
if(combinedDuration > minDuration && combinedDuration < maxDuration) {
return {
duration: combinedDuration,
trackList: [...trackList, track]
}
}
// otherwise, enter the new entry in the list
const newDurationEntry = {
duration: combinedDuration,
trackList: [...trackList, track]
}
durationListInsert(durationsList, newDurationEntry);
}
// add this track on its own
const selfEntry = {
duration: duration_ms,
trackList: [track]
};
durationListInsert(durationsList, selfEntry);
}
// if we've reached here, that means we've exhausted our search without finding a suitable playlist;
// in this case, we just return the best we have.
const closestIndex = binarySearchIndex(durationsList, playlistDuration);
return durationsList[closestIndex];