Skip to main content
October 14, 2021

Master Blazor WebAssembly on .Net6

Did you know that it is possible to create “vanilla” WebAssembly projects in .Net6? Yes, it is possible! Although it is not officially supported, the new Blazor WebAssembly SDK is flexible enough to allow you to build pure wasm projects.

This is extremely useful in many scenarios where you just want to build a web client application but leverage all the power of WebAssembly for high-demanding algorithms using .Net. Here at the Plain Concepts Research Team, we have developed a set of samples that illustrates multiple “hidden” functionalities that you can do with WebAssembly on .Net6, to cover the most common use cases for high-performance applications. We can even use the debugger in Visual Studio 2022!

Moreover, we added some missing functionality to easily interact with Javascript, to have the possibility to use the Virtual File System (calling System.File in .Net), and Brotli/GZip compression for production environments. Keep reading to master Web Assembly on .Net6.

What is Blazor WebAssembly?

Blazor is a framework developed by Microsoft designed to bring C# to the browser, in addition to allowing the creation of SPA applications. It is similar to other Javascript frameworks such as Angular or React, but with .Net code. To work, it uses WebAssembly to generate fluid pages that do not require reloading all the content, much faster, because the code is executed directly in the user’s browser. This means that no external server is needed to operate, so you end up saving money.

In other words, WebAssembly is a technology that allows binary code to be executed in the browser, providing superior performance to Javascript.

Vanilla wasm project

First of all, for every web assembly project, you will need a csproj that uses .net6 framework, the Microsoft.NET.Sdk.BlazorWebAssembly SDK to enable the wasm targets, and the references to the WebAssembly and DevServer nugets. Additionally, we need to add a Program.cs static class with a Main method that creates the host builder, and a wwwroot folder with an index.html file that calls blazor.webassembly.js entrypoint. Finally, to print something to the console, you could just call Console.WriteLine in the main method. To enable the debugger, a lauchSettings.json with “inspectUri” property needs to be in the properties folder.

vanilla wasm project

If it feels a little messy don’t worry, you will understand it much better looking at the console sample. You can just use this sample as your default WebAssembly template. Another easy way would be just to create a Blazor WebAssembly App from Visual Studio 2022, and then delete all razor files and remove from Program.cs the lines that add RootComponents and scoped services.

Once that everything is in place, put a breakpoint on Program.cs and run the debugger using the name of the project profile, or the IIS Express profile (we recommend the first as it seems to be faster). Your web app will stop in the breakpoint at the .Net6/WebAssembly code!

net6 webassembly code

Javascript <-> .Net interaction

Back and forth interaction between Javascript and .Net is possible using the JSRuntime exposed by the blazor host. However, out of the box you can only invoke global javascript functions from .Net. Therefore, we built a JSRuntime wrapper (look at the jsinteraction sample) that exposes an API like the one that was previously available in mono. It enables not only invoke js functions but also manipulate “JSObjects” like getting/setting properties or adding event listeners that call back to .Net.

javascript net interaction

Although this flexibility is great, it comes with a price: It is rather slow. Check the next section to know more.

Fast Javascript interaction: Linking to a native library, and callback

The functionality available in the above samples is great but it is not enough to build high-performance applications. For example, when you add an event listener which callback needs to access js properties and methods, multiple calls between js and .net are performed every time it is triggered. This is especially harmful on events that trigger often, like “mousemove”.

To solve this, like many other problems like interacting with native browser libraries like WebGL, OpenAL or WebXR, the best option here is to create a custom C++ or Rust library and then call them using P/Invoke.

Check the console-native example to know how to compile a C++ library using emscripten, that also makes a callback to .Net very fast.

console wasm sample

This is the most optimum way to work with the browser, but its architecture is a bit more complex. We recommend keeping the JSRuntime wrapper for no real-time algorithms, like initializations or sporadic events, and leave the native libraries approach for the rest, like operations that occur inside a draw loop.

Use the Virtual File System

Blazor SDK doesn’t support (at least for now) working with the virtual file system. That means loading web assets to a Javascript virtual file system, which then can be accessed from .Net just using System.File API, as if you were in a real OS. This has many advantages, like reusing .Net code or libraries that were originally built to work for Windows or any other OS.

Fortunately, we have implemented a VFS system that instructs your web assembly application to load the files described at your csproj into the virtual file system, to be used later by your app seamlessly. Check it out the source code at the filesystem sample.

console wasm sample

Production: Brotli/Gzip compression

Load times are very important in every app. As you probably know, in web apps file size is critical to load time. The Blazor WebAssembly SDK targets already allow us to compress our web assets using GZip or Brotli, but it doesn’t provide an out-of-the-box solution to serve those files when the client asks for them.

Fortunately, we found a simple solution: Create an “empty” ASP.net server and configure it to serve compressed files automatically when a compressed version of a file exists in the server. Check the full source code at the filesystem-server sample.

Final notes

We hope you find these samples useful and helps you boost your wasm apps. All this work is framed in the efforts made to bring WaveEngine to .Net6 in web. With these building blocks, we were able to render with WebGL at a great performance, as well as other technologies like OpenAL or WebXR. The samples repositories already have a WebXR sample that we are using as a sandbox for testing the technology, and maybe soon we will add WebGL and OpenAL ones in a new article. Keep posted to know more!

javier carnero
Author
Javier Carnero
Plain Concepts Research

We'll contact you!