2025-04-23
Developing a Boids program from scratch without restarting it.
Tags: lisp programming artsy
<< previousSome Lisps, like Common Lisp, have a powerful feature that tends to go underappreciated amidst all the talk about macros: the ability to recompile your program while it's running, without restarting it. For the purposes of this post, and because it sounds cool, let's call this ability livecoding1.
Entering this strange land where the programs never stop, we'll first take a brief tour of Common Lisp and one of its graphics frameworks, Sketch, before walking through a livecoded implementation of the Boids algorithm.
"Wait, what exactly is this livecoding thing?"
Consider the typical workflow needed to modify a running application, like a videogame.
- Stop the application.
- Change the code.
- (If a compiled language) Wait N time units for a full recompilation.
- Start the application again.
- Fiddle with the application to get it back to its previous state.
- Carry on.
In a livecoding environment, the application is never stopped, which eliminates steps 1, 4 and 5. Instead, small code changes (which can be as granular as recompiling a single function) are immediately reflected in the running program. Step 3 is often instantaneous because only the changed parts of the program must be recompiled. In theory, then, you can develop an entire application while it continuously runs in the background, without ever waiting for code to recompile. This makes the development process more fluid and interactive, with minimal downtime.
In Common Lisp, the workflow might look something like this:
- Make a small change to a single function.
- Recompile the function (instantaneous).
- Carry on.
For an example of this workflow in action, check out Common Lisp and Emacs being used as an environment for live musical performance. You can hear about a Lisp program being debugged remotely while running in deep space. Livecoding (or hot reloading, or whatever you like to call it) is also available in other languages, like Smalltalk and Erlang.
A rough sketch of Sketch
Before jumping into Boids, let's take a brief look at Sketch, our Common Lisp graphics framework of choice. We'll be more concerned with big ideas than with code minutiae, but if you're unfamiliar with Common Lisp and want to understand the code samples, then please take a detour through Learn Common Lisp in Y Minutes.
So, Sketch. The Sketch API is heavily based on that of Processing. Its primary entry point is the defsketch macro. The code below defines a "sketch" called my-sketch.
After the name of the sketch comes a list of bindings that define its state and configuration. Here, the window properties width and height are set to 200, while n is an attribute we've added for our own use.
Then comes the drawing code. This gets run in a loop while the sketch is running, once per frame. The following snippet draws 5 red circles on a black background, each of radius 10 and in random positions.
After painting the background black, the all-powerful loop macro is used to draw n circles. The with-pen macro (defined by Sketch) configures drawing properties like fill colour, stroke width and stroke colour. It takes a "pen" object as an argument.
Here's all the code together:
Finally, to run the sketch, we compile our code and execute (run-sketch 'my-sketch) from the REPL, resulting in...

That's all we need to know about Sketch for now!
Livecoding Boids
Boids is an algorithm from 1986 for simulating flocks of birds. In its essence, it consists of applying 3 forces to the simulated birds. Quoting Wikipedia2, these forces are:
- separation: steer to avoid crowding local flockmates
- cohesion: steer to move towards the average position (center of mass) of local flockmates
- alignment: steer towards the average heading of local flockmates
How can we implement this ourselves? First, we need a canvas to draw on!
The only mysterious thing in this code is the restart-on-change parameter, which is available in my fork of Sketch. When its value is nil (false), the sketch's state - like the boid positions - won't be reset when we recompile our code.
Compiling the defsketch form in Emacs (with the Ctrl-C Ctrl-C shortcut) and executing (run-sketch 'boids) at the REPL gives us... 🥁... a gray background. Wonderful.

(Note: all going well, this modest window will run continuously throughout the entire development lifecycle).
Now let's create some boids to populate our world. We add a boid class to store their position and velocity, as well as a convenience function make-boid to create a boid from x & y co-ordinates. These rely on a hopefully self-explanatory implementation of 2d vectors, which are created using the vec2 function.
To the sketch itself, we add 20 boids in random positions, and pass them to the draw-boids function in the drawing loop.
If we then recompile defsketch (with Ctrl-C Ctrl-C)...
...we get an error! Woops.
But of course! We forgot to define draw-boids. The program doesn't crash, however, and we'll soon be able to recover from this setback.
Here's an implementation of draw-boids. We don't need to get into the weeds of how it works. For each boid, it does some unwieldy vector math to figure out which direction the boid is facing and draws a triangle pointing in that direction.
As soon as we compile draw-boids, the error screen disappears and our lovely boids are drawn into place. And we didn't have to restart the program to fix it!
There are two Common Lisp features that enable us to fix errors on-the-fly like we've done here:
- Newly compiled code, and recompiled code, is immediately loaded (sometimes called "hot reloading") into the running program. This opens up possibilities such as optimising a program as it runs, tweaking parameters like gravitational force and background colour, and iteratively developing a GUI.
- The condition system! This is somewhat like exception handling in other languages, but more powerful. Not only can we signal exceptional situations ("conditions"), but we can also define "restarts" for recovering from those situations. When a running Common Lisp program encounters an unhandled condition, control passes to the debugger, and the user is presented with a selection of restarts. Perhaps they want to recompile the offending function and continue execution from the previous stack frame. Or perhaps the error was a division by zero, and the offending function provides a restart that swaps in a value of 1 for the divisor. Suddenly, there are a lot more possibilities than just crashing the program.
Anyway, a worthy discussion of the condition system would take up a full blog post of its own. Back to Boids!
Now that our boids are drawn correctly, we want them to move around and do boid things. First, we implement an update-positions function, which basically adds the velocity of each boid to its position (so that the boid moves), and applies the 3 Boidian forces to update the boid's velocity. For now, the functions implementing these forces are stubbed out.
We then have to modify the drawing loop to call update-positions.
So far, these changes haven't affected the boid behaviour, so let's circle back and implement rule-1, which can be summarised as "stay away from other boids". When a boid is less than 10 pixels from another boid, we push them away from each other to avoid crowding.
(Note: the vector functions ending in !, like v+!, follow the convention of storing the result in the vector passed as the first argument).
When we recompile this function...
...a pair of boids that happen to be too close to each other are sent flying off into the void. There's no counterforce to bring them back, just yet.
Next, we implement rule-2: boids should fly towards the average position of other boids. Our implementation could be more efficient by summing the boid positions just once, rather than doing it for every single boid, but I can't be bothered.
Recompiling rule-2, we get...
Yes! This is starting to look vaguely like Boids. Let's add the final rule, rule-3: boids should match their velocity to all the other boids. Implementation note: we probably shouldn't update the velocities until all the new velocities have been calculated, but this doesn't seem to matter too much.
Recompiling, we see the Boids calm down a little bit.
Since it's not very bird-like to fly around in a vortex of death, we could also give the boids a purpose by making them follow the mouse position. The result of these changes can be seen at the top of the post.
And with that, we have a complete implementation of Boids! At the risk of beating a dead horse, I'll re-emphasise that we did the whole thing without once restarting our program or waiting a perceivable amount of time for code to compile.
Closing thoughts
I hope, in this brief demonstration of livecoding, I've given you a taste of how useful and fun this feature can be, whether you're developing a graphics application or mundane accounting software. Like I've said, it's not unique to Common Lisp, as at least Smalltalk and Erlang have similar capabilities. It's also possible to bridge the gap in less interactive languages by making applications automatically restart themselves when a code change is detected, or by bolting on a scripting language. Just do me a favour and ask yourself, the next time you're waiting the requisite time units for your code to recompile: How can I make this workflow more interactive? How can I make it more... like Common Lisp?
<< previous
I'd be happy to hear from you at [email protected].