Along with dropped sup­port for NaCl, Mag­num now has first-class WebAssembly sup­port. I also took this op­por­tun­ity to over­haul the out­dated Show­case page with WebAssembly builds and there is a bunch more Em­scripten-re­lated good­ies all over the place!

As (P)NaCl was deemed dead by its cre­at­ors, I axed all sup­port for it from all re­pos. Frankly, it was quite a weird plat­form, some­thing in-between OpenGL ES 2.0 and WebGL 1.0, hav­ing very spe­cial ugly C++ API and shar­ing a lot of C++11 work­arounds with An­droid (sigh). In the Mag­num re­pos­it­ory alone, the amount of de­leted code was al­most 5k lines.

On the oth­er hand, adding sup­port for WebAssembly was al­most too easy. With Em­scripten 1.37.9 and new­er it just boils down to adding one new com­piler and linker flag, dif­fer­ent files are gen­er­ated (but they are still loaded auto­mat­ic­ally from the main *.js file), and everything else stays the same as with the clas­sic asm.js com­pil­a­tion.

But how?

If you pull latest re­vi­sions of Mag­num re­pos­it­or­ies, build­ing for WebAssembly in­stead of asm.js is just a mat­ter of spe­cify­ing dif­fer­ent tool­chain — Emscripten-wasm.cmake in­stead of Emscripten.cmake. Note that I had to use some new­er CMake func­tion­al­ity to make the dif­fer­ence, so you’ll need at least CMake 3.7 to com­pile for Em­scripten now. I hope that’s not a prob­lem for you — in the worst case you can al­ways down­load a pre­b­uilt bin­ary archive of CMake, ex­tract and run it from whatever loc­a­tion. More info about build­ing for WebAssembly is in the docs. If you are on Arch­Linux like me, there are also new PKGBUILD-emscripten-wasm and PKGBUILD-emscripten-wasm-webgl2 pack­age scripts in package/archlinux dir­ect­ory of each re­pos­it­ory to make your life easi­er.

In or­der to prop­erly de­ploy your WebAssembly app, you’ll need to change your install() step. With the OPTIONAL keyword you can sup­port both the asm.js case (sep­ar­ate *.js.mem file) and WebAssembly case (sep­ar­ate *.wasm file). Also, there is a new MAG­NUM_DE­PLOY_­PRE­FIX vari­able that can be used to dif­fer­en­ti­ate in­stall loc­a­tion for the fi­nal app from in­stall loc­a­tion for sys­tem files (so, for ex­ample, you can set MAGNUM_DEPLOY_PREFIX to point to a loc­a­tion used by your web­serv­er while CMAKE_INSTALL_PREFIX still points to sys­tem loc­a­tion where lib­rar­ies are stored):

if(CORRADE_TARGET_EMSCRIPTEN)
    install(FILES
        MyApplication.html EmscriptenApplication.js WebApplication.css
        DESTINATION ${MAGNUM_DEPLOY_PREFIX})
    install(TARGETS MyApplication DESTINATION ${MAGNUM_DEPLOY_PREFIX})
    install(FILES
        ${CMAKE_CURRENT_BINARY_DIR}/MyApplication.js.mem
        ${CMAKE_CURRENT_BINARY_DIR}/MyApplication.wasm
        DESTINATION ${MAGNUM_DEPLOY_PREFIX} OPTIONAL)
endif()

Note that in or­der to make full use of all the new fea­tures, you might want to up­date your cop­ies of EmscriptenApplication.js and WebApplication.css from the re­pos­it­ory.

Show me what you got

The most ex­cit­ing about WebAssembly and web things in gen­er­al is that you don’t have to get up from your com­puter, go to the app store across the street and tell them to down­load and in­stall a new app on your ma­chine. WebAssembly demos are just one click away — head over to the fully re­worked Show­case page! Apart from all ex­amples that were already por­ted to Em­scripten and now they are just re­com­piled in WebAssembly, there is also a fresh port of the Au­dio ex­ample:

Audio example screenshot
Au­dio Ex­ample webgl1 au­dio
Loads an OGG file and shows how to play spa­tial­ized au­dio with the Mag­num Au­dio lib­rary.

I aimed to make all the demo pages re­spons­ive and mo­bile-firendly, but nobody’s per­fect — if you see some­thing strange, don’t hes­it­ate to re­port a bug either on the web­site re­pos­it­ory or for a par­tic­u­lar ex­ample. Feed­back wel­come, as al­ways.

Browser support

Even though WebAssembly is not that new, it may hap­pen that the above ex­amples didn’t work on your ma­chine. Here’s a list of browsers that sup­port it:

Browser Sup­port state
In­ter­net Ex­plorer Ser­i­ously? No. Sorry.
Edge Since ver­sion 16 (Win­dows 10 Cre­at­ors Up­date)
Fire­fox Since ver­sion 52
Op­era Since ver­sion 45
Viv­aldi Since ver­sion 1.9
Chrome Since ver­sion 58
An­droid Chrome Since ver­sion 56
Sa­fari Since ver­sion 11 (ma­cOS 10.13)
iOS Sa­fari Since ver­sion 11 (iOS 11)

Windowless applications

In or­der to make prop­er browser test­ing pos­sible in the fu­ture, “win­dow­less” ap­plic­a­tions are now sup­por­ted in Em­scripten as well. In prac­tice it means that in­stead of show­ing a can­vas, the web app is show­ing its tex­tu­al out­put. This also makes the Mag­num Info util­it­ies fi­nally avail­able on the web:

Magnum GL Info screenshot
Mag­num GL Info asm.js fall­back webgl1 webgl2
Text util­ity print­ing info about Mag­num OpenGL cap­ab­il­it­ies. Ver­sions for WebGL 1, asm.js WebGL 1 and WebGL 2.
Magnum AL Info screenshot
Mag­num AL Info
Text util­ity print­ing out vari­ous in­form­a­tion about Mag­num and the Open­AL im­ple­ment­a­tion it’s run­ning on.

Win­dow­less ap­plic­a­tions for Em­scripten are us­ing the Plat­form::Win­dow­lessE­glAp­plic­a­tion class, see its doc­u­ment­a­tion for de­tailed us­age guide. There is also a new win­dow­less-em­scripten boot­strap pro­ject.

“Command-line” arguments

Along with win­dow­less ap­plic­a­tions be­ing sup­por­ted, it’s now pos­sible to pass “com­mand-line” ar­gu­ments to apps run­ning in the browser. This was pos­sible since ever when run­ning com­mand-line apps through Node.js, but now this can be done with the browser apps as well. Just pass the ar­gu­ments as URL GET para­met­ers. Only long named ar­gu­ments and boolean op­tions are sup­por­ted. For ex­ample, hav­ing the URL as

/my-app/?enable-msaa&magnum-disable-extensions=GL_OES_vertex_array_object GL_EXT_texture_filter_anisotropic

is equi­val­ent to call­ing the com­mand-line ver­sion of the ap­plic­a­tion as

./my-app --enable-msaa --magnum-disable-extensions "GL_OES_vertex_array_object GL_EXT_texture_filter_anisotropic"

The pro­gram name is pre­pen­ded to the ar­gu­ment list (to be­come argv[0]) auto­mat­ic­ally by Em­scripten and is hard­coded to ./this.program. All --magnum-* op­tions sup­por­ted by the en­gine are work­ing on Em­scripten-com­piled ap­plic­a­tions as well. More info in the docs.

Size comparison to asm.js

The dif­fer­ence is very minor — but that’s largely due to the fact that all bin­ary data in the (op­tim­ized) asm.js ver­sion were in a sep­ar­ate bin­ary file (in­stead of be­ing rep­res­en­ted in text) and a very ag­gress­ive clos­ure com­piler step was ap­plied to the gen­er­ated JS file to mini­fy it. Com­press­ing the data makes the dif­fer­ence even smal­ler — there’s simply the same amount of in­form­a­tion, just en­coded dif­fer­ently. Here’s a table show­ing size of the gen­er­ated *.js and *.js.mem / *.wasm files for the Tri­angle ex­ample:

Tri­angle ex­ample build Size
asm.js, un­com­pressed 720.3 kB
wasm, un­com­pressed 590.1 kB
asm.js, gzipped 179.9 kB
wasm, gzipped 165.9 kB

Be­cause com­press­ing the data really makes a dif­fer­ence, en­abling it on the serv­er is cru­cial for fast down­load times. There’s one prob­lem, though: by de­fault, the serv­ers are con­figured to com­press only tex­tu­al data such as *.js, *.html or *.txt files, ex­clud­ing the very-nicely-com­press­ible *.wasm data. Be­cause WebAssembly is quite new, re­ly­ing on its MIME-type might be prob­lem­at­ic and so it’s best to just rely on file ex­ten­sions in your Apache con­fig­ur­a­tion or .htaccess file:

AddOutputFilter DEFLATE html css js wasm

An­oth­er pos­sib­il­ity is re­nam­ing the *.wasm files to e.g. *.wasm.txt, which is a solu­tion when you don’t have the pos­sib­il­ity to over­ride your serv­er con­fig­ur­a­tion. But note that then the WebAssembly files won’t get loaded auto­ma­gic­ally and you need to sup­ply your own async load­ing code.

~ ~ ~

Okay, that’s all! There’s still quite a lot Em­scripten-re­lated fea­tures, demos and im­prove­ments in my buf­fer, so ex­pect an­oth­er blog post later!