Musings from my desk

Loop Supreme, part 2: Adding a Metronome

2022-11-04 12:32:38 +0000 UTC

This is part 2 in a series about building a browser-based live looper

Goal

I need to add a metronome!

In my ideal looper app, the metronome is the heart of the entire application. By default, I want the metronome to determine the length of the loop (number of measures 𝗑 time signature 𝗑 BPM), as well as synchronizing the start and end of recording for each loop. The user should be able to arm a track for recording, but recording should actually begin when the metronome starts the beginning of the loop.

Implementation

Since the metronome is going to have properties that are shared among many components, I have 2 “obvious” options that I’m aware of in the React ecosystem:

  1. create a Context that will store some “global” state about the current tick, BPM, etc
  2. create an instance of a “Metronome class” and pass it around everywhere that it is needed

I’m going to try using a Context because it feels a little more “React-y” and I think it might by easier than passing a Metronome down through oodles of different components.

Learnings

I’ve never seen or used such a complex Context before, and tbh I’m not sure if it’s idiomatic for React Contexts. It does feel a little strange to be managing an interval inside the component, but in simple tests it seems to work OK. I’m also not at all convinced that it is good practice to pass state updater functions to the context provider. Again, seems to work in a simple test, but we’ll see when I get the Scene and Track elements going if this actually works as expected.

I also learned that for Tailwind to work properly, you need to point it at all the relevant source files. This is probably obvious to anybody who has used Tailwind before but I couldn’t figure out why my styles for my header weren’t working. I moved my header into the static HTML file (eventually I’ll optimize this file a bit more for SEO) and wanted to use Tailwind styles for it. I had to add my public/index.html file to my Tailwind config in order to make this work. After that, both npm run css and npm start compile the CSS properly and apply the styles as expected. Magic! ✨

State of the app

  1. Merged PR: https://github.com/ericyd/loop-supreme/pull/3
  2. The app runs! npm start
  3. MetronomeContext counts the beats. It works for various time signatures, BPMs, a measure counts
  4. Metronome component displays current metronome settings and updates them
  5. There is a bug where the MetronomeContext is creating two intervals, as demonstrated by a simple console log. This is probably due to double-mounting done by React in development mode. Need to test in production mode to see if this will be an issue long term.

loop-supreme-1-metronome.png

Time logging