Perita

Giving Old Games a New Home with Emscripten

Last week i added some of the games i submitted to game jams last year on itch.io to the website. All these games are made with Heaps, a game framework for Haxe, because it has the option to target HTML5 when building. While doing that, i grumbled to myself that i could not publish older games i did long ago because they were written in C++ and SDL, intended for desktop PC and Linux consoles. Until i remembered that Emscripten exists and i heard it has a good port of SDL.

Emscripten is an LLVM-based compiler that can compile C++ projects to WebAssembly, and the result can be run on browsers.

To be honest, even though i found some problems during the port, i was surprised how easy it was. Emscripten supplies tools to run CMake with the correct parameters set and to build the project with make. And most code compiles without any problem whatsoever. The only real big problem i had was with the game’s main loop.

Most of my games run in a loop similar to this:

int main() {
	// …
	Uint32 frame_rate = 28; // 35FPS
	Uint32 last_ticks = SDL_GetTicks();
	while (!game_finished) {
		update_game(frame_rate);
		draw_game(screen);
		SDL_Flip(screen);
	
		Uint32 current_ticks = SDL_GetTicks();
		Uint32 elapsed_time =  current_ticks - last_ticks;
		if (elapsed_time > frame_rate) {
			SDL_Delay(frame_rate - elapsed_time);
		}
		last_ticks = current_ticks;
	}
	return EXIT_SUCCESS;
}

Since none of my games is very complex, it is unlikely that they will take more than 28 milliseconds to update and render each cycle, and the call to SDL_Delay pauses the game to sync with the target frames per second. This works for desktop and mobile games, but the browser runs JavaScript code effectively in a single thread, and this loop would freeze the browser.

The solution that Emscripten gives us is to move the code inside the loop into a separate function, and call that function from within the loop for native targets, but for Emscripten we have to use the emscripten_set_main_loop function instead.

#include <emscripten.h>
#include <emscripten/html5.h>

static Uint32 frame_rate = 28; // 35FPS

int main() {
	// …
#ifdef __EMSCRIPTEN__
	emscripten_set_main_loop(loop_cycle, 0, 1);
#else
	Uint32 last_ticks = SDL_GetTicks();
	while (!game_finished) {
		loop_cycle();
		Uint32 current_ticks = SDL_GetTicks();
		Uint32 elapsed_time =  current_ticks - last_ticks;
		if (elapsed_time > frame_rate) {
			SDL_Delay(frame_rate - elapsed_time);
		}
		last_ticks = current_ticks;
	}
	return EXIT_SUCCESS;
#endif
}

void loop_cycle() {
	update_game(frame_rate);
	draw_game(screen);
	SDL_Flip(screen);
}

Setting 0 or a negative value as the second parameter of emscripten_set_main_loop tells Emscripten to use the browser’s requestAnimationFrame to call our loop_cycle function each time the browser is going to repaint. Passing a true value to emscripten_set_main_loop’s third parameters means that this is an infinite loop and that control won’t return to the application after the function call.

That, and making sure that CMake does not fail to find SDL, was almost all i had to do for it to work.