Splatter, splat, splash-ing ♬

It may have been old news already Internet Explorer officially going into retirement, not unlike Flash player reaching EOL possibly, clear sign happily however that web programming was never more fun and full featured. Writing vanilla, framework-less, transpiler-free client-side code is a real option these days: No fluff, no fuss! Finally!

And seeing how generative art is popular again reminds me of the late nineties when there was similar sense of excitement around creative media in general and, coming from a music background, I was trying to decide where to go next with my infant grasp of it and a new millennium on the horizon.

Chance encounter, turn of the century TTTP eye-opening handout

Exhibit 2-24 JNY-867-5309 [S-1][EHN] 19989 Denmark Str. Technologies to the 𝔓𝔢𝔬𝔭𝔩𝔢® web mechanic summary primer: Sounds relatable and I could not have asked for a better invite to be doing things online, thanks.

More than interactive graphics or AI, I was interested in conceptual and networking aspects, the promise of nonlinear narratives and self publishing, but I wonder if this all means somehow… it could be… the world is telling me… the time has come to… be pressing jacksonpollock.org onto a GIF?! 🤠

Quirky and fanatically pompous, Neen was amusing as a whole, but this one bit I have always thought special, because it deals with ever relevant impossible debate themes such as originality and appropriation, the where the value lies question, on results or mostly effort, or which proportion of both, and is also a nice play on the "computers can do that?" perception.

Vim vertical split showing signature section and splat function are parallel surface

Cheeky Wo! On top of The Artist taking their name through figlet(6) to "hacker proof" the source, duchamp-o-signing the canvas takes up roughly as much space as the star function? 🤦

I believe the story goes Stamen had initially developed a meatball (sic) based, Convergence inspired drawing app called splatter that was then modified and served up as an art piece with a domain to match, which is luckily still accessible to fiddle with and to study. From what I can tell, the idea is to:

  1. Sample cursor movement for control points tracing out Bézier curves to mimic brush strokes, thinner at higher rates.
  2. Run heavenly RANDOM-ous thrust 🚀 relative math pulled from Newton's own revised Principia secret notes unearthed in 1913 while dismantling his Leicester fields home where today, let's not forget, you can certainly borrow a telescope from, to determine the size and rotation of hellipses decorating the tips.
  3. Sprinkle extra paint drops here and there, RANDOM-ly.
  4. Pick a fresh color at RANDOM with each tap or specific keystroke.

Going through the base script, I first want to get rid of Paper.js, which is a fine library, but there really are one-for-one browser-native CanvasRenderingContext2D equivalent methods to fully cover intended behaviour here. For example, quadraticCurveTo() or ellipse() can be used respectively instead of Path.curveTo() or Path.Ellipse() etc.

Vim vertical split showing number of color switch cases running on key up

Mamma mia! The switch statement with more cases than there are planets in the solar system, 46 to be exact. Maybe if I were given to mock up something quick to help with a vanity project, I might have gone with equally pedantic logic in silent protest.

Next, I feel like disguising some of the geometric calculations with an abstraction for readability's sake, adjust for the latest JS language spec and anti-lint rules, because look at those barbaric var declarations from a bygone era crowding the entrace, DRY up the hyperinflated color switching routine, add touch device listeners, double check the visual output is no different, and that should leave only the GIF encoding part to figure out.

PJP like painting in name only sans a round line-cap, aka the shape used to draw the end points of lines

Meh Not liking what happens on the default "butt" line-cap setting.

Except, uh-oh, hold on, it's in the details, the formula is tied to the lineCap being round? Seems tacky. I appreciate the bright looks, but, engine wise, would rather focus on the original. I may have to factor out the very Web 2.0 nearly three quarters of a megabyte combined excess weight of Pixi.js, glMatrix, jQuery, Underscore, and Eric Meyer's reset.css, but at least the model is no fudge.

Simply put, path points are collected while dragging, iterated over to rubber stamp radial-alpha-gradient.png scaled copies for each, like glowing beads, rendered off screen, then texture fed into a couple of shaders, one for rotating through the color palette and one that reads alpha values past a certain threshold to know if the out pixel should be kept transparent.

Pre and post metaball shader visual comparison

Shadow canvas On the left, the actual drawing is hidden from view. On the right, post shader filter user-facing output.

Other than replacing external dependencies, I took care to: (a) WebGL2 reformat the main shader, (b) Preload SVG sprites for each color to avoid having to rely on a static image and to skip the ColorChangeFilter shader pass, (c) employ modern JS as appropriate.

Heavy tailed On each tick, choose a new direction and a step size that has higher probability of being short, imaginably close to Pollock's own moves.

Drawing manually can get boring of course. But, Lévy flight type RANDOM walking, in particular after the Pareto distribution in my eyes, works fairly okay for spreading paint apparently, so why not bring those elements into the mix as well. The trade off is longer jumps are noticeably straight line. However, lowering the max travel cap will counter that effect and gives more concentrated drips, which is sort of cool too.

Animated GIF sample output

End product demo One hundred and sixty (2408) frames of button operated, in-browser, self-contained, tech-current, copycat remix humor.

Since Go's standard library offers built-in image processing tools, dynamically compiling animated GIFs is reasonably straightforward to achieve using WebAssembly even. Not blocking the event loop took some trial and error, but offloading the encoding calls onto a web worker seems to have done the trick.

In toto