Grok {Shan, Shui}*: Advent of understanding the generative art
I spent 24 days digging into the code of {Shan, Shui}* Chinese painting generator and lived to tell the story.
This article is written for my Substack, you can subscribe to it by email now!
While a lot of fellow developers spent this year’s advent on solving this year’s Advent of Code puzzles, I decided to have my own little “advent.”
A few weeks before December, I’ve stumbled upon an awesome generative art project by developer and artist Lingong Huang. It generates infinite (and surprisingly varying) scrolls of Chinese paintings like this:
It struck me with its beauty—as well as with the fact that I don’t have the slightest idea of how it might work. After half-an-hour looking into the code, I knew I wanted to have some level of understanding of how those things are done in general—and this one, in particular—enough to spend some hours on it. So, I thought, let it be my “advent”—and that happened.
Trying to understand
My usual way to understand how others’ code works is to translate it into another language. I did quite a few projects of this kind. And deciding on what language to translate it to, I made a weird decision: to just rewrite (some parts of the) {Shan, Shui}*
in the same JavaScript—but using modern features of the latest versions and semi-functional style of chained computations I am fond of.
This is not to say the original author’s code was “bad,” and I made it “good”—nothing of the kind. I just experimented with how things might be expressed so they would be closer to my reading/writing habits—just as some goal making me understand them.
The result of the project is the diary below. By the end of 24 advent days, I haven’t seen/read/rewritten all parts of the algorithm—that would require trice this amount of time, probably. But I went through all the layers of the picture and got some understanding on how things are done there—from a singular brushstroke to generating a whole mountain with a small village and a forest. And I didn’t lose my awe, and I am trying to pass it to others.
For the impatient: you can probably just go through the day 1 intro and then go immediately to day 24 with the overview of everything I’ve understood on the road. But I hope that reading through the whole diary might also be of some fun. There are small philosophical bits here and there, too, discussing how the code might be written, and it can be either insightful or repulsing, or both.
Note that JS is not my mother tongue. While using modern features for better expression of the meaning, I probably still went far for the modern preferred JS style: say, I dropped a lot of
var
s everywhere (never bothering withlet
/const
, though I probably should’ve) and didn’t use any linter/autoformatter. So it might be a somewhat painful reading or a JS developer by trade.And English is not my first language either. Usually, I use at least Grammarly to make sure the texts are decent… But with the amount of work this project brought, I can only promise the diary was spellchecked. Have mercy.
So, here goes!
The diary of grokking!
- Day 01: Advent of
Grok {Shan, Shui}*
—Intro to the project and the way I am planning (planned) to do it. - Day 02: Overview and starting to change the code—Analysing the whole, and starting to read & rewrite one of the tree-generating functions,
tree01
… - Day 03: Fighting the tree—…and continue…
- Day 04: Making sense of the tree—…and continue; also look into
poly
andblob
low-level shapes-generating functions. - Day 05: Moar treees!—Finalize
tree01
and look into another tree,tree02
, - Day 06: Even moar treees!—…and
tree03
. - Day 07: Build me a house—With trees being (somewhat) understood, starting to look into how architectural elements are made, starting with
arch02
; which turned out to be a tough nut to crack. A lot of simple arithmetic, but, like, a lot of it! So I started with thebox
function for drawing parallelepipeds… - Day 08: What’s in a box?—…and spent on it…
- Day 09: Lines and boxes—…good…
- Day 10: (there are no good puns with the word “stroke”)—…few days, finishing with other small(ish) functions.
- Day 11: Back to home—And with that, considering the
arch02
logic more or less understood. - Day 12: Seeing the forest behind the trees—Starting anew, from top-to-bottom now, I started to look into
chunkloader
function that created big parts of the landscape. - Day 13: Plan for the mountains—…and into the
mountplanner
function that decides which landscape feature would be where… - Day 14: Mountain-planning, continued—..and looked into how it decides on a list of big objects.
- Day 15: Between the landscape and the tree—Then, I started with the last big endeavor: the thing between chunk and a singular tree: a
mountain
with all the contours, trees, and houses. - Day 16: Deeper into the mountain—…and…
- Day 17: Let’s Vegetate!—…continued…
- Day 18: Generation details—…to do…
- Day 19: Wrapping up the mountain—…so…
- Day 20: REALLY wrapping up the mountain—…for a while.
- Day 21: Now what?—Then, I reviewed the big picture: planning and rendering “chunks”
- Day 22: Where were we?..—…and how it is made truly infinite.
- Day 23: Housekeeping—The technical one, explaining how I’ll organize the project/diary to wrap it up.
- Day 24: Putting it all together—The most important one, summarizing all of the findings and outcomes of the investigation!
PS
You know what? I have a Substack now! And a lot of plans for writings for the upcoming year.