Screen Recording 2023-10-30 at 12.16.51 PM.mov

b.mov

Conception

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.

API Authentification

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

Making a Playlist

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.

Screen Shot 2023-10-30 at 2.47.02 AM.png

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];