Josherich's Blog


Porting libigl using Emscripten, Get Started

22 Apr 2019

Playing around with libigl is fun, but C++ compiling isn’t. I read a fun piece about porting doom3 to browser, and decided to play around with this.



Compiling dependency

emcc compiler’s lookup path is ./incoming/system/include, as a short, you could always just put the source and headers inside this folder.

Main thread loop

Multi-threading is implemented using Web Workers, but the main thread is alwasys a window, meaning looping and polling doesn’t work. I have to, like most porting will do, convert it to callback style.



In early 2018, major browsers disabled ShareArrayBuffer for security issues(high resolution timer allows cache-based side channel attacks) relating “Spectre” and “Meltdown”, obviously people still need this to communicate between workers.

For now, Chrome is the only one among major browsers with enabling it as default. You can enable it in flag setting for others.

Loop to Callback

libigl run launch_rendering() as a forever loop with glfwPollEvents inside each iteration, and compute the duration for exact frame rate. For it to work in browser, where the window simply stop responding if any Javascript is running, you could easily move the rendering to callback functions(mouse_move, mouse_down, key_down). For animations, it still has to run as a loop.

Using GLFW

The amazing thing about Emscripten is glfw works without a change(except there’s a potential bug in src/library_glfw.js setting callback)


// support WebGL 2

// enable OpenGL ES 2.0 and 3.0 emulation, so that
// we have missing features in WebGL, like unbounded buffer, client-side arrays
// ES2 and ES3 flags are orthogonal
-s FULL_ES2=1
-s FULL_ES3=1

OpenGL to WebGL

Surprisingly, glPolygonMode is missing in WebGL, frame rendering could be converted to GL_LINES, since face index and vertex are all given by the OFF mesh format.

Migrating shader

use version 100 for webgl or 300 for webgl2, change in, out to attribute and varying


Useful debug flags:

// =1 for memory allocation errors checks
// =2 for showing function pointer information

// ignoring calling function pointer with wrong types

// show demangled function name

// enable general debug information

Import file

--preload-file path/to/data

Callback lifecycle

As the main thread is not looping forever, animation and viewer control is implemented in callback. I have to make sure objects in callback is alive outside main().