Calling OpenGL from C on the Web by Using Emscripten, Sharing Common Code with Android and iOS

In the last two posts, we started building up a simple system to reuse a common set of C code in Android and iOS:

In this post, we’ll also add support for emscripten, an LLVM-to-JavaScript compiler that can convert C and C++ code into JavaScript. Emscripten is quite a neat piece of technology, and has led to further improvements to JavaScript engines, such as asm.js. Check out all of the demos over at the wiki.


For this post, you’ll need to have Emscripten installed and configured; we’ll cover installation instructions further below. It’ll also be helpful if you’ve completed the first two posts in this series: OpenGL from C on Android by using the NDK and Calling OpenGL from C on iOS, Sharing Common Code with Android. If not, then you can also download the code from GitHub and follow along.

Installing emscripten

Installing on Windows (tested on Windows 8)

There is a set of detailed instructions available at There’s no need to build anything from source as there’s prebuilt binaries for everything you need.

Here are a few gotchas that you might run into during the install:

  • The GCC and Clang archives need to be extracted to the same location, such as C:\mingw64.
  • The paths in .emscripten should be specified with forward slashes, as in ‘C:/mingw64’, or double backward slashes, as in ‘C:\\mingw64’.
  • TEMP_DIR in .emscripten should be set to a valid path, such as ‘C:\\Windows\\Temp’.

You can then test the install by entering the following commands into a command prompt from the emscripten directory:

python emcc tests\hello_world.cpp -o hello_world.html

Installing on Mac OS X (tested on OS X 10.8.4)

The instructions over at should get you up and running. Instead of brew install node, you can also enter sudo port install nodejs, if using MacPorts. I installed emscripten and LLVM into the /opt directory.

First you should run emcc from the emscripten directory to create a default config file in ~/.emscripten. After configuring ~/.emscripten and checking that all paths are correct, you can test the install by entering the following into a terminal shell from the emscripten directory:

./emcc tests/hello_world.cpp -o hello_world.html
open hello_world.html

Installing on Ubuntu Linux (tested on Ubuntu 13.04)

The following commands should be entered into a terminal shell; They were adapted from

Installing prerequisites

sudo apt-get update; sudo apt-get install build-essential openjdk-7-jdk openjdk-7-jre-headless git

Installing node.js:

Download the latest node.js from, extract it, and then build & install it with the following commands from inside the nodejs source directory:

sudo make install

Installing LLVM

sudo apt-get install llvm clang

To download and install LLVM and Clang from source, instead, see the instructions on this page:

Installing emscripten

sudo mkdir /opt/emscripten
sudo chmod 777 /opt/emscripten
cd /opt
git clone git:// emscripten

Configuring emscripten

cd emscripten

This command will print out a listing with the auto-detected paths for LLVM and other utilities. Check that all paths are correct, and edit ~/.emscripten if any are not.

You can then test out the install by entering the following commands:

./emcc tests/hello_world.cpp -o hello_world.html
xdg-open hello_world.html

If all goes well, you should then see a browser window open with “hello, world!” printed out in a box.

Adding support for emscripten

Let’s start by creating a new folder called emscripten in the airhockey folder. In that new folder, let’s create a new source file called main.c, beginning with the following contents:

#include <stdlib.h>
#include <stdio.h>
#include <GL/glfw.h>
#include <emscripten/emscripten.h>
#include "game.h"

int init_gl();
void do_frame();
void shutdown_gl();

int main()
	if (init_gl() == GL_TRUE) {
		emscripten_set_main_loop(do_frame, 0, 1);


	return 0;

In this C source file, we’ve cleared a few functions, and then we’ve defined the main body of our program. The program will begin by calling init_gl() (a function that we’ll define further below) to initialize OpenGL, then it will call on_surface_created() and on_surface_changed() from our common code, and then it will call a special emscripten function, emscripten_set_main_loop(), which can simulate an infinite loop by using the browser’s requestAnimationFrame mechanism.

Let’s complete the rest of the source file:

int init_gl()
	const int width = 480,
	         height = 800;

	if (glfwInit() != GL_TRUE) {
		printf("glfwInit() failed\n");
		return GL_FALSE;

	if (glfwOpenWindow(width, height, 8, 8, 8, 8, 16, 0, GLFW_WINDOW) != GL_TRUE) {
		printf("glfwOpenWindow() failed\n");
    	return GL_FALSE;

    return GL_TRUE;

void do_frame()

void shutdown_gl()

In the rest of this code, we use GLFW, an OpenGL library for managing OpenGL contexts, creating windows, and handling input. Emscripten has special support for GLFW built into it, so that the calls will be translated to matching JavaScript code on compilation.

Like we did for Android and iOS, we also need to define where the OpenGL headers are stored for our common code. Save the following into a new file called glwrapper.h in airhockey/emscripten/:

#include <GLES2/gl2.h>

Building the code and running it in a browser

To build the program, run the following command in a terminal shell from airhockey/emscripten/:

emcc -I. -I../common main.c ../common/game.c -o airhockey.html

In the GitHub project, there’s also a Makefile which will build airhockey.html when emmake make is called. This Makefile can also be used on Windows by running python emmake mingw32-make, putting the right paths where appropriate. To see the code in action, just open up airhockey.html in a browser.

When we ask emscripten to generate an HTML file, it will generate an HTML file that contains the embedded code, which you can see further below (WebGL support is required to see the OpenGL code in action):

Exploring further

The full source code for this lesson can be found at the GitHub project. Now that we have a base setup in Android, iOS, and emscripten, we can start fleshing out our project in the next few posts. Emscripten is pretty neat, and I definitely recommend checking out the samples over at!

About the book

Android is booming like never before, with millions of devices shipping every day. In OpenGL ES 2 for Android: A Quick-Start Guide, you’ll learn all about shaders and the OpenGL pipeline, and discover the power of OpenGL ES 2.0, which is much more feature-rich than its predecessor.

It’s never been a better time to learn how to create your own 3D games and live wallpapers. If you can program in Java and you have a creative vision that you’d like to share with the world, then this is the book for you.


10 thoughts on “Calling OpenGL from C on the Web by Using Emscripten, Sharing Common Code with Android and iOS”

  1. Awesome article – I’m curious though, where is the process happening by which the browser knows to translate OpenGL into OpenGL ES? The browser only knows ES. I imagine either:

    – Emscripten is doing it (unlikely)
    – GLFW determines it

    Or maybe those functions you are using are strictly OpenGL ES (I’m familiar with OpenGL but not so much ES, and they do look pretty foreign to me).


    1. I believe that it all gets translated into WebGL calls by the emscripten compiler (it has special behaviour for certain APIs including GL); so in effect only what WebGL supports would be supported, AFAIK.

  2. Hi,

    Do you have a github repo for the source code of your book? Because there’s some part that really confusing. I got so many red marks on my class. Just want to check if im following them correctly.

    1. Hi Skadush,

      There is no Github for the book but there is downloadable source code from For this specific post (which is not part of the book), there is GitHub code available at The C code in this post will unfortunately have some red underlined errors in Eclipse, but that’s OK — you can manually delete the errors from the “Problems” view and the code should still compile fine.

Leave a Reply

Your email address will not be published. Required fields are marked *