Asm.js and WebGL for Unity and Unreal Engine
This article is part of a web dev series from Microsoft. Thank you for supporting the partners who make SitePoint possible.
Unity and Epic’s Unreal Engine, the popular middleware tools frequently used by game developers, are not limited to creating compiled applications that run as an executable. Unity previously had a web player, which was a downloadable plugin that used ActiveX. Chrome killed support for NPAP (Netscape Plugin API), but announced it over one year ago.
In April, with the release of Chrome 42 stable, they finally put the axe to it. There are a number of reasons as to why, but most notably they stated “hangs, crashes, security incidents, and code complexity. Google suggests using web standards instead, such as WebGL, which I will explain below.
Microsoft is following suit and deprecating ActiveX, VBScript, attachEvent, and other legacy technologies in favor of web standards. The need for ActiveX controls has been significantly reduced by HTML5-era capabilities, which also produces interoperable code across browsers.
With the advent of WebGL and asm.js, developers can now harness much of the power of their computing device from within the browser and access markets previously unavailable. In this tutorial, I’ll show you a slice of “how”:
Why Compile to JavaScript?
JavaScript is the only language which works in all web browsers. Although only JavaScript will run in the browser, you can still write in other languages and still compile to JavaScript, thereby allowing it to also run in the browser. This is made possible by a technology known as emscripten.
Emscripten is an LLVM based project that compiles C and C++ into highly performant JavaScript in the asm.js format. In short: near native speeds, using C and C++, inside of the browser. Even better, emscripten converts OpenGL, a desktop graphics API, into WebGL, which is the web variant of that API.
How is this Code Turned into WebGL?
Unity recently did some performance benchmarks of their software running on WebGL as well.
Having code cross-compiled (often called transpiling as well) is not without its pitfalls, however. Common performance-enhancing techniques found in statically typed languages, such as multi-threading (JS is single threaded) and SIMD (Single Instruction Multiple Data) are not available yet.
Mozilla, along with several other leading tech companies, have been working on SIMD.js though, so increased performance and reduced power usage could be something of the near future. Read more about it here. Instead of those performance tweaks above, Unity is relying on their new scripting runtime, IL2CPP (In Line 2 C++). That’s a story for a different post, but Unity has been doing a fantastic web series every few weeks to illustrate how IL2CPP works.
IL2CPP has two distinct parts:
- An ahead-of-time (AOT) compiler
- A runtime library to support the virtual machine (VM)
The Intermediate Language (IL) from .NET compilers is compiled to C++ source code via the AOT compiler. Services and abstractions such as platform-independent access to threads and files as well as a garbage collector are some of the benefits the runtime library provides.
Look at it this way:
When you run an .exe on a Windows machine, it is not actually byte code (0s and 1s) at that point. It is still in a binary that the VM reads at runtime, which is then turned into bytecode. The DotNet CLI on a Windows machine is an example of a Virtual Machine which may read this binary. The CPU cannot comprehend anything other than binary, therefore this additional step is needed to take place.
Still confused? This post explains more about the difference between machine code, byte code, and VMs.
How Does asm.js Fit into the Picture?
Asm.js, short for Assembly JavaScript, is a subset of JavaScript. An asm.js program will behave identically whether it is run in an existing JavaScript engine or an ahead-of-time (AOT) compiling engine that recognizes and optimizes asm.js—except for speed, of course!
In terms of speed, it’s difficult to offer a precise measurement of how it compares to native code, but preliminary benchmarks of C programs compiled to asm.js are usually within a factor of 2 slowdown over native compilation with clang, the compiler frontend for C, C++, and Obj-C programming languages. It’s important to note that this is a “best” case for single-threaded programs. More on this limitation of the JavaScript language below.
On the backend, Clang uses LLVM, which is a library for constructing, optimizing and producing intermediate and/or binary machine code (those 0s and 1s again). LLVM can be used as a compiler framework, where you provide the “front end” (parser and lexer such as Clang) and the “back end” (code that converts LLVM representation to actual machine code)
Further reading: Alon Zakai of Mozilla has a fantastic slide deck which goes into further detail about how this all works.
So how cool is asm.js? Well it has its own Twitter account, @asmjs. While the asm site is a bit sparse, it does cover the W3C spec, in addition to having a thorough FAQ. Even better, Mozilla coordinated the Humble Mozilla Bundle in 2014, which allowed you to buy a bunch of gamest that took advantage of asm.js.
Why Not Just Turn Your JavaScript Code into asm.js?
JavaScript can’t really be compiled to asm.js and offer much of a benefit, because of its dynamic nature. It’s the same problem as when trying to compile it to C or even to native code – a VM with it would be necessary to take care of those non-static aspects. You could write asm.js by hand, however.
If one could already translate standard JavaScript in a fully static manner, there would there be a need for asm.js. Asm.js exists so for the promise that JavaScript will get faster without any effort from the developer. It would be very difficult for the JIT to understand a dynamic language as well as a static compiler.
To better understand this, it is important to comprehend why asm.js offers a performance benefit at all; or why statically-typed languages perform better than dynamically-typed ones. One reason is “run-time type checking takes time,” and a more thought out answer would include the enhanced feasibility of optimizing statically-typed code. A final perk of going from a statically typed language such as C is the fact that the compiler knows the type of each object when it is being compiled.
Asm.js is a restricted subset of JS that can be easily translated to bytecode. The first step required would need to break down all the advanced features of JS to that subset for getting this advantage, which is a bit complicated. But JavaScript engines are optimized and designed to translate all those advanced features directly into bytecode – so an intermediate step like asm.js doesn’t offer much of an advantage.
What is WebGL Doing?
WebGL (Web Graphics Library) is a JavaScript API for rendering interactive 3D computer graphics and 2D graphics within any compatible web browser without the use of plug-ins. WebGL has three distinct advantages:
-
Tasks : Drawing reflective materials or complex lighting generate a ton of overhead, and seeing that JavaScript is single threaded and CPU bound, why not offload some of that to the GPU in your device and have that do the heavy lifting?
-
Performance : Utilizing hardware acceleration (the GPU built into your device), WebGL is a great fit for games or complex visualizations.
-
Shaders : * * Complex visual effects can be produced with small programs known as “shaders”. This may be as simple as producing a sepia coloring effect, or more complex simulations such as water or flames. Visit Shadertoy for a showcase of some examples which really highlight this.
When you build a WebGL project, Unity will create a folder with the following files:
- an index.html file that embeds your content in a web page.
- a JavaScript file containing the code for your player.
- a .mem file containing a binary image to initialize the heap memory for your player.
- a .data file containing the asset data and scenes.
- some supporting JavaScript files to initialize and load the player.
You can also customize the style of the page to better suite your game, although taking advantage of the Fullscreen API is recommended to get a more immersive experience.
What’s Missing from WebGL?
WebGL is a subset of the OpenGL ES spec. This is the graphics API you frequently see on mobile devices, such as Android and iOS devices. That ES (Embedded Systems) spec is actually a subset of OpenGL, the graphics API available to desktop machines and consoles, such as PlayStation and Wii. Because WebGL is not a direct 1-to-1 match with OpenGL, some features will be missing.
Here’s a short list of missing features not currently available to WebGL versions of Unity games. Expect this to change over time.
- Runtime generation of Substance textures
- MovieTextures
- Networking other than WWW class (a WebSockets plug-in is available)
- Support for WebCam and Microphone access
- Hardware cursor support
- Most of the non-basic audio features
- Script debugging
- Threads
- Any .NET features requiring dynamic code generation
What About Browser Support?
This is where things get crazy. You can try out two of their WebGL demos right here. You need to use a browser which supports asm.js. As of this writing (July 2015), asm.js support is as follows:
- Firefox
- Chrome
- Edge
- Safari
It’s important to note that the asm.js spec is not implemented 100% in all of the browsers though, so results will vary. Unfortunately, asm.js is not listed on the popular feature-checking site CanIUse.com, so it’s difficult to get a clear understanding of how well it is supported in each browser. This will not work on mobile browsers as they currently do not support asm.js, although several support WebGL. I wrote a piece about the current state of WebGL on mobile devices in November of 2014.
What About Performance?
You can try Unity’s benchmark suite in your browser to see how well their C# to JavaScript conversion performs. This covers everything from the Mandelbrot GPU test to 2D physics and particles. This post from Unity in October 2014, before Microsoft’s Edge browser was announced, also highlights some interesting finds:
-
In almost all benchmarks, Firefox with asm.js is faster than both Chrome and Safari, and is currently the best browser to run Unity WebGL content.
-
When you are mostly GPU-bound, you can expect WebGL to perform very similar to native code.
-
In some areas, WebGL will actually outperform native code significantly. This is the case for tests which rely a lot on script performance (Mandelbrot and CryptoHash, which both implement their algorithms in C#), as IL2Cpp can produce more optimized code ( More info in this post).
-
Native code can still be several times faster than WebGL for areas heavily optimized to use multi-threading and/or SIMD, such as the 3D physics tests (PhysX 3.3 in Unity 5.0 is now fully multi-threaded). Compared to that, 2D physics is very close to parity when comparing Firefox to Native (Box2D is not multi-threaded). We hope that the future will bring SIMD and multi-threading extensions to JavaScript, at which point this may change.
So, what does this mean for you, as the developer? Naturally there are some areas where WebGL is significantly slower than native code, such as areas which make use of multi-threading, but WebGL performance is decent and only improving by the day. In fact, the W3C, the international community which works to develop web standards, just announced WebAssembly, a new intermediate representation for safe code on the web. “Wasm” for short, is a new binary syntax for low-level safe code, which will run alongside asm.js for the time being. This will further improve the performance of JavaScript applications running within the browser.
Alon Zakai wrote a post in March outlining how the speed of asm and JavaScript overall has been improving in the major browsers over the last year.
Why Would Anyone Want to Do This?
This is a pretty common question. The one I hear most though is_ “who would want to download a 60mb website?_ And you’re right — 60mb for a website is massive! But I suppose the same people who are downloading gigabytes worth of video content from YouTube and Netflix each day are those same people. If you look at it as a website, then sure, it’s huge. If you look at it as a game though, 60mb is small! The case study below illustrates that well.
Furthermore, as a developer you can now circumvent the restrictions of the app store. Want to update your application? No problem, push a new build to your website. Don’t want to wait out the one-week approval process of the iOS App Store (that is, IF it gets approved)? Perfect, push to your own site.
You could, of course, always sell it behind a paywall, too; or require some sort of authorization to play the title. Additionally, you do not need to pay the annual licensing fees for the app stores, nor do you need to prepare images for their stores, icons for their desktops, etc. Now things are starting to look appealing, and we’ve only just touched the surface.
Take a look at what the team at Illyriad games has done with their space combat title, Age of Ascent. They initially stream only a small bit of data, just enough to get you going, before sending the additional bits to you. I believe during our last conversation it started at 50mb. You can hear more about it during the podcast I did with them. Users can hop in and play immediately. No massive client to download, and instantly lowers the barrier to entry. Teenage-me would have loved this during my Everquest years.
In contrast, traditional titles send everything to you at once before you can start playing. Granted, consoles have just started to use “chunking”, which breaks up a title into smaller pieces and starts the download with the parts you need to play almost immediately.
Jonas Echterhoff pointed out that in Unity, streaming assets is already possibly by using AssetBundles. Alternatively, you can try this Asset Store package, which repackages the WebGL build data, so that scenes in your build are split across multiple files, and your content can start once the first scene is loaded:
https://www.assetstore.unity3d.com/en/#!/content/38368
In keeping with the space sim theme, I looked at Star Citizen, which is looking to come in at around 100gb. Do you honestly believe that you will ever experience all 100gb of that content?
An Offline Experience
Just because you are pointing to an HTML5 site doesn’t mean you can’t have an offline experience. Don’t believe me? Take a look at this video from Syd Lawrence one of the creators of Snowbuddy. Syd runs We Make Awesome Sh and during his talk he showcases some incredible apps made with PhoneGap and his top 7 tips for making high performance PhoneGap apps at PhoneGap Day EU 2015, but specifically highlights how to create an offline experience.
It would be wise to have a mix of local and remote content for your app. Technologies like IndexedDB, localStorage and AppCache, which are supported by Microsoft Edge, allow for this. A local page can be kept in your app package that can still provide a basic offline experience.
Case Study: Owlchemy Labs’ Aaaaa! Converted to asm.js from Unity
The team at Owlchemy labs converted their game Aaaaa! In 2014 and put together a great postmortem about the process. Using the pre-alpha version of the WebGL exporter in June of last year, they were able to take all of their C# code and turn it into one JavaScript file which was more than 1 million lines of code long!
Aaaaa! Has over 200 levels with more than 300 assets that can be spawned at runtime, in addition to 38 full-length songs. On PC/mac they were looking at a 388mb uncompressed file, so as you can imagine, having to download all of this each time someone wanted to play the game would be a bit of trouble.
One of their biggest size savers was Unity’s AudioClip streaming solution, which could stream music at runtime on demand. When completed, their final compressed WebGL build size, which includes all loaded assets as well as the Unity engine itself, ended up wat 68.8 MB. The compressed standalone PC build was almost 3x that size, at 192 MB.
There were certainly some UX changes that needed to be made, including rebinding the escape key, which in many games would display a pause menu, but inside the browser would exit full screen mode release mouse lock. Additionally, because browser’s security model forces these games to be sandboxed, saving large chunks of data to disk or loading custom audio from a user’s hard drive could prove problematic.
Finally, it would be important to consider some sort of cloud sync feature, as gamers often will not play web based games on only one machine. It would be far more convenient for a consumer to load into their profile from any machine and have their settings / saves just appear.
Are Any Other Middleware Tools Taking Advantage of This?
Epic’s Unreal Engine 4 exports to WebGL and takes advantage of asm.js as well. You can find step-by-step instructions here.Their process is nearly identical to that of Unity’s minus that first C# step, as the code you write in Unreal Engine is already in C++.
Epic’s current showcase piece is Tappy Chicken, a Tappy Bird type game which is also available on iOS and Android. They first illustrated UE 4 working within Firefox at GDC 2014:
At GDC the year before that, Mozilla and Epic surprised everyone at one of their talks when they revealed that UDK (Unreal Engine 3) was working within the browser after only one week of work with their Epic Citadel demo.
Monster Madness from NomNom Games, was the first commercial Unreal Engine 3 game published on the Web, using asm.js.
What Does the Future Hold?
WebGL isn’t limited to games. Application could easily take advantage of this technology as well.
Get a glimpse of the Unreal Paris 1.2 demo, where users can walk through a meticulously designed apartment. (Download link)
Imagine working as an architect or designer and trying to pitch this to a client. You can run it on the fly, inside of the browser. Even better, the user does not need to download the entire application.
Look at something like Age of Ascent. Download 54 Mb and you can start playing the app because it only streams what you need right then can cache it locally using a technology such as IndexedDB , so you do not need to download it again. Users can be up and running in seconds. I interviewed the team at Ilyriad games in December to get a better understanding of how they put their technology together.
On the other end of the Spectrum, we have Star Citizen, which starts at 70Gb.That’s a huge barrier to entry to many folks, especially in places where they may not have quick broadband.
For developers who perhaps do not want to use C or C++ to write your applications, you can still use a WebGL framework and write it all in JavaScript. BabylonJS is one example of this, which includes a physics engine for both 2D and 3D applications, as well as handJS which funnels all input types (click, touch, pen) into pointer events.
Are There Other Alternatives?
But of course! PlayCanvas is a fantastic WebGL based framework which uses asm.js for physics. Even better, it has excellent documentation and tutorials, on top of a browser-based editor.
For tools such as this, you’d need to use a wrapper of some sort (such as Cordova or PhoneGap) to port this to an app store such as Google Play, the App Store, or Windows Marketplace. ManifoldJS.
ManifoldJS aims to make the life of a mobile developer easier than ever, by utilizing Web App Manifests, which allowing websites to declare app-like properties. ManifoldJS uses that standard for those platforms that support it, but falls back to Cordova for those who don’t. Cordova is great, but the W3C also considers work done at Mozilla (Firefox Open Web Apps), Google (Chrome Hosted Apps) and Microsoft has done (Windows 8 had local web apps, Windows 10 extends over to hosted web apps). With this, we can now wrap websites and creating hybrid applications which can be deployed in the various app stores, while still taking advantage of many native aspects for each device (contacts, calendar, file storage, gyro, GPS, etc.)
Jeff Burtoft explains hosted web apps very well at http://www.thishereweb.com/hosted-web-apps-explained/
When we combine the two, we can create applications at native speed which can be deployed in a number of app stores, and utilizing largely one code base. There is no silver bullet for mobile development, but this certain makes the process easier.
Conclusion
Unity outlines the process of exporting your title to their WebGL player in their docs. Browser support for both WebGL and asm.js is constantly improving, and Firefox was even showcasing features of WebGL 2.0 at the Game Developers Conference in San Francisco earlier this year. WebGL 2.0 comes with a number of improvements, including the ability to render up to 32 textures at one time, as opposed to the current standard of 8, in addition to developer access to antialiasing and multiple render targets.
The advantages of porting a Unity game to WebGL are numerous:
- Distribution through sources other than curated app stores
- Often a smaller package size
- Easily demo or share projects
Developers have already proven that the model works, as illustrated by the_ Aaaaa!_ Case study and Mozilla Humble Bundle, so it’s an opportune time to take advantage of what the browser can finally offer and have additional exposure for your work.
More Hands-on with JavaScript
This article is part of the web development series from Microsoft tech evangelists on practical JavaScript learning, open source projects, and interoperability best practices including Microsoft Edge browser and the new EdgeHTML rendering engine.
We encourage you to test across browsers and devices including Microsoft Edge – the default browser for Windows 10 – with free tools on dev.modern.IE:
- Scan your site for out-of-date libraries, layout issues, and accessibility
- Use virtual machines for Mac, Linux, and Windows
- Remotely test for Microsoft Edge on your own device
- Coding Lab on GitHub: Cross-browser testing and best practices
In-depth tech learning on Microsoft Edge and the Web Platform from our engineers and evangelists:
- Microsoft Edge Web Summit 2015 (what to expect with the new browser, new supported web platform standards, and guest speakers from the JavaScript community)
- Woah, I can test Edge & IE on a Mac & Linux! (from Rey Bango)
- Advancing JavaScript without Breaking the Web (from Christian Heilmann)
- The Edge Rendering Engine that makes the Web just work (from Jacob Rossi)
- Unleash 3D rendering with WebGL (from David Catuhe including the vorlon.JS and babylonJS projects)
- Hosted web apps and web platform innovations (from Kevin Hill and Kiril Seksenov including the manifold.JS project)
More free cross-platform tools and resources for the Web Platform: