I recently wrote a post about why reflecting on growth is important. So that’s what this post is about.

Back in roughly 2013, I had recorded a bunch of yoga pose audio tracks and thought it would be cool to automate playing them with a playlist app. But instead of it being a regular playlist, I wanted to programmatically set the amount of time that a person would hold each pose. For example, 10 seconds, then do the next pose. Since I had no programming skills at the time, this idea manifested as loooong mp3 files that I made with Garageband. I mean, as tedious as it was to manually move 60 individual audio snippets along a time line in Garageband – for three completely different Garageband files (10 sec, 20 sec, and 30 sec), a girl’s gonna get it done, right? And I did. I’ve been using those audio track compilations for the past few years.

Fast forward to today… and now that I have programming skills… guess what!

I built that app. It occurred to me last weekend that, oh, I could build that now. So I did. I have an app with a playlist full of poses where I can set the hold time. In other words, I just built the thing I wished I could have built back in 2013 and I’m appreciating the learning and the work I’ve done to get here.

So hey, good job, me! And this is what I ended up building:

This app has a simple, single purpose: to play a list of individual yoga audio tracks in evenly set intervals. For example:

play track for pose 1
hold for 20 seconds
play track for pose 2
hold for 20 seconds
play track for pose 3
hold for 20 seconds
play track for end chime

As this is an app I built simply as a utility for myself, I focused on function over fancy. This is what a playlist looks like on mobile.

playlist show page

A playlist has a name, a hold time, and has_many :playlist_poses, which is the join table for a many-to-many relationship. A pose can be on many playlists and a playlist has many poses.

A pose has a name, image, and audio file. Here is what the poses index page looks like:

poses index page

I love the flexibility I have in this app. First, there is flexibility in creating a playlist without needing to re-record audio tracks. I can simply reuse them at any time in a playlist. For example, if I really wanted to go back and forth between warrior 1 and warrior 2 five times in a row, I can do that.

playlist with repeated poses

Second, there is flexibility in the amount of time I want to spend any given day doing a yoga playlist. Some days, I may have a full 30 minutes, so I’ll use a hold time of 30 seconds on my “flexibility playlist”. Other days (much like this past week when I had a mean cold), I may choose to do an 8 second hold. I could still get through all of the poses and I still did something good for my body that day – whereas if I had been locked in to a 30-second hold, that would not have happened. I added a duration calculation, to give me an idea of how long it will take to get through the playlist with a given per-pose hold_time.

Show me the Magic

Is the magic of the hold time in a Ruby sleep(30)? Lol. My backend-oriented heart wishes. No, it is in a javascript forEach loop with a setTimeout. And it looks like this:

console.log('Begin!');
audioTracks.forEach(function(item, index, array) {
  setTimeout( (function( index ) {
    return function() {
      console.log(`playing: ${item}`);
      const track = new Audio(`/audio_files/${item}`);
      track.play().catch(e => { console.log(e); });
    };
  }(index)), (holdTime * index) );
});

see the code

While building this loop, I chose to output all of the tracks to the console as a sanity check. Then I decided to leave them in place in production… as a sanity check ;) Also shout out to @tomrich82 for being a voice of reason as I struggled in this async world.

playlist running with console output

That’s pretty much all there is to it! I ran into some i-have-no-idea-what-i’m-doing territory with the changes to the Rails 6 asset pipeline. With Rails 6, one does not simply plop javascript into a .js file and call it a day. Webpacker is involved. For this project, I circumvented my Webpacker woes and was able to move forward with launching a beta version for myself by putting the javascript directly in the HTML pages where it was needed. My next step will be learning how to do this TheRightWayTM, using Webpacker.