obsolete.computer/geekery/

Digging into Vermiculate

A typical Vermiculate visual

For a long time, I've wanted to highlight a program that quickly became the screensaver I would always set when building a new Linux PC: Vermiculate, part of the XScreenSaver package. It seems to create only a handful of different patterns, all based on "worm-like motion" (hence the name), but they're so visually interesting that it's been my go-to screensaver for probably over a decade now. So I thought, instead of just doing a basic overview of it, why not get my hands dirty (see what I did there) and figure out what makes it tick? It's open-source, after all.

So I downloaded and extracted the source for XScreenSaver, and found vermiculate.c. The first interesting feature I noticed in the cource code was an array of structs defined near the beginning, called sampleStrings[]:

static const struct stringAndSpeed
{
  const char * const str;
  int speed;
}
sampleStrings[] =
{
/*  { "]]]]]]]]7ces123400-9-8#c123456#s9998880004#ma3#car9ma6#c-#r1", 600} , 
    { "bmarrrr#c1234#lc5678#lyet]", 600} , */
  { "AEBMN222222223#CAR9CAD4CAOV", 150} ,
  { "mn333#c23#f1#]]]]]]]]]]]3bc9#r9#c78#f9#ma4#", 600} ,
  { "AEBMN22222#CAD4CAORc1#f2#c1#r6", 100} , 
/*  { "mn6666666#c1i#f1#y2#sy2#vyety1#ry13i#l", 40} , */
  { "aebmnrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr#", 500} , 
/*  { "bg+++++++++++++++++++++++#mnrrrrrrrrrrrrrrrrrrrrrrr#y1#k", 500},  */
/*  { "BMN22222223#CAD4CAOVYAS", 150}, */
/*  { "aebmnrrrrrrrrrrrrrrrr#yaryakg--#", 100} , */
  { "mn6rrrrrrrrrrrrrrr#by1i#lcalc1#fnyav" , 200 } ,
  { "mn1rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr#by1i#lcalc1#fn", 2000 },
  { "baeMn333333333333333333333333#CerrYerCal", 800 },
  { "baeMn1111111111111111111111111111111111111111111111111111111111#Cer9YesYevYerCal", 1200 },
  { "baMn111111222222333333444444555555#Ct1#lCt2#lCt3#lCt4#lCt5#lCerrYerYet", 1400 },
  { "baMn111111222222333333444444555555#Ct1#lCt2#lCt3#lCt4#lCt5#lCerrYerYetYt1i#lYt1i#sYt1#v", 1400 }
};

What could these be? Some sort of pattern definition? As I dug further in, this hunch was confirmed: a function called consume_instring() is basically a giant switch statement which determines the motion of the worms, and is fed the contents of a randomly-chosen string each time the program is initialized. So the "sample strings" are basically a language that establishes the worm definitions. What's interesting is that there are a handful which are commented out, like the author was toying with them but didn't like them enough to leave them turned on.

But then I noticed something else, near the bottom of the source there is a section which appears to define extra command line options beyond the standard ones used by XScreenSaver. One is -instring and one is -speed. Could I perhaps use these to see what type of displays the commented-out strings would produce? Turns out, you can do exactly that.

/usr/lib/xscreensaver/vermiculate --instring "aebmnrrrrrrrrrrrrrrrr#yaryakg--#" --speed 100

If you have XScreenSaver installed, typing the above spawns a Vermiculate instance in a window... and if you try it you'll see that it presents a pattern here which doesn't normally appear by default. It appears to create a randomly-generated maze, and the worms bounce around trying to escape, destroying walls in the process.

Deciphering the Worm Language

A scan of the giant switch statement reveals that the 'G' character is the one responsible for creating the maze grid. I confirmed this by typing:

/usr/lib/xscreensaver/vermiculate --instring "g" --speed 50

...and I saw that a simple maze grid was created. What's curious is that even with a string so short, there were still worms bouncing around in it. There must be a default number of worm threads that get created, even when none are explicitly defined in the string. (A glance at the maininit function reveals the default to be 4.)

Digging in the Dirt

As I examined the source code, it became apparent that this program was originally meant to be run interactively. The "instrings" are really just recorded keypresses that the user would have entered while it was running... adding and removing worms on the fly. Well... that led me down a bit of a ~~rabbit~~ worm hole. After a bit of searching, I found there was an older version of the source file still floating out there with the interactive-mode code intact. I combined it with a boilerplate Makefile and a couple of the needed source files to make it work, played around with the GCC flags, and viola... it worked! And it's pretty fun to play with. So that it doesn't get lost to time, I'll host it here on this site (it's licensed very permssively) for anyone to play with. Tyler, if you're out there, thanks for this neat piece of code.

Meet the Family

Here are images of all the worm types, 1-7: (8 and 9 work but aren't really defined, so they just go in a straignt line)

Worm Type - 1 of 7 Worm Type - 2 of 7 Worm Type - 3 of 7 Worm Type - 4 of 7 Worm Type - 5 of 7 Worm Type - 6 of 7 Worm Type - 7 of 7

The Vermiculate Dictionary

Being able to use the program interactively made it much easier to document the commands and modes. So I started a README file which documents (most of) the keyboard interface. Check it out on the code page here:


Modified Friday, November 18, 2022