I first saw this effect used with an image of Alan Turing and the word CODE superimposed. It was many months before several disparate ideas came together and I finally rediscovered how to generate these kinds of pictures. Here now I’ll show you how I built up the Processing code to do the same.
I used Processing because it is my favorite for making quick visual prototypes. I’m going to skip the basics of Processing and get right to it. In the first draft all I did is make a tiny sketch to draw one half of one Truchet Tile of diagonal lines:
int tileSize = 60;
int lineSpacing = 10;
// style=/
void tileA(int x0,int y0) {
int x1=x0+tileSize;
int y1=y0+tileSize;
for(int x=0;x<tileSize;x+=lineSpacing) {
line(x0+x,y0,x0,y0+x);
}
}
void setup() {
size(960,960);
for(int y=0;y<height;y+=tileSize) {
for(int x=0;x<width;x+=tileSize) {
tileA();
}
}
}
void draw() {}
Which makes this image:
Next, I added the code to fill in the second half of the square and a second method to draw the second tile – a repeat of tile 1 turned 90 degrees. To make it a bit fun I filled the screen with randomly chosen tiles.
// style=/
void tileA(int x0,int y0) {
int x1=x0+tileSize;
int y1=y0+tileSize;
for(int x=0;x<tileSize;x+=lineSpacing) {
line(x0+x,y0,x0,y0+x);
line(x0+x,y1,x1,y0+x);
}
}
// style=\
void tileB(int x0,int y0) {
int x1=x0+tileSize;
int y1=y0+tileSize;
for(int x=0;x<tileSize;x+=lineSpacing) {
line(x0+x,y0,x1,y1-x);
line(x0+x,y1,x0,y1-x);
}
}
void setup() {
size(960,960);
for(int y=0;y<height;y+=tileSize) {
for(int x=0;x<width;x+=tileSize) {
int t = floor(random(2));
if(0==t) tileA(x,y);
else tileB(x,y);
}
}
}
Nice. Next, in setup, I loaded an image that was exactly the size of my window.
PImage img;
void setup() {
size(960,960);
img = loadImage("tunein-turnon-dropout-karililt.jpg");
img.filter(GRAY);
img.loadPixels();
for(int y=0;y<height;y+=tileSize) {
...
I knew I wanted to play with the Processing method strokeWeight() to make the line thick wherever the picture is dark. strokeWeight() can only be set once per line, so where I used to have one long line I would now need lots of little lines. I swapped out every call to line() with a new method, inter()
int iterSize = 1;
float maxWeight = lineSpacing*2/3;
void inter(int x0,int y0,int x1,int y1) {
// get the line length
float dx = x1-x0;
float dy = y1-y0;
float len = sqrt(dx*dx+dy*dy);
// step many times over the whole line in tiny segments.
for(float i=0;i<len;i+=iterSize) {
// find the x/y at the start of the line segment
float ox = x0+dx*(i/len);
float oy = y0+dy*(i/len);
// don't go past the end of the line.
float i2 = min(i+iterSize,len);
// find the x/y at the end of the line segment
float px = x0+dx*(i2/len);
float py = y0+dy*(i2/len);
// the stroke weight is the image darkness.
float c=0;
// as long as we're still inside the image!
if( px>=0 && px<img.width &&
py>=0 && py<img.height ) {
float darkness = red(img.get((int)px,(int)py));
// I have a number where 0 is black and 255 is white.
// I want 0 for white and maxWeight for black.
c = maxWeight * (255.0-darkness) / 255.0;
}
// keep the weight between 1 and maxWeight.
c = max(c,1);
// now draw the line segment.
line(ox,oy,px,py,c));
}
}
And boom!
By the way: go give a like to @karililt
My last experiment was to make the tileSize equal to the lineSpacing – as small as they can get – and use a second image of white letters on a black background to choose the tile. In this way I was able to recreate the image that first inspired me.
If you liked that, like/share/subscribe/patreon. You can find all the code on my github.