I studied computer science at Brown University alongside a concentration in philosophy. After that, I worked at the CUNY Research Foundation for three years, at first as a front-end developer and later as a UX engineer. After CUNY, I’ve focused on learning more about web development from the ground up. Lately, I’ve been putting those efforts into Playtree, a web app which lets a user make Spotify song collections where playback can loop and branch.
All the while, I’ve also worked on personal game projects like Entale, an open-world RPG with a procedural language system. I also attended the Recurse Center, a programming retreat, where I experimented with computer graphics, React development, and logic programming.
Playtree is an application for making and listening to playtrees, which are like playlists, except that playback can branch, loop, and start at multiple entry points. You can try out playtree here. In this post, I’ll provide a broad overview of the Playtree application and its development. The source code is available on GitHub.
Check out the playtree explainers for a more in-depth explanation of playtrees.

Playtree is a web application for creating custom playtrees, which are directed graphs. Playnodes contain songs to be played, and playedges define a path playback can follow.
In the course of making Playtree, I came across some esoteric terminal commands for MacOS. To start with the least useful:
afplay <audio_file>
will play a specified audio file from the command line. I used this in an early CLI prototype for Playtree, but its utility is pretty limited. There’s no way to control audio once playback starts. You can stop playback by sending an interrupt signal with CTRL-C, and you can “mute” audio by suspending the process with CTRL-Z. This doesn’t pause playback, though. It’s still playing through silently in the background.
Playtree uses the Spotify API to search for tracks on Spotify, and to play music within Playtree. It also makes use of the Spotify Web Playback SDK, which allows for Spotify playback to occur within the app itself, and for certain events pertaining to Spotify playback to be reported real-time to the application.
Spotify API calls must be authenticated, and so I use the Authorization Code flow to obtain an access token from the server side. I need to use the access token on both the client and the server. Web Playback SDK can only be used client-side, and my server uses Spotify tokens as a proxy for the user’s identity within the site. Access tokens and refresh tokens are stored as cookies.
The rules that govern playback on a playtree are rather complicated. The user can play and pause audio, skip forward, go back to previously played songs, which must be memoized when playback randomly branches. Additionally, a user can switch between playheads, which loads/reloads a full playback context.
I use a reducer to handle playback logic. A reducer is a pure function which takes a state and an action and returns a state. Actions are dispatched as a response to UI events and external state changes, prompting an internal state update according to the reducer function. This provides a clean way to manage the logic of complex state updates.
I wanted to create an intuitive, fun UI for making and visualizing complex playtrees. Playtrees are graph structures, so I used React Flow, a library for rendering and interacting with graphs. The editor displays highly customized nodes and edges using React Flow. I’ll call these nodes and edges that appear in the editor’s UI “flow nodes” and “flow edges.” These flow nodes and flow edges are distinct from playnodes and playedges, which are represented in data within a playtree data structure, which exists as a JSON file when stored on the server, and a Typescript object when used in the editor or player component on the Playtree client. Even so, the custom flow nodes and flow edges used in the editor UI do depend on the data with a playnode and playedge.