Introducing Tuplet -- A Language for Breakcore Splicing

Aiden Sato • May 16, 2025

In which I write about my slightly silly drum sequencer (and Racket DSL).


Over my last semester at Northeastern, I took Hack Your Own Language, an elective focused on building domain-specific languages (or DSLs) in Racket. By the end of the class, we each had our own DSL, conceived and developed ourselves. I’m really proud of what I built, so I’d like to (briefly) show it off a little.

There were a couple of goals I had for my DSL right off the bat:

  • Do something in a creative field
  • Make something I haven’t already seen before
  • Actually make something using it

The third goal was especially important—not only would it mean that I could demonstrate it in a practical way (or that it was usable at all), it limited me to projects in domains I was already familiar with.

Coming up with the concept itself was easier than I expected. There wasn’t some exciting process of discovery with this project—I literally came up with it in the shower. “Something to do with breakcore” was in my mind for a while, but I had no clue what that would look like until the pattering of hot water clicked it into place: what if all rhythm was conceptualized as arbitrarily-nested tuplets? (I’ll let Adam Neely explain what this means, musically—he’ll do a better job than I ever could.)

I wanted to make it easy to make changes to music written in Tuplet. By using a simple textual syntax, operations such as moving and copying notes and phrases should be trivial (there’s a reason why I write my resume in HTML/CSS lol). And by representing rhythm as tuplets, doing wacky and wild stuff with the music—and making changes to it—should be made easy. The results may not sound good, but it would be easy!

Of note, I wanted Tuplet to focus on the manual creation of music. Breakcore is characterized by the meticulous splicing of drum loops, piece by piece. I wanted to preserve that ethos in my project, leaving every creative micro-decision to the user. If you use Tuplet to program a beat, you wrote that beat. Not me. Not the machine. And certainly not some stupid genAI algorithm.

Creative field? Check. Haven’t seen it before? Check (sorta, Tidal Cycles allows for similar functionalty—but I didn’t see Tidal Cycles until I came up with the idea, so it still counts). I just had to make it, and make something with it.

A couple things surprised me while implementing Tuplet. The first is that I was completely wrong about the difficulity of each task. The second is that Racket kinda freaking slaps. It’s so good.

Here’s a rough outline of each task, in order of implementation, and their expected and actual difficulties:

taskexpected difficultyactual difficulty
data definitionsmediumeasy
runtime functions for loading and exporting oneshotseasyeasy
runtime functions for “squeezing” and assembling tupletshardeasy
macros for tuplet and track definitionsmediumeasy
macros for pattern bindinghardmedium
runtime functions for manipulating audiovery easyvery hard

Evidently, my estimations were awful. Some tasks, such as “squeezing” tuplets, ended up being really easy—turns out fitting x notes into y beats is very simple mathematically. And pattern binding was able to utilize literally the same code. Others, like audio manipulation, ended up requiring me to literally call ffmpeg as a subprocess if I wanted to finish the project in a reasonable amount of time whatsoever. (Don’t look at the code, please.)

Because I was making a language with new syntactic forms, I needed to use Racket’s macro system. And it rules.

[Cueball is sitting at a computer, and Megan is standing behind the desk.]
Cueball: Lisp is over half a century old and it still has this perfect, timeless air about it.
Cueball: I wonder if the cycles will continue forever. A few coders from each new generation rediscovering the Lisp arts.
[Man in Jedi robes carrying a towering stack of parentheses in his arms, speaking to Hairy.]
Jedi: These are your father's parentheses. Elegant weapons. For a more... civilized age.
(credit: explainxkcd)
Oh no. I'm becoming a Lisp developer. (credit: randall munroe)

Some of its advantages are simply due to the nature of s-expressions. Racket’s syntax is inherently unambiguous, which means order of operations will never be a problem. Most, though, are due to clever engineering from Racket’s developers. Racket’s syntax parsing features include a ton of features, such as custom syntax class attributes, that I used for Tuplet that I don’t believe exist as fully in other languages. (Rest assured, my opinion is very biased. Matthias Felleisen was my Software Development professor.)

Most importantly, however, was I able to make something with it? Yes, sorta:

A few measures of a breakcore beat I produced using Tuplet.

Why only sorta? I initially wanted to make a full song, but I didn’t. Because Tuplet is slow. Almost prohibitively slow. And this is a bit disappointing to me. I may fix it someday, or I may just finish a track with the current version anyway, who knows? Despite that, I still made a pretty cool beat exclusively using Tuplet. I think that last bullet can safely be checked off, too.

Like I said previously, I’m really proud of Tuplet as a piece of software I created, and I truly enjoyed the process of creating it. I hope you find it as cool as I do.

If you want to learn more about Tuplet and how to use it, check out the readme and tutorial.

And if you want to know more about the implementation, feel free to look at the code, or the developer notes.