Render the Abelian sandpile — a deceptively simple cellular automaton that produces intricate, self-similar fractal art — to a PNG, using nothing but the Node.js standard library.
Drop a big pile of sand grains onto a single cell of a grid. Whenever a cell holds four or more grains it topples, handing one grain to each of its four orthogonal neighbours. Toppling cascades outward until every cell holds three grains or fewer.
The astonishing part — and the reason the model is called Abelian — is that the final configuration doesn't depend on the order in which cells topple. Drop the same number of grains and you always get the same picture: a symmetric, fractal-like mandala of 0s, 1s, 2s and 3s. It's one of the canonical examples of self-organised criticality.
There are no image libraries here: the PNG encoder (src/png.js) is written
from scratch — CRC-32, chunk framing, scanline filtering — on top of Node's
built-in zlib.
No dependencies. You just need Node.js (v16+).
git clone https://github.com/neverlone/sandpile.git
cd sandpilenode sandpile.js [options]
-g, --grains N grains to drop on the centre (default 50000)
-o, --out FILE PNG output path (default sandpile.png)
--ascii also print an ASCII preview to stdout
-h, --help show help
$ node sandpile.js --grains 512 --ascii --out pile.png
512 grains -> 51x51 grid, 5,175 topples in 6ms
wrote pile.png (272 bytes)
.....
.# ::: #.
:.:#:#:#:.:
:## : # : ##:
:# ######### #:
..## #.###.# ##..
#: ##:: # ::## :#
. #:#.:::#:::.#:# .
.:: ## : # : ## ::.
.:####### #######:.
.:: ## : # : ## ::.
. #:#.:::#:::.#:# .
#: ##:: # ::## :#
..## #.###.# ##..
:# ######### #:
:## : # : ##:
:.:#:#:#:.:
.# ::: #.
.....Bigger grain counts reveal more structure (and take longer):
$ node sandpile.js --grains 100000 --out pile.png
100000 grains -> 639x639 grid, 178,641,503 topples in ...ms
wrote pile.png (16,725 bytes)src/sandpile.js—computeSandpile(grains)runs the automaton. The grid is auto-sized to2·(⌈√grains⌉+2)+1so the pile never reaches the boundary and no sand is lost. Rather than moving one grain at a time, a cell holdinghgrains sheds⌊h/4⌋·4in a single step, and only cells that become unstable are queued for the next round — this converges dramatically faster.src/png.js—encodePNG(w, h, rgb)builds a truecolour PNG by hand: signature,IHDR, a zlib-deflatedIDATof zero-filtered scanlines, andIEND, each chunk wrapped with a length prefix and a CRC-32.sandpile.js— the CLI: parses flags, maps heights 0–3 to a four-colour palette, and writes the PNG (optionally printing an ASCII preview).
The Abelian property gives us two free correctness checks, both exercised by the tests: grains are conserved (the final heights sum to the number dropped), and the result is deterministic regardless of toppling order.
node --testMIT © neverlone
