48 Little Oxford St
Darlinghurst NSW 2017
02 9356 2837
425 Smith St
Fitzroy VIC 3065
21 Marcus Clarke St
New Acton ACT 2601
02 6257 2774
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.
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.
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.
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:
We perform these four simple steps for every pixel, a few times per second. These simple rules can lead to some pretty interesting results:
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:
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.