If you have WS2812 or similar programmable RGB leds, and a microcontroller with

*N*×

*M*×2+768 bytes of RAM, you can do some really nice fire and plasma effects.

The idea is that you use a good pseudorandom number generator as a seed in suitable cells. I recommend using the 32 or 40 high bits of Xorshift64*, e.g.

`#include <stdint.h>`

// Any nonzero 64-bit seed will work; randomize it!

uint64_t prng_state = 1;

// Cache for 8-bit pseudorandom numbers.

uint8_t prng_cache[4];

uint8_t prng_cached = 0;

// Obtain a random number between 0 and 255.

uint8_t prng_u8(void)

{

if (prng_cached > 0)

return prng_cache[--prng_cached];

uint64_t x = prng_state;

x ^= x >> 12;

x ^= x << 25;

x ^= x >> 27;

prng_state = x;

x = (x * UINT64_C(2685821657736338717)) >> 24;

prng_cache[0] = x;

prng_cache[1] = x >> 8;

prng_cache[2] = x >> 16;

prng_cache[3] = x >> 24;

prng_cached = 4;

return x >> 32;

}

You declare two buffers and an RGB look-up table:

`#define N //rows, vertical`

#define M //columns, horizontal

typedef struct {

uint8_t cell [N][M];

} grid;

grid g[2];

uint8_t rgb[256][3];

For each inner cell, you apply a 3×3 kernel, centered on the cell, with the weights summing to 257 (or less if you want dimming).

`const uint8_t kernel[3][3] = { ... };`

void apply(grid *const dst, grid *const src, const uint8_t flicker, const uint8_t bias)

{

const uint_fast16_t cmin = (uint_fast16_t)flicker * (uint_fast16_t)bias;

const uint_fast16_t cmax = (uint16_t)( -(uint_fast16_t)flicker * (uint_fast16_t)(255 - bias) );

for (uint_fast8_t n = 1; n < N-1; n++) {

for (uint_fast8_t m = 1; m < M-1; m++) {

uint16_t c = (uint_fast16_t)(kernel[ 0 ][ 0 ]) * (uint_fast16_t)(src->cell[ n-1 ][ m-1 ])

+ (uint_fast16_t)(kernel[ 0 ][ 1 ]) * (uint_fast16_t)(src->cell[ n-1 ][ m ])

+ (uint_fast16_t)(kernel[ 0 ][ 2 ]) * (uint_fast16_t)(src->cell[ n-1 ][ m+1 ])

+ (uint_fast16_t)(kernel[ 1 ][ 0 ]) * (uint_fast16_t)(src->cell[ n ][ m-1 ])

+ (uint_fast16_t)(kernel[ 1 ][ 1 ]) * (uint_fast16_t)(src->cell[ n ][ m ])

+ (uint_fast16_t)(kernel[ 1 ][ 2 ]) * (uint_fast16_t)(src->cell[ n ][ m+1 ])

+ (uint_fast16_t)(kernel[ 2 ][ 0 ]) * (uint_fast16_t)(src->cell[ n+1 ][ m-1 ])

+ (uint_fast16_t)(kernel[ 2 ][ 1 ]) * (uint_fast16_t)(src->cell[ n+1 ][ m ])

+ (uint_fast16_t)(kernel[ 2 ][ 2 ]) * (uint_fast16_t)(src->cell[ n+1 ][ m+1 ])

;

if (c >= cmin && c <= cmax) {

c = c - cmin + prng_u8() * uint_fast16_t)flicker;

}

dst->cell[n][m] = c >> 8;

}

}

}

You need two grids,

` grid g, gtemp;`Initialize the main one to all zeros, set the seed cell values using the

`prng_u8()` function, and apply the kernel twice,

` apply(gtemp, g, 0, 128);`` apply(g, gtemp, 0, 128);`The flicker term is how much randomness is added to each cell, and bias is whether it tends to increase (<128) or decrease (>128) the cell value.

Now you have the updated state in the main grid, and you can sample specific cells for your RGB LEDs, using the RGB look-up table

`rgb[``g.cell[`*row*][*column*]][`0..2]` for red, green, and blue components for that LED, respectively. You can also do a number of

`apply()` rounds before updating the LEDs.

My favourite is using a regular triangular grid for the LEDs, with relatively large grids.

If you use a RAM-based

`kernel`, you can adjust it in real time, skewing it to a side, creating changing "draft". Bias it completely towards one side to get drifting plasma/fire.

The possibilities are very nearly limitless, and it is easy to get going and some nice results, while genuine-looking fire takes careful

*tuning* and experimentation.