Wave Engine runs on the Web thanks to Mono Wasm
What WebAssembly means for .NET
You can right now run your .NET Core Console app embedded in an HTML file, make web requests, access the File System (FS), draw stuff with WebGL (the Web branch for OpenGL), and a lot of more things, just because it is the .NET runtime being executed in Wasm. There are still steps to be taken in order to make things smoother for us the Developers but, at the same time, begins to be mature enough for using along with projects.
What Mono Wasm is & why Wave Engine needs it
With Wave Engine, our cross-platform 3D engine focused on industrial needs, we already tried to target the Web a few years ago, back with JSIL, but the state of art was simply not ready for us. We paused such an attempt until we could gain on performance and easiness to target the low-level drawing APIs.
Along with the development of Mono Wasm, and parties like Uno Platform using such as their underlying technology, we rethought our position and, after studying how WebGL could be achieved, we started enabling the Web as another platform we can, nowadays, target.
Your first app with Mono Wasm
There are currently different paths if you want to target Wasm in .NET:
- Uno’s Wasm bootstrap: which allows Uno Platform to run on top of Wasm, and is built also on top of Mono Wasm NuGets;
- Mono Wasm NuGets: downloadable through their Jenkins artifacts, are the barebones needed to enable the Wasm scenario in .NET
There are some other cool projects to turn Wasm modules into .NET assemblies —like Eric Sink’s wasm2cil—, for instance, which empower the community too.
Getting the toolchain
In order to push in the direction which would help Microsoft/Mono to build the below mentioned infrastructure, and enjoy the latest bits from the repo, you should:
- Navigate to the latest successful build and download the artifacts:https://jenkins.mono-project.com/job/test-mono-mainline-wasm/label=ubuntu-1804-amd64/lastSuccessfulBuild/Azure/
- Download the ZIP containing the NuGets:mono-wasm-*.zip
- Uncompress it and find the NuGets under packages/
Creating the project
We simply tell .NET compiler to target Wasm through the Project’s SDK attribute and, apart from this, there is no other fundamental difference with any other one:
<None Remove="index.html" />
Basic structure of a .csproj file targeting Wasm
In order to the compiler detect where Mono.WebAssembly.Sdk NuGet is you will need to supply a NuGet.config file, establishing where such is in the hard drive.
With the goal of simplifying all these steps, we have set up a repo that provides everything ready to start writing your code: WasmApp1; along with an article on how to serve the resulting HTML page locally. We thought of it as if you could create a new project within Visual Studio, just choosing a Wasm template.
Interop-ing with the DOM
In the same way, you update your UI controls from a Xamarin.Forms app, you make the same with the DOM in a web page: you can modify the web UI from .NET and, also, invoke .NET stuff from the UI —like through a button click, for example.
From .NET to the DOM
WasmApp1 template comes with an HTML button that fires some code on the .NET side. Such button is created dynamically when the app starts, and we subscribe to its onclick event just after:
using (var document = (JSObject)Runtime.GetGlobalObject("document"))
using (var body = (JSObject)document.GetObjectProperty("body"))
using (var button = (JSObject)document.Invoke("createElement", "button"))
button.SetObjectProperty("innerHTML", "Click me!");
Ideally, the community will provide NuGets so JSObject may not be visible at a higher level, as Kenneth Pouncey is already doing with his wasm-dom project —see the samples folder!
From the DOM to .NET
Following with WasmApp1, when a user clicks the button such happens on the DOM side, as the button triggers its onclick event. As the template showcases, such event is subscribed from .NET:
window = (JSObject)Runtime.GetGlobalObject())
window.Invoke("alert", "Hello, Wasm!");
The Program.cs portion where the button’s onclick subscription happens
BINDING.call_static_method("[WasmApp1] WasmApp1.Program:Main", );
Init code at index.html to execute our app’s entry point
In the syntax of type “[AssemblyName] FullQualifiedClassName:StaticMethodName” we can tell the .NET Runtime to immediately execute such method and wait for its return value. The second parameter provided is an array to pass arguments.
Wave Engine’s glTF viewer
A few weeks ago, we made public our first experiment with Wave Engine targeting the Web: a 3D models viewer.
Such supposed a milestone achievement for us, as demoed we are ready to create 3D experiences within the browser though for the industrial needs: CAD tools, 3D viewers, GPU-based computation, etc. Reaching Wasm with our C# existing codebase has meant a tremendous value for us and sure the same applies to other companies and individuals around the globe.
If you want to look in-depth at how we developed this solution please keep reading here.
Although most of the .NET Core 3 API is supported, we have stumbled upon specific scenarios which still have some caveats:
- You can consume System.IO namespace, but only through its sync API: it has to do with the underlying Emscripten FS virtualization, but would be great to have some documentation on what users can do nowadays, like best practices
- You cannot use multi-threading broadly: there is already support from Emscripten and .NET, but just some browsers support it, as Wasm it-self is maturing too at the same time
If we could write a love letter to .NET Wasm, it would be like this:
- A single toolchain to rule them all: currently, there is no chance to get .NET Wasm bits from nuget.org (the same package names are also taken by other users), which frustrates beginners from playing with it. Uno distributes them internally; we, Wave Engine, have a private feed with such uploaded
- Documentation: Blazor is doing a good job in this aspect, but is focused on the ASP.NET point of view from our perspective: we would really like plain, clean, documentation on the WebAssembly Runtime it-self, which will encourage users to think of the Web as a platform per se, from where initiatives like Wave Engine or Uno Platform emerge from the ground up.
As being developed in Open Source, you, or your company, can collaborate to make Wasm support better. All the work is made at GitHub under sdks/wasm path. You can start by cloning the repo into a Linux machine —or simply under Windows 10 with WSL installed.
Going through its root README.md, enables you with everything needed to build and run the test suite used. Simply reporting bugs —where we have found the WasmApp1 repo branches to be a quick path for such— leverage the need for even more attention in this promising, and quite beautiful, platform.