• 2 Liverpool Lane
    Darlinghurst NSW 2010
    ‭02 9051 0561

  • 425 Smith St
    Fitzroy VIC 3065
    03 9070 5737

  • 21 Marcus Clarke St
    New Acton ACT 2601
    02 6257 2774

02 6257 2774
re-up spray paint.

This little web toy simulates wet spray paint on a brick wall. This is something we worked on a couple of years ago for a client, which unfortunately never got used!

There’s a few cool things going on here, if you’re interested:

As the user draws, we sample the coordinates of the mouse and store them in a queue for a few milliseconds. Then, when we have enough samples, we take this queue of coordinates and create a bezier curve, which we then render to a regular old HTML5 Canvas as a series of blurry circles, before then passing that canvas onto the WebGL side of things. The drawing canvas is cleared after every frame. You can see this happening in the low-speed GIF below — notice how there’s a bit of a delay between the mouse movement and the actual drawing operation.

Slow-mo drawing

Bezier curves should be familiar to anyone who’s spent some time in Adobe Illustrator, although you may not have heard the term! They’re just curves created from a few control points.

A classic bezier curve

So why use a bezier curve, rather than just drawing blobs to the canvas every animation frame? First of all, it ensures that the path of the paint is nice and fluid — it should look like someone has used their entire arms to plot the path of a paint can, not crappy mouse pivoting on their human wrist! Second, have you ever tried spray paint tool in old-school MS Paint?? Notice the gaps when moving at a higher speed.

Poor MS paint

Next up, a series of WebGL shaders process the incoming drawing data, apply it to our “wetness” and “colour” layers, apply lighting based on the brick wall “normal map”, and merge it all together for the final output.

But, once that paint has been applied, we still need to simulate the flow of liquid down the wall. This is actually still performed as part of the rendering process, since WebGL gives us access to pixel-based graphics acceleration.

Before every frame, we take the current “wetness” map, and the normal map, which are both 1024×1024, and we iterate over each pixel. For every pixel, we look at the wetness values of the surrounding pixels, multiplied by the normal value. This allows us to figure out how likely it is that paint should flow from the pixels above us, and in which direction. Using this value, we can update the current pixel “wetness” and “colour”, to produce the next wetness and colour maps. In essence, the liquid doesn’t flow down, but it’s actually pulled from above! Bear with me.

This project actually started out as an implementation of Conway’s Game of Life — an algorithm designed by a mathematician trying to show that simple systems can give rise to complex ones.

The rules for Game of Life are as follows:

  1. Any live cell with fewer than two live neighbors dies, as if by under population.
  2. Any live cell with two or three live neighbors lives on to the next generation.
  3. Any live cell with more than three live neighbors dies, as if by overpopulation.
  4. Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.

We perform these four simple steps for every pixel, a few times per second. These simple rules can lead to some pretty interesting results:

John Horton Conway’s Game of Life

The principles for our liquid simulation are actually kinda similar, although the rules are different. Just like the Game of LIfe, our simulation, you iterates over every pixel, and use each pixels neighbouring pixels to determine the new values (wetness and colour) for that pixel.

The rules for our liquid simulation are roughly:

  1. Calculate how “wet” the pixels above are, and also the physical slope between the two pixels using the normal map, as well as the difference in wetness
  2. If the wetness and slope are such that the liquid should flow, then we increase the wetness of the current tile, and also mix some of the above colour in with the current pixels colour.
  3. At the same time, we also reduce the wetness of the current pixel, whether the pixels above are flowing or not. This makes the simulation tend to dry out over time, which we want!

Do this 30 times per second, for every pixel, and allow the user to add wetness and colour, and you’ve got yourself a spray paint simulator.