Digital Weaving
Solo (2020)

Introduction

Digital Weaving is project where I implemented real world "weaving drafts" (basically a "computer program" for looms) and attempted to evolve computational cultures on.

It is another one of my favourite projects, even though the results weren't quite what I expected. It was very instructive for me to realize some of the ways in which cultural evolution works or does not work, something I can definitely use on future projects.

I worked on it in September and October of 2020, and I learned about the weaving drafts from Andrew Glassner (you can read his three articles here, or his gallery).


What are weaving drafts?

Basics

Looms (of the kind this emulates) have horizontal and vertical threads; let's say the horizontal ones are black, the vertical ones are white. A weaving draft is a diagram that describes which thread in on top (and thus visible) at each crossing. The best way to understand it is visually!

There are three elements to the weaving draft: the horizontal array (or warp threads), at the top; the vertical array (or weft threads), on the right; the motif (or tie-up), on the upper right; together, they fully determine the woven pattern, on the bottom left.

The horizontal and vertical arrays have exactly one "black" square per column and row, respectively. The motif can be any combination of black and white squares. As you can see (I hope) by following the picture on the right. You start in any square of the pattern, and look at the black square in the horizontal and vertical arrays (as shown by the red and blue lines, respectively). Then, you see you send horizontal and vertical beams from the arrays and see where they intersect in the motif. The black square of the pattern leads to a black square on the motif, and the white square on the pattern leads to a white square on the motif. You can see, then, how the three elements determine the final woven pattern.

The thing I love about this system is that it has a clear element of "information compression". If you make the arrays and motif large enough, you can the color of each square individually, so you could represent any picture with it. The interest comes in when you reduce the sizes. Now, you can't represent every picture; if your pattern is large enough, it means that some squares are reused somewhere. It is not always predictable where, and that's where the fun begins! You play around with the elements and see what comes up.

Structure

To my own system (extending Glassner's), I add two others: the Unit and the Symmetry String. The unit is simply a small pattern on the array. This unit is repeated throughout, but transformed. You can see it the figure to the right, where I highlight three occurrences of the unit. The red pattern, on the left, is the basic unit. The blue pattern, on the center, is reflected across the y-axis. The green pattern, on the right, is reflected across the x-axis. Rotations also occur, as well as both reflections. The symmetry string is simply a string of characters that describes how the unit is transformed to make the full array. In this example, it is "NRYNXY" (N=normal, R=rotated, X=reflected across x, Y=reflected across y). This string itself can be symmetrical (not the same as a palindrome).


Coloring

Coloring was an interesting part of this project, and it lead me to new places (which I still haven't fully explored). How do you color a binary pattern? The obvious answer, of course, is with two colors - but this isn't very interesting. In real world weaving, you color the threads themselves - but the fun of a formal system is, in part, which constraints you can drop (or add). I tried coloring the strands with alternating variations of the same color, but the result wasn't spectacular.

After some poking around, I came across an interesting idea, and that is of perceived lightness. The basic idea is that different hues (like yellow, red, blue) have different "brightness" levels (I saw a grayscale photo of vegetables and fruits, which were still very distinguishable in grayscale). This is a bit different from HSL's lightness value, and there are difference definitions for how lightness is actually computed, but the rough idea is a good start! The pictures that follow used completely random colors, except that ones with low lightness were chosen for the black squares and vice-versa for white squares.

Not perfect, but certainly a solid start. And, of course, the hue of the palette also matters.

Adding a more controlled palette, but still largely clustered by lightness, the results are significantly better! Also, if you notice, these pictures seem to have somehow more structure. Where there would be large zones of black or white before, now there are colorful patterns. This is because I colored the motif rather than the threads, as seen to the right.

The coloring system I settled on was:

  • Choose a color palette;
  • Distribute colors into two groups according to lightness (and drop ones too close to the middle);
  • Assign colors to the motif with light colors going to white squares and dark colors to the black.

You can see how this changes the initial black and white pattern - the figure on the right is the same as the one from the introduction, with added colors to pattern and motif.

All that's left in this project is to discuss how these patterns were created, and that leads to a discussion of cultural evolution.


Cultural Evolution

For a more detailed preamble, read my more general writing on Cultural Evolution. The general idea is to create not individual patterns, but styles of patterns, in a way that you can identify which style a particular pattern comes from and even its diverse influences.

Approach 1: Statistical Learning

My first approach was to use encapsulate a whole culture in a single agent. A single agent would learn based on the input patterns and produce many variations. For this, I started with Markov Chains to learn units, and Markov Fields to learn the motifs, though the algorithms themselves underwent several iterations with heavy borrowing from the Pixel-based Texture Synthesis with more general probability fields.

In a naive and mathematical way, it succeeded - both units and motifs were statistically different between agents (that is, agents started the same, but evolved differently and converged on a specific range of patterns over generations). Unfortunately, this wasn't enough to create a unified style - the system was too chaotic and small variations in unit and motif created big changes in the resulting pattern.

It did, sometimes, create a more coherent sequence of patterns, but it wasn't reliable enough to be usable.

Approach 2: Dawkins' Culto-morphs

After a mild crisis-of-faith, I remember Richard Dawkins' Biomorphs. This is not what I initially had in mind, but it turned out much more effective in such a chaotic system!

Basically, you encode the pattern in a single string, like genes in DNA, and variations are produce with random mutations. The downside to this is that it doesn't generalize to multiple agents as well (sure, you imagine an orgy-like reproduction, but it gets messy), but works great for single or pairs, where combining two strings it straightforward.

As you can see, there is a sense of style to the patterns. Two aspects warrant some comment: (1) not every mutation has visible effects - this is just like genetic mutations, some change some internal structure that only "unlocks" future evolutionary paths, and that is certainly the case here, since not every square of the motif is necessarily used; (2) this is not a satisfying representation of culture, since it lacks any kind of tradition - you can start with a completely random pattern and generate variations from there.

Conclusion

At the end of the day, neither of the paths was exactly what I wanted to get out of it. I tried other variations, for example, with the idea of critics and cultural differentiation (agents generate many patterns and cultural-critics decide which patterns are learned by the next generation, by being more representative of their own culture than other), but these were difficult to make very appealing (at least without an additional aesthetic evaluator of some kind).

It was interesting to work on regardless, and opened some doors while closing others, so hopefully the next project involving cultural evolution will be more fruitful!


Beyond

One thing I learned from this project, is that cultural evolution is difficult to make appealing when working on too low and indirect of a level. By this I mean that I was working on a system of pixels determined in a roundabout way by the arrays. While the patterns it produces are cool, a small change in the draft can lead to very different outcomes; in simulating culture, you probably want changes to be identifiable, to see the influences. This says little about real cultural evolution in humans, but my purpose is to create systems that players can look at and see how styles change in an identifiable way from place to place. In the future, I will try to use a coarser system of cultural change, perhaps by having multiple units and exchanging them whole, and so on.

Glassner create a whole language to describe the arrays. I found this a bit overkill, but my own system was very limited. Something else that would be cool to experiment it is multiple units - including, say, coupling units to the array position, using a different unit for the border of the pattern and another for the center; and also different transformations - instead of simply symmetrical transformations, individual squares could be change.

Finally (and this is something I'll probably do), to work out a more robust palette generator for binary patterns. The lightness idea has legs, I think, but I need to figure out how to generate pleasant hue palettes and put them all together.

Here's a little extra: