Conway's Game of Life (in WebAssembly C)

September 26, 2021 — Ulysse

It has been a while since I wanted to write my web version of the game of life. It turns out I just needed this excuse: a Game of life may appear as slow, and use a lot of CPU when ran in JS. So I have two things to do now, the game of life AND some WebAssembly, let’s go!

WebAssembly

TL;DR: front-end calls an assembly function, which updates a state array, and we can then render it.

This is my first time digging through web assembly, and I’ll have to admit: it still feels like a very young techno. There is few documentation, and there are lot of people doing things various ways. The fact that WebAssembly can be used in various languages really doesn’t help in that matter…

However, following emscripten.org’s documentation and some examples on GitHub, I could manage my way through, and hopefully the code used for this article will be yet another example for one wanting to use the power of WebAssembly.

Basically, WebAssembly works by sharing a Uint8ClampedArray between the front-end and the assembly code. For instance, the render function will update an unsigned int array in C:

#define DATA(game) ((unsigned int*)((game).data))

unsigned int* EMSCRIPTEN_KEEPALIVE render(int index) {
	game_of_life game = *games[index];
	gol_step(game);
	gol_render(game);
	return &(DATA(game))[0];
}

And this array will be interpreted as an image in JavaScript:

const pointer = render(index)
const size = width * height * 4 // must match size of the C array
const data = new Uint8ClampedArray(memory.buffer, pointer, size)
const img = new ImageData(data, width, height)
canvas.getContext('2d').putImageData(img, 0, 0)

Note the EMSCRIPTEN_KEEPALIVE used in the C code, it is only here to say: this function will be called from JavaScript.

The hard part for me was debugging, for now I can only hint that you can hack your way through the shared data to write a String to it in C, and then read it in JS. I could not find a way to bind printf correctly however. Hence if you’re reading this article and know more than me about emscripten, please reach to me to talk about debugging. Otherwise, wait for another article on that topic.

The lifer community

If you do not know about Conway’s Game of Life yet, it is a game with very few rules, which boils down to this switch statment:

/* A 2d space of cells either alive (1) or dead (0). */
int [][] board;
/* For each cell, the count of alive adjacent neighbors up to 8 */
int [][] neighbors_count;
/* Each turn we update every cell based on its neighbors. */
switch(neighbors_count[i][j]) {
	case 2:
	/* An alive cell will survive on the nex generation
	 * if it has exactly 2 neighbors. */
		board[i][j] = board[i][j];
		break;
	case 3:
	/* A cell — either dead or alive — will live on next
	 * generation if it has exactly 3 neighbors. */
		board[i][j] = 1;
		break;
	/* If a cell has neither 2 nor 3 neighbors, it cannot live
	 * next round. */
	default: // 0-1 && 4-8
		board[i][j] = 0;
		break;
}

The idea is brilliant for it is really simple, yet it gives a lot of possiblities. Such that the game has more than 50 years and still has a large community discovering new forms of life within its rules.

Press any key to see it from the beginning.

Forms of life, such as the one above are divided in a lot of categories, such as spaceships (things that move) or still lives (things that don’t) and many more.

For more on the game of life itself, I suggets reading the wiki.

Some lifer (Brice Due) went all in, such that they created a metapixel which emulates the game of life, using the game of life! Here is some more information on the topic if you want to see how it works

What thumbnail for this article?

I know of WebAssembly, I know of Conway’s Game of Life, but the journey is not over yet! A good article has a good thumbnail, and it is obvious that I have to make some kind of gif out of my Game of Life implementation.

Fortunately, I did not have to use one of the huge C image library, as there is a really straightforward, less than 1k LOC lib on GitHub: lecram/gifenc.

Of course, this was not that easy, support for transparency was missing. However, as this is Open Source and people are nice, I quickly could discuss over transparency support in #11. And I discovered something: working on gif makes for really cool discussions full of colors, and easy reproductions!

Now the PR is merged, and if you share this article you’ll directly share a Game of Life example :)

The end of the journey

I’ve wrote a simple implemantation used for this article, feel free to use it to generate a gif, ascii art or some WebAssembly as in this article.

I have to thanks the ruby language once again, for it helped me to find a way to generalise the game_of_life.h implementation with the set_pixel and data elements, which are a pale copy of C extension data integration in ruby.

Of course, I’m welcoming PRs and criticism :)