It looks like you're new here. If you want to get involved, click one of these buttons!
In Rivulet, code is inscribed in strands, box-drawing characters that combine into continuous lines. Strands are organized into glyphs, units of code that resemble paragraphs more than blocks of code. They are usually tightly packed; the aesthetic of Rivulet is inspired by the satisfying compactness of mazes, Anni Albers's Meanders series, and space-filling algorithms. Semantically, a glyph is a set of strands that execute together. But they are also syntactic units, in that not all strands can appear together in the same glyph because they might block each other in how they flow through the glyph's space.
Rivulet is a language inspired by natural language; it has an internal logic, a coherence, but with no rational explanation. Rivulet relates to the Week 3 discussions, as it's part of the Source Code Exhbition, but it's also in Forty-Four Esolangs, discussed in Week 1.
It takes a while to understand the full syntax of Rivulet, but I want to focus here on only two aspects: first, the way that line numbers work in the language, and second in how "zero strands" are written.
Here is a single Rivulet glyph:
1 ╵──╮───╮╶╮
2 ╰─╯╰──╯ │
3 ──╮ │
5 ╰──╯ ╷
And here is pseudo-code for the glyph, which can be generated by running the interpreter with the -v for verbose option:
list2[0] += 0
list2[1] += 1
list1[0] += -16
The glyph begins and ends with glyph markers: ╵ and ╷. Each strand begins with a hook pointing up or left, which marks its beginning (strands without hooks or with hooks pointing in other directions are used for other types of strands not considered here). Each unit it moves to the right adds the value of that line number, and each movement to the left subtracts it. The line number it appears on marks a list element it is writing to; by default, it is an add-assignment (+=). First from the left is the strand beginning on line 2, which gives us list2[0] += 0. This is called a zero strand, and it has no effect on that list element, but is there so that the second strand beginning on line 2 can write to list element 1, for list2[1] += 1. Finally, it reads the third strand (reading from left to right), which assigns to list1. It moves to the left twice on line 5, subtracting 10, and then twice on line 3, giving us -16.
You may notice there is no line 4. In Rivulet, line numbers are successive primes. Line numbers don't need to appear in the code, but can be included as reference, as the Rivulet interpreter ignores all non-box-drawing characters. They also reset with each glyph. Why prime numbers? The compactness of the glyphs need numbers that grow faster than a linear progression, and less than exponential growth, so that one can represent large numbers without making the glyphs very large. Prime numbers hint at a deeper meaning, but this is primarily an aesthetic decision. I imagined what a programming language might look like had it developed like a natural language, with conventions that have an internal consistency, but no rational explanation.
The zero strand is needed often, to allow writing to list elements beyond zero. There are many ways to write them. Here is a set of zero cells writing to list1. Each of these is equivalent:
1 ╵╰──╮╰───╮╶╮
2 ─╯ │ │
3 ─╯ ╰──╮
5 ╭────╯
7 ╰── ╷
Running -v gives us this pseudo-code:
list1[0] += 0
list1[1] += 0
list1[2] += 0
But more specifically, the flow is:
list1[0] += (2*1) + (-1*2)
list1[1] += (3*1) + (-1*3)
list1[2] += (2*3) + (-5*5) + (-3*7)
Writing to list2, these are all zero strands:
1 ╵╭─╮ ──╮
2 │╶╯╶╮╰────╮╰─╯╶╮
3 ╰──╮╰─╮ ╭─╯ ╭──╯
5 ─╯ │─╯ ╰────╮
7 ╭───╯ ──╯
11 ╰────╮
13 ──╯ ╷
list2[0] += (-1*1) + (2*3) + (-1*5)
list2[1] += (1*3) + (-3*7) + (4*11) + (-2*13)
list2[2] += (4*2) + (-1*3) + (-1*5)
list2[3] += (1*2) + (-2*1)
list2[4] += (-2*3) + (4*5) + (-2*7)
Any one of these can be used as a zero strand for list2. We would draw a strand -- one of these or another -- which gets out of the way of other strands we want to appear in the same glyph. Writing Rivulet is often a re-working of existing strands in order to add others to the glyph.
The Rivulet interpreter also includes an svg generator, which draws the strands as complete lines and colors them so that related strands are the same color (we have no strands that refer to each other here, since we are only dealing with the simplest kind of strand, that used for add-assignment):

And here is a complete Fibonacci program, using the same algorithm, the same "code," represented with strands that move differently through the space (and generated with different color themes):

More on Rivulet
Comments
Thanks for this detailed walkthrough, Daniel. Rivulet sits within the exhibition's "Source Code as Cultural Artifact" section, alongside pieces like the Wenyan language and the obfuscated C. But where Wenyan challenges the linguistic hegemony of English and the obfuscated C plays with the tension between human and machine readability, Rivulet does something different: it questions why programming became textual at all. The dominance of text wasn't purely logical — it emerged from material constraints like Teletype machines, punch cards, and the Unix culture that grew around line-based editors and pipes. Rivulet proposes an alternative lineage, one rooted in calligraphy and spatial composition, and in doing so helps visitors see that the paradigm we inherited was historically contingent, not inevitable.
Your description of writing Rivulet as "re-working existing strands to add others" reminds me of early programming constraints, where coders had to hold the entire machine state in mind. Rivulet seems to demand a similar totality of awareness, but spatial rather than temporal. It also makes me think of figures like Paul Graham or Maciej Ceglowski, programmers who came from painting and bring a visual sensibility to their work. Does Rivulet suggest something about what programming might owe to ways of thinking we don't usually associate with code?
@Titaÿna
I love the framing of Rivulet as grounded in an "alternative lineage" of "spatial composition." The word 'lineage' seems particularly serendipitous here because it is a lineage based on the expressive use of lines.
One part of this 'lineage' would surely be patch-cord based programming. We could think of the Enigma machine and its plugboards in the 1930s and 40s, and the corresponding Bletchley Park Bombe for decoding them with its own configuration of connecting lines (and historical recreations with their own cable-connected patch panels). There are even simulations like the Virtual Turing-Welchman Bombe that let you walk up to the machine and program it virtually connecting different parts with virtual cords in a 3D first-person walking simulator.
In more general purpose computing, ENIAC was also patch-based:
...and this word "patch" also descends through the history of computer music (e.g. the Moog synthesizer, also patch-cord programmable) to the "patcher" programming languages such as Max/MSP and PD (Pure Data), where each code patch is a tiny diagram.
Of course, Rivulet is not a patch-based language, in the sense that the shapes of the cords in an Enigma / ENIAC / Moog / Pure Data configuration do not have expressive shapes -- they are abstractly diagrammatic means to connect point A to point B, while in Rivulet, the journey is the destination (or perhaps the meander is the message/massage). In that sense it might have an alternate lineage in some of the two-dimensional esolangs or Befunge-like fungeoids.
Another element of Rivulet that hadn't occurred to me until very recently is its resistance to AI; something not intended, since it was written really before AI-assisted coding was very good.
Esolangs feel like a kind of AI resistance to begin with. They generally don't have enough code to train on (with the exception of super popular languages like brainfuck), and their logic is often very different from mainstream languages.
AI assistants tend to make everyone's code read the same, adding to the blandness of the procedural, multiparadigm languages like Python, JS, and C# that already all add the same features at the same time (e.g. when map/reduce and other functional array stuff was added to everything, or async, or whatever the trendy feature is this year). The difference between languages seems to become minimized. Esolangs, however, are small, random, and unpredictable, and there are many of them.
But Rivulet has two features that seem to push back against AI tools even more. First, the strands flow up and down, right-to-left sometimes, and these tools do not do well with code that can change direction at any moment (same issue for languages like Befunge). Second, there are so many ways to write the same code, such as in the Fibonacci programs above, which are essentially the same code, just drawn in a different way.
This may be somewhat arbitrary, of course, because we could train AI assistants to understand these flows. But then a thousand other esolangs differ from the defaults of how code is written in very different ways, some requiring a whole different approach to what we consider "a line of code."
To echo what @Temkin said here, AI resistance in esolangs feels like it comes in as many forms as esolangs themselves do. I'm thinking of something like Lenguage or maybe ぬいぐるみアート (stuffed animal art), one with programs far too large and resource-demanding to ever be efficiently trained on, and another that is so obtuse and dependent on human interpretation that it would be a real headache to train on. To me, this echoes something so inherent to programming languages that gets lost in efficiency narratives that esolangs keep alive - there are always multiple valid approaches to the same programming problem because of the inherent dynamics of programming languages as intermediary rhetoric between humans and machines. They speak to both human and machine logics and thought patterns, which are not a perfect set of opposites, but do have differences that lead to strange interactions and behaviors. In response to the vibe coding thread from Week 2, I went and tried some vibe coding tools and noticed that they commonly borrow from whatever the "it" library of the moment is, like Tailwind CSS. It sort of flattens out the coding task in a sense, and it is resistant to reinventing the wheel when it would seem unnecessary, something esolangs love to engage with. This thread and your comment reminded me once again why I started studying programming languages in the first place. Such strange objects in a vast communicative space, always surprising us, even and especially when we author them.