Piet Mondrian

Replicating Piet Mondrian’s art with code is no easy task, in fact honestly I’d say there’s no real way of pinning down his creations entirely with code, they were hand made. That said, we can try to replicate something within the realm of what would have been one of Piet’s works… so that’s what we will do in this tutorial… and yes, we will add the color as well.

As usual, here is our initial setup. Using window.devicePixelRatio to scale the canvas on retina screens, and setting our canvas size, with only a html <canvas> on the page.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');

var size = window.innerWidth;
var dpr = window.devicePixelRatio;
canvas.width = size * dpr;
canvas.height = size * dpr;
context.scale(dpr, dpr);
context.lineWidth = 8;

Now, the approach I’d like to take isn’t perfect, but what I’d like to do is start with a big square (the canvas) and then start to divide it up… we will pick a line, either horizontally or vertically and break any squares in that area… after that, we’ll add some random to the splitting, so not all squares are split, which should give us something around the famous Mondrian look, albeit probably a little more mathematically rigid.

We’ll create an array of squares.

11
12
13
14
15
16
17
var squares = [{
    x: 0,
    y: 0,
    width: size,
    height: size
  }];

And, as I like to do, we’ll create our “draw” function and call it, so we can see what we’re making.

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function draw() {
  for (var i = 0; i < squares.length; i++) {
    context.beginPath();
    context.rect(
      squares[i].x,
      squares[i].y,
      squares[i].width,
      squares[i].height
    );
    context.stroke();
  }
}

draw()

This is looping through all of our squares (just one at the moment, and drawing it on the canvas).

Now, we can create a function to find which squares should be split… and then the function to split the square in the direction we’ve given.

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function splitSquaresWith(coordinates) {
  // Loops through the squares, and find if
  // one should be split
}

function splitOnX(square, splitAt) {
// Create two new squares, based on
// splitting the given one at the
// x coordinate given
}

function splitOnY(square, splitAt) {
// Create two new squares, based on
// splitting the given one at the
// y coordinate given
}

splitSquaresWith({x: 160})
splitSquaresWith({y: 160})

You can see at the bottom that I’m also calling the split the squares on the x and y, both in the middle. If these work, we’ll know we can do a lot more splitting. But for now, these will be great for testing.

First, the splitSquaresWith function.

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const { x, y } = coordinates;

for (var i = squares.length - 1; i >= 0; i--) {
const square = squares[i];

if (x && x > square.x && x < square.x + square.width) {
squares.splice(i, 1);
splitOnX(square, x);
}

if (y && y > square.y && y < square.y + square.height) {
squares.splice(i, 1);
splitOnY(square, y);
}
}

There’s a bit going on here, including some cheeky little tricks.

Of course, our single square has disappeared because we need to fill out the splitOn functions, these are going to look pretty similar.

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
var squareA = {
  x: square.x,
  y: square.y,
  width: square.width - (square.width - splitAt + square.x),
  height: square.height
};

var squareB = {
x: splitAt,
y: square.y,
width: square.width - splitAt + square.x,
height: square.height
};

squares.push(squareA);
squares.push(squareB);

And…

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
var squareA = {
  x: square.x,
  y: square.y,
  width: square.width,
  height: square.height - (square.height - splitAt + square.y)
};

var squareB = {
x: square.x,
y: splitAt,
width: square.width,
height: square.height - splitAt + square.y
};

squares.push(squareA);
squares.push(squareB);

These two functions are creating two squares, where the previous single square was, and then adding them back into our squares array. You can see by splitting in the two centers, we’ve made a window.

Instead of our two practice splits, we’ll create a step variable, and split on that step over and over.

10
11
var step = size / 6;

And then the loop.

75
76
77
78
79
for (var i = 0; i < size; i += step) {
  splitSquaresWith({ y: i });
  splitSquaresWith({ x: i });
}

Whew, that was a lot of set up… we can get into the random now. Rather than splitting each and every square when we hit it, we’ll only split them half the time.

26
27
28
29
30
if(Math.random() > 0.5) {
  squares.splice(i, 1);
  splitOnX(square, x); 
}

Ooh, looking good. And on the y axis too!

33
34
35
36
37
if(Math.random() > 0.5) {
  squares.splice(i, 1);
  splitOnY(square, y); 
}

And there we have it, the shapes and structure we want! As always, with these tutorials you can hit the small arrow sitting between the editor and the demo and the code will rerun, if you press it a few times now, you’ll see our Mondrian structure take a few different shapes.

Let’s add some color in. First the variables, using those beautiful red, blue and yellow colors.

11
12
13
var white = '#F2F5F1';
var colors = ['#D40920', '#1356A2', '#F7D842']

We’ll pick three random squares, and give each of them a color. You might see only one or two colors, and that is because the same square was randomly selected twice.

87
88
89
90
for (var i = 0; i < colors.length; i++) {
  squares[Math.floor(Math.random() * squares.length)].color = colors[i];
}

And of course, making sure the draw function colors them in.

97
98
99
100
101
102
103
if(squares[i].color) {
  context.fillStyle = squares[i].color;
} else {
  context.fillStyle = white
}
context.fill()

Colors, beautiful! Again, if you don’t see any, it should be a matter of hitting the arrow on the side a few times.

You can see now as well, how simple it is to add or remove complexity based on the grid.

10
11
var step = size / 20;
10
11
var step = size / 4;
10
11
var step = size / 7;

And there we have it, a Mondrian. Please, hit the link below to have a play on CodePen, play with the colors, how you apply them, play with the split percentages. It’s all good fun!

↪ You can edit the code above, and have it run by pressing the arrow between the code and demo, but if you like, you can also play around a little with this code