Advent of Code (feat. Scheme for Max)
Introduction
It’s December, which means it is time for Advent of Code: a series of daily programming puzzles loosely tied together by a Christmas theme involving Santa’s elves. I’ve used a few different programming languages over the years, never quite finishing all of the puzzles in any given year, and it’s always fun. This time, however, it snuck up on me just as I was also discovering Live Coding, especially the work of Sam Aaron and Meta-eX. It instantly piqued my interest and at first I tried getting started with the same tools he and Meta-eX used, (namely Clojure and the Overtone bindings to the SuperCollider synthesis engine). I quickly stumbled across a reddit comment saying that at this point, in 2025, Overtone is pretty much dead and people are better off looking for more vibrant projects. So I discovered Iain Duncan and his project Scheme for Max, which provides a runtime for a dialect of Scheme Lisp inside the Max “patching environment”. I immediately had the idea, in order to get up to speed both with Scheme and with Max, to create my Advent of Code solutions within the Max environment using Scheme for Max.
Overview of Scheme For Max
To set the stage a bit, I’ll introduce the different technologies I’ll be working with. Max, according to its website, is “an interactive, visual patching environment for musicians, artists, and anyone who loves creating, inventing, or prototyping their own unique software”. Essentially, it gives you a blank canvas and a library of components called “objects” to work with and you connect the objects you lay out with virtual patch cables to generate some sort of output (audio, visual or other). It doesn’t provide the cohesive workflow a DAW would give you out of the box, but you could sort of build your own DAW.
Scheme is of course a (dialect of?) Lisp. I am just learning it this week, but at this point I’ve picked up enough from dabbling with Haskell and even C# and JavaScript for it to come pretty naturally so far.
Scheme For Max is an extension for Max which adds an object to the library which embeds a Scheme interpreter and allows you to send Scheme code into Max and interact with any other objects you have in your workspace/patcher. I work in my usual text editor, but I can pipe Scheme expressions to Max via UDP and they are executed there.
Solving Advent of Code Puzzles
My goal for Advent of Code was to execute my solutions in Max, as a way to get comfortable with coding
in that environment. The first hurdle I encountered was getting the input data for the first puzzle into
my program. What I ended up doing was using a text object in Max, which reads an arbitrary text file
from the filesystem but treats it as a list of commands so when I received the data from the object each
line was prepended with set, which I had to strip back out in my Scheme code.
Once I was able to read the data from my code, things were pretty straightforward. I solved the Advent of Code puzzle with regular (although far from idiomatic I’m sure) Scheme code. Then I just used S4M’s utility for writing to the Max console to output my answer. That already felt pretty cool.
But then my wife pointed out it was kind of lame to have no musical aspect to this, if I’m doing all of this in a programming environment embedded in a music application. And fortunately, there was a pretty obvious tie-in between the day 1 puzzle and music. The puzzle involves rotating the dial on a combination lock in order to figure out a password to get into the north pole, which of course calls to mind manipulating dials on a synthesizer. So I quickly figured out how to add a sawtooth oscillator and a filter into my Max project.
Here is the setup in action:
There is a text object in the top right of the patcher into which I’ve loaded the text file containing
the puzzle input. I created a clocker object at the top which generates a message every 50 ms and that
message gets sent to the s4m object at input 3. My code has registered a callback with the s4m object
which will send a line message to the text object for each event, requesting the next line of input
data. That data also gets sent to the s4m object, at input 2, and is processed by a callback in my code
which parses the data and updates its state accordingly. My code also uses the current input to calculate
a frequency (a numeric value representing hz) and outputs that on output 2 of the s4m object. At the
bottom of the patcher I have set up a simple audio signal chain consisting of a sawtooth wave oscillator,
a lowpass filter, and an output. The frequency value I output from the s4m object sets the cutoff
frequency of the lowpass filter. I also added a 20% chance of setting the oscillator frequency an octave
higher or lower each time I process a line of input, just to spice things up a bit. So that is how I use the puzzle input to drive changes in the audio signal that is being generated.
My code and the Max patcher file are on Github.
Next Steps
I am pretty happy with how much I learned about Scheme and Max just from deciding to solve the puzzle in this environment. There is obviously plenty of potential for more complex and nuanced manipulation of Max objects using Scheme and I’m excited to keep exploring this. I need to learn more about the different forms of synthesis available in Max, how to setup sequencers and mixers to produce actual musical pieces, and the best ways to use Scheme to control everything and allow for interesting manipulations and live coding. Hopefully I’ll have more to document about these topics soon!