Hacker News new | past | comments | ask | show | jobs | submit login
Grok {Shan, Shui}*: Advent of understanding the generative art (zverok.github.io)
227 points by abetusk on Dec 28, 2021 | hide | past | favorite | 22 comments



I grokked it too over a few weeks a few months ago, then rewrote it all in a sane language: C. There are also a few mistakes in the code, where it is clear what the author intended, but it is not what he wrote. After fixing them, the fences around buildings, for example, look correct. I then replaced a lot of the O(n^4) algorithms with faster ones for that one: O(n^2logn). This made it go from needing ~1e10 floating ops to render an image to 1e7, allowing me to do it on a small microcontroller, producing this: https://twitter.com/dmitrygr/status/1470934354712928257


Is your C code available? I could not find it in the linked twitter thread.


Not published (yet?)


Wow. Now THAT is impressive!


Bravo! Amazing work.


It generated a pizza shop for me :D

http://shan-shui-inf.lingdong.works/?seed=1640730920030

Seed: 1640730920030, view: 200, and I went right a few clicks.

Uploaded svg: https://raw.githubusercontent.com/chris-ux/images/ea0801790f...


Which part do you see as a pizza shop?

[edit] ohhh haha now I see it. That's hilarious.

https://github.com/LingDong-/shan-shui-inf/blob/9f754d2b2e73...


Classic programmer easter egg? :) Not sure how probable it is from a first glance at the code...


In rewritten code: https://zverok.github.io/advent2021/day11.html — I'd say it is relatively probable (grep for this line):

   if_(stories == 1, ()=> chance(2/3, ()=> [1, "Pizza Hut"]))
E.g. with 1-storey building of this type, chance is good! OTOH, the building of this type will 1 story with probability 1/4: https://github.com/zverok/grok-shan-shui/blob/main/grok.html...

...and how many of these buildings will be in the picture is more complicated. Anyway, just opened the online picture and scrolled it for a few iterations and met it almost immediately https://www.dropbox.com/s/mr1f0gpjr5zvrsq/pizza_hut.jpg?dl=0


Yeah, I am mentioning it at Day 11, while debugging the singular house logic: https://zverok.github.io/advent2021/day11.html :)


This is an inspiring series of writing. After reading the intro, I expected to read the first and last post as suggested by the author for 'impatient' people like me.

However, after reading the first post, I'm wondering, should I read one post a day, at the tempo the author has written, and see if I can grok all (or some) of what they have?

Yes, yes I should.

There's something particularly impressive about any creator who comfortably exposes their journey, not only their destination. This is something I'm trying to work on, being okay with incompleteness. Maybe it's something we all struggle with.


> This is an inspiring series of writing.

Check out his other writing, too, like the "Rebuilding the Spellchecker" series: https://zverok.github.io/spellchecker.html. And "Wikipedia as the Datasource": https://zverok.github.io/blog/2021-11-30-irregular1.html


I have {Shan, Shui} running on an e-Ink display framed on my wall.

https://dheera.net/projects/einkframe/

I made minor modifications to the code so it runs in NodeJS instead of the browser, and spits out an SVG file every 10 minutes, which is then rendered to the screen.

I'm now working on a 3-panel version that allows for a more wide perspective.


That’s what I wanted to make. Till I realized how slow r-pi0 is and that it will never be battery powered. That’s why I didn’t go this route…


Well done. I’ve played around with framing electronics and yours is a great example.


This kind of "code study" is underrated. Turns out I was also influenced by studying code by the same author, but on another of his projects: https://github.com/LingDong-/Hermit (video demo: https://www.youtube.com/watch?v=mPYeTJd8klQ )

The code isn't perfectly organized (eg. the file containing the main loop is literally called "foo.py"), but it was still clear enough to convince me that implementing 2D skeletal animation from scratch was actually a reasonable task for a hobby game, something that I ended up doing myself.


This is really nice. I was thinking about coding some generative art but after a short while it started feeling cold, unlike these nice landscapes. Use your favorite image search on "generative art" and you'll see that most looks plotted and mathematical, despite some of it being very good looking.


There are two tricks I know of to generative art, which, as it happens, also apply to manually crafted art.

With all things in pattern recognition this principle holds: if the proportions are like a thing you recognize, it maps to that thing symbolically. That could be physical proportion, range of pitch or rhythm, or otherwise. And that means that deconstructing a reference into a sample set of proportions is a useful way to create organic elements and reduce the "cold" of being strictly mathematical. To accomplish, for example, fantasy creature drawings is primarily a matter of reconstructing familiar creatures with new proportions and body plans - it's an art of remixing a stew of influences to create something new, and the methods can be fairly direct(a "photobash and trace" approach will take you to a mostly complete image, though it might be lacking in anatomical understanding). With {Shan, Shui}* the original sample set has a more direct and obvious source; what is interesting is the method of reconstruction. ML is very popular as a reconstruction tool now, but it's a big and unwieldy cannon. Collecting smaller algorithms together is a more intricate exercise but gives a lot more targeted intent to the result.

The second necessary element is chaotic behavior. Randomness is often applied instead of chaos, but we recognize randomness as noise, while we recognize chaos as "a very complex pattern". Hence we tend to see representations of the Mandelbrot set as more beautiful than Perlin noise, because Perlin noise is too random - while it's a believable texture at small scales, it doesn't suggest overall composition. Chaos creates a high depth of engagement in artwork since it mixes anticipation and surprise. It's the moment when you are reading a page-turner novel and need to know what happens next - you might have a rough idea of the plot but be wrong on the details.


This is fascinating and I'm guessing part of the research to combat "the uncanny valley" that we've read so much about.


Nice series, thanks.

In day2, the author refactored some "old-school" code[1], I'd say I prefer the old-school version (the new version is less readable to me). Old-school is not out-dated nor in-correct IMO.

[1] https://ibb.co/C0cYbdt


The `times` solution was overhtought, and later I get rid of it. In the end, the code of cycle like this are handled with `range`, for example:

    var points = range(resolution).map(
      i => ({x, y: y - (i * height) / resolution})
    )
The point of the rewriting `for()` cycles is not some abstract "making them new and shiny", but expose _meaning_ (in a way that would be obvious for _my reading habits_). Generally speaking, `for()` is "how we do" instead of "what we are doing". It might mean a lot of things; porting it into `map`, or `filter`, or `zip`, etc. allows to review and rethink what was the point of iteration.

As you might see in the final code[1] the initial "cycle of noise generation" you are pointint to was gone at all (because several iterations of "making it one phrase" made it obvious that it has no context of its own, and should just be embedded in a points generation).

With that being said, I explicitly stated many times throughout the diary I don't expect the approach to be for everyones liking.

[1] https://github.com/zverok/grok-shan-shui/blob/main/grok.html...


That's impressive.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: