Questing · 2026-07-09 · Physics · Complexity · Zero Dependencies

PILE

Drop grains of sand onto a grid, one at a time. Each grain might do nothing. Or it might trigger an avalanche that rearranges the entire pile. You never know which. The pile is always poised at the edge — between stillness and cascade. That edge is a law of nature.

Open Pile →

What Is Pile?

Pile simulates the Abelian Sandpile Model — a cellular automaton with a single rule so simple it fits in a sentence: any cell with four or more grains topples, losing four grains and giving one to each of its four orthogonal neighbors. Grains that fall off the boundary are lost. The simulation starts with an empty grid and drops grains one at a time onto the center.

Most grains land, the count increments by one, and nothing else happens. Occasionally a grain tips a cell over the threshold and a cascade begins — a ripple of topplings that can spread across the entire 400×400 grid before settling. The amber flash overlay makes these avalanches visible as they propagate outward from the center.

What emerges over millions of grains is a fractal mandala — a four-color symmetric pattern of extraordinary intricate beauty that no one designed. It arises from the rule alone.

What Is Self-Organized Criticality?

In 1987, Danish physicist Per Bak, together with Chao Tang and Kurt Wiesenfeld, introduced the sandpile model in a paper titled “Self-Organized Criticality: An Explanation of the 1/f Noise.” Their insight was striking: the pile does not need to be tuned to a critical state. It finds the critical state on its own, and then stays there. This is what “self-organized” means — no external parameter needs to be set just right.

The signature of the critical state is a power-law distribution of avalanche sizes. In a subcritical system, avalanches have a characteristic size — most are small, and very large ones are exponentially rare. In the critical sandpile, there is no characteristic size at all. An avalanche 10,000 times larger than average is exactly as “normal” as an avalanche 10 times larger. The distribution follows P(s) ∝ s−τ with τ ≈ 1.0 for the 2D sandpile.

Bak argued that SOC is universal — it explains the 1/f (“pink”) noise found in music, traffic, neural firing, economic fluctuations, and river flow. The Gutenberg-Richter law for earthquake magnitudes — where a magnitude-5 earthquake occurs approximately 10× more often than a magnitude-6, following log N ∝ −bM with b ≈ 1.0 — is exactly the power-law signature of a self-organized critical system. Solar flares, extinction events in the fossil record, and the size distribution of neuron avalanches in the cortex all share the same exponent structure. The sandpile is the simplest model of a system that evolves, without any tuning, to perpetual criticality.

The Toppling Rule

The model is defined by a single update rule applied simultaneously to all unstable cells:

For each cell (x, y) with height h ≥ 4:
  h(x,   y  ) -= 4
  h(x-1, y  ) += 1   (if x > 0)
  h(x+1, y  ) += 1   (if x < N-1)
  h(x,   y-1) += 1   (if y > 0)
  h(x,   y+1) += 1   (if y < N-1)

Repeat until no cell has h ≥ 4.
One grain is then dropped at the center: h(N/2, N/2) += 1.

The model is called “abelian” because the final stable configuration is independent of the order in which unstable cells are processed. This is a deep algebraic property: the toppling operators commute. The stable pile after dropping N grains at the center is uniquely determined by N, regardless of the path taken. This means the fractal mandala is not a coincidence — it is the unique fixed point of the dynamics.

Why Does a Fractal Mandala Emerge?

The height pattern of the stable pile — the image you see on the canvas — is one of the most surprising objects in mathematics. It has fourfold rotational symmetry (from the square lattice), but within each quadrant the structure is fractal: the same patterns repeat at smaller and smaller scales. The pile after N grains contains the pile after N/4 grains as an approximate sub-pattern.

The four colors represent the four stable heights (0, 1, 2, 3). Height 3 (amber) creates the outermost filigree of a cell one grain away from toppling — the most “dangerous” cells, poised at the edge. Height 0 (near-black) marks regions the cascade has never reached. The structure between them has a fractal dimension of approximately 2.0 — it fills the plane densely, yet with fractal boundaries between its color regions.

As you watch the pile grow, you will notice the pattern is not random — it has long straight lines, arcs, and geometric motifs that repeat across scales. These arise from the underlying lattice symmetry interacting with the criticality. Mathematicians have proven that the boundary of the pile (where grains first reach as N grows) converges to a limiting shape — an octagon — as N → ∞.

Why the Avalanche Bar?

The thin bar below the canvas lights up when an avalanche occurs, scaled logarithmically to the peak avalanche size in that frame. A flicker is a small avalanche — a handful of topplings, over in microseconds. A long bright pulse is a large one: thousands of topplings sweeping across the grid before the pile restabilizes.

Watch the bar over time. Early in the simulation (low grain count), most flickers are small — the pile is compact and cascades die out quickly. As the pile grows toward the boundary, large avalanches become more frequent. There is no “safe zone” where you can predict whether the next grain will do nothing or trigger a massive restructuring. That unpredictability is the critical state.

Implementation

The simulation runs on a 400×400 grid stored in an Int32Array (160,000 cells). Each animation frame processes a batch of grains (adjustable: ×1 to ×100 per frame). Stabilization uses a BFS queue — an Int32Array of 640,000 slots with a Uint8Array in-queue flag to prevent duplicate additions. Within each toppling, all four grains are distributed at once (batched toppling), which preserves the abelian property while reducing BFS iterations.

// Batched toppling (abelian-equivalent):
topples = grid[idx] >> 2           // floor(h / 4)
grid[idx] -= topples * 4           // h mod 4
each neighbor += topples

// Flash overlay (per-frame decay):
flash[idx] = 1.0   (on topple)
rendered_color = lerp(height_color, gold, flash[idx])
flash[idx] *= 0.72 per frame → fades in ~8 frames

Rendering writes directly to an ImageData pixel buffer (Canvas 2D). The flash component blends each height color toward bright gold (#E6C364) on the frame of toppling and decays multiplicatively. The canvas uses image-rendering: pixelated so each 400×400 cell maps to a crisp screen pixel. Zero libraries, zero backend, zero server calls.

Built 2026-07-09 · Pure JavaScript · Canvas 2D · Zero backend · Live demo → · More questing →