Perlin Noise

Rendering & Graphics

Perlin noise is the smooth, organic randomness behind decades of procedural clouds, terrain and textures. It places a random gradient at each grid corner and blends between them with a carefully-shaped curve, so the field looks like drifting smoke rather than static. Here it flows across a Super Nintendo — computed entirely in fixed point, so it's bit-exact. Runs on its own; no joypad needed. Written in C and compiled with the llvm-mos-based 65816 toolchain (+mos-a16, 16-bit accumulator mode).

loading core…

Self-running demo — the noise field drifts like smoke.

Click the screen to focus, then watch. Tab away and it pauses.

What it is

The trick is the blend. Each cell of the grid gets a random direction; a sample point in between is the fade-weighted mix of the four surrounding gradients:

fade(t) = 6t^5 - 15t^4 + 10t^3;      // smooth S-curve blend
n = lerp(                             // blend the 4 corner gradients
      lerp(dot(gAA), dot(gBA), fade(xf)),
      lerp(dot(gAB), dot(gBB), fade(xf)),
      fade(yf));

That fade curve, 6t⁵−15t⁴+10t³, is what removes the blocky grid and leaves a smooth, flowing field. It's all multiply-adds in fixed point here, and the result is identical across every codegen mode.

Compiler stress-test

ItemWhat it exercises
gradient noiseUnlike random dots, Perlin noise is smooth: it places a random gradient at each grid corner and blends between them, so nearby points get similar values. The result looks like clouds, smoke or marble — coherent, not fuzzy.
the fade polynomialThe blend uses a specific quintic curve, 6t^5-15t^4+10t^3, which is flat at both ends (its first and second derivatives vanish) so the noise has no visible grid seams. Evaluating that polynomial per sample is the heart of it.
gradient dots + lerpAt each corner the gradient is dotted with the offset to the sample point, and the four results are interpolated with the fade curve. Lots of multiply-adds — done here entirely in fixed point.
no floats, bit-exactIt is computed in 8.8 fixed point with a seeded integer shuffle for the permutation, so there is no rounding ambiguity — the field is identical on the host and the 65816, a sharp differential.

Written in C with the llvm-mos 65816 toolchain and verified against bsnes-jg (and MAME where the SPC700 IPL is present). Hit Verify fidelity to reproduce the build gate's WRAM assert (gate CRC 0xA72D — a fold of noise samples and fade-polynomial values) live in this tab. No far pointers — the same source passes every way: host == default == +mos-a16 == +mos-xy16 == bsnes-jg, -verify clean.