Developers are closer than ever to acquiring what is widely considered the “Holy Grail” of software: the ability to write code once and have it run anywhere. WebAssembly is a standard that defines a cross-platform binary format that executes in web pages. The binary code can be represented in a text format that looks strikingly like assembly code. The standard paves the way for optimizations that enable JavaScript engines (including browser engines and Node.js) to run code at near native speeds. It is a supported compile target for dozens of languages including C, C++, C#, Go, and Rust.

March 12th, 2019 marked the 30-year anniversary of the World Wide Web. Invented by Tim Berners-Lee in 1989, its original purpose and use was to interlink documents using revolutionary hyperlinks that enable navigation between resources and embedding of assets such as images and videos. The combination of commercial use and the invention of JavaScript by Brendan Eich in 1995 led to an evolution from simple web pages to rich, interactive web applications. Today, many websites consist mostly of JavaScript code that fetches data over HTTP APIs and dynamically renders data to create a full-featured program that runs in the browsers as a “single page application” or SPA.

JavaScript is a dynamic, weakly-typed language that is interpreted at runtime. Despite decades of optimizations to improve performance that have led to widespread use in both the browser and on the server, there is still a huge gap in capabilities compared to natively developed code. This began to change in 2014 with the release of the asm.js standard. Asm.js prescribes a strict subset of JavaScript. It supports static types through JavaScript-compatible conventions. For example, the following code is perfectly valid JavaScript:

function addOne(i) {
   i = i|0;
   return (i+1)|0;
}

In JavaScript, the |0 operation removes any fraction from the number passed in. JavaScript treats numbers as double precision floating point values stored in 64 bits. However, modern JavaScript engines that support asm.js recognize |0 as an integer type, store the values as 32-bit integers and perform integer addition that is significantly faster than floating point addition. The strict subset and conventions enforced by asm.js enables the generation of native code that runs faster and more efficiently than legacy JavaScript. A popular software development kit named emscripten can compile C and C++ to asm.js. In addition to supporting performance improvements, this also enables porting legacy code to run in the browser.  

Asm.js was very successful. It enabled teams to port massive codebases, including entire 3D gaming engines like Unreal, to the browser with satisfactory performance. In 2017, the process was taken a step further with the introduction of WebAssembly, or Wasm for short. WebAssembly defines what is conceptually a stack-based virtual machine that runs in the browser sandbox and exposes APIs to JavaScript. Given a C program like this:

int main(int i) {
       return i + 1;
}

WebAssembly generates a series of byte codes that can be represented with text like this:

(module
  (type $t0 (func))
  (type $t1 (func (param i32) (result i32)))
  (func $__wasm_call_ctors (type $t0))
  (func $main (export "main") (type $t1) (param $p0 i32) (result i32)
    get_local $p0
    i32.const 1
    i32.add)
  (table $T0 1 1 anyfunc)
  (memory $memory (export "memory") 2)
  (global $g0 (mut i32) (i32.const 66560))
  (global $__heap_base (export "__heap_base") i32 (i32.const 66560))
  (global $__data_end (export "__data_end") i32 (i32.const 1024)))

Wasm began as the fastest and most efficient way to compile C and C++ code to the web, but quickly evolved to much more. Languages like Go and Rust soon added Wasm support to their toolchains. More recently, Microsoft tackled an even loftier goal: porting the entire .NET runtime via an experimental project called Blazor.

The journey began in 2004 with the release of Mono, a cross-platform version of the .NET Framework. Mono was designed to run .NET assemblies on any platform. It is the basis for the mobile Xamarin platform that was acquired by Microsoft. In 2017, several teams and individuals explored the feasibility of targeting WebAssembly with Mono. They succeeded! The result is an implementation of the .NET runtime in Wasm. Unlike other solutions that target WebAssembly, the Mono approach allows existing .NET DLLs to run “as is” without re-targeting or recompilation. The Blazor team leveraged Mono to build a rendering engine, reusable component model, templates, routing, and other features that allow developers to build a fully functional web application with C# and .NET.

The advantages of leveraging existing code go beyond performance benefits. In a typical business application scenario, data is marshalled from the server to the client via APIs and then modified and sent back. Solutions like Blazor enable true code sharing: a single class library contains data definitions, business logic and validation code that runs on both the server and in the client browser “as is.” A single change is immediately available throughout the stack. The drawbacks include a larger file size (the full .NET libraries in the form of DLL files must be shipped to the browser) and performance (the .NET code is currently interpreted by the framework running in Wasm, as opposed to being parsed into pure byte code). Teams are working diligently to address all of these issues in the near future.

The tide has turned and companies are moving games, business applications, and even sophisticated algorithms for blockchain solutions into the browser with WebAssembly. As optimizations continue, the ecosystem of supported languages and tools grows, and the specification matures, developers will find themselves in a unique era. Customers want their apps to run anytime, anyplace, on every device. With Wasm, the dream of writing code once that runs anywhere may finally be realized. Maybe you can have your cake and eat it too!