Elena Canorea
Communications Lead
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.
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 compile the .Net code and generate smooth pages that do not require reloading all the content. As with any other client web application, this means that no external server is needed to run, so you save money.
In other words, WebAssembly is a technology that allows binary code to be executed in the browser, providing superior performance to Javascript.
As Microsoft explains, with Blazor WebAssembly, the client application and its dependencies are compiled into binary code and downloaded in parallel to the web browser. When a user accesses the application, it runs very quickly in the browser the end user uses, because, thanks to WebAssembly, the code is highly optimized.
Microsoft describes on its website how to secure a standalone Blazor WebAssembly application using the Blazor WebAssembly authentication library.
Thus, when an application is designed for individual user accounts, it instantly receives a reference from Microsoft.AspNetCore.Components.WebAssembly.Authentication package. With this, users can be verified, and tokens can be obtained.
In Blazor, a component is a tool used to create what the end user sees. For example, in a web page, it would be the home page, another one of its URLs, one of the drop-down elements…
Razor components can be used in Blazor applications, automatically making them Blazor components. Razor is a .NET based code syntax for creating web pages and uses extensions such as .cshtml or .razor.
Blazor, React, and Angular are all open-source frameworks. Blazor uses C#, while React and Angular use TypeScript. The former is from Microsoft, while React was created by Facebook and Angular was created by Google.
Using one or the other depends on the developer’s needs: the programming language he prefers, the developer community behind it (React and Angular are older than Blazor)… But there is no doubt that with Blazor, it is very easy to use all the power of WebAssembly to create web applications that take advantage of all the system’s resources.
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.
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!
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.
Although this flexibility is great, it comes with a price: It is rather slow. Check the next section to know more.
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.
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.
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.
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.
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!
Elena Canorea
Communications Lead
Cookie | Duration | Description |
---|---|---|
__cfduid | 1 year | The cookie is used by cdn services like CloudFare to identify individual clients behind a shared IP address and apply security settings on a per-client basis. It does not correspond to any user ID in the web application and does not store any personally identifiable information. |
__cfduid | 29 days 23 hours 59 minutes | The cookie is used by cdn services like CloudFare to identify individual clients behind a shared IP address and apply security settings on a per-client basis. It does not correspond to any user ID in the web application and does not store any personally identifiable information. |
__cfduid | 1 year | The cookie is used by cdn services like CloudFare to identify individual clients behind a shared IP address and apply security settings on a per-client basis. It does not correspond to any user ID in the web application and does not store any personally identifiable information. |
__cfduid | 29 days 23 hours 59 minutes | The cookie is used by cdn services like CloudFare to identify individual clients behind a shared IP address and apply security settings on a per-client basis. It does not correspond to any user ID in the web application and does not store any personally identifiable information. |
_ga | 1 year | This cookie is installed by Google Analytics. The cookie is used to calculate visitor, session, campaign data and keep track of site usage for the site's analytics report. The cookies store information anonymously and assign a randomly generated number to identify unique visitors. |
_ga | 1 year | This cookie is installed by Google Analytics. The cookie is used to calculate visitor, session, campaign data and keep track of site usage for the site's analytics report. The cookies store information anonymously and assign a randomly generated number to identify unique visitors. |
_ga | 1 year | This cookie is installed by Google Analytics. The cookie is used to calculate visitor, session, campaign data and keep track of site usage for the site's analytics report. The cookies store information anonymously and assign a randomly generated number to identify unique visitors. |
_ga | 1 year | This cookie is installed by Google Analytics. The cookie is used to calculate visitor, session, campaign data and keep track of site usage for the site's analytics report. The cookies store information anonymously and assign a randomly generated number to identify unique visitors. |
_gat_UA-326213-2 | 1 year | No description |
_gat_UA-326213-2 | 1 year | No description |
_gat_UA-326213-2 | 1 year | No description |
_gat_UA-326213-2 | 1 year | No description |
_gid | 1 year | This cookie is installed by Google Analytics. The cookie is used to store information of how visitors use a website and helps in creating an analytics report of how the wbsite is doing. The data collected including the number visitors, the source where they have come from, and the pages viisted in an anonymous form. |
_gid | 1 year | This cookie is installed by Google Analytics. The cookie is used to store information of how visitors use a website and helps in creating an analytics report of how the wbsite is doing. The data collected including the number visitors, the source where they have come from, and the pages viisted in an anonymous form. |
_gid | 1 year | This cookie is installed by Google Analytics. The cookie is used to store information of how visitors use a website and helps in creating an analytics report of how the wbsite is doing. The data collected including the number visitors, the source where they have come from, and the pages viisted in an anonymous form. |
_gid | 1 year | This cookie is installed by Google Analytics. The cookie is used to store information of how visitors use a website and helps in creating an analytics report of how the wbsite is doing. The data collected including the number visitors, the source where they have come from, and the pages viisted in an anonymous form. |
attributionCookie | session | No description |
cookielawinfo-checkbox-analytics | 1 year | Set by the GDPR Cookie Consent plugin, this cookie is used to record the user consent for the cookies in the "Analytics" category . |
cookielawinfo-checkbox-necessary | 1 year | This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary". |
cookielawinfo-checkbox-necessary | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary". |
cookielawinfo-checkbox-necessary | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary". |
cookielawinfo-checkbox-necessary | 1 year | This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary". |
cookielawinfo-checkbox-non-necessary | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Non Necessary". |
cookielawinfo-checkbox-non-necessary | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Non Necessary". |
cookielawinfo-checkbox-non-necessary | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Non Necessary". |
cookielawinfo-checkbox-non-necessary | 1 year | This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Non Necessary". |
cookielawinfo-checkbox-performance | 1 year | Set by the GDPR Cookie Consent plugin, this cookie is used to store the user consent for cookies in the category "Performance". |
cppro-ft | 1 year | No description |
cppro-ft | 7 years 1 months 12 days 23 hours 59 minutes | No description |
cppro-ft | 7 years 1 months 12 days 23 hours 59 minutes | No description |
cppro-ft | 1 year | No description |
cppro-ft-style | 1 year | No description |
cppro-ft-style | 1 year | No description |
cppro-ft-style | session | No description |
cppro-ft-style | session | No description |
cppro-ft-style-temp | 23 hours 59 minutes | No description |
cppro-ft-style-temp | 23 hours 59 minutes | No description |
cppro-ft-style-temp | 23 hours 59 minutes | No description |
cppro-ft-style-temp | 1 year | No description |
i18n | 10 years | No description available. |
IE-jwt | 62 years 6 months 9 days 9 hours | No description |
IE-LANG_CODE | 62 years 6 months 9 days 9 hours | No description |
IE-set_country | 62 years 6 months 9 days 9 hours | No description |
JSESSIONID | session | The JSESSIONID cookie is used by New Relic to store a session identifier so that New Relic can monitor session counts for an application. |
viewed_cookie_policy | 11 months | The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data. |
viewed_cookie_policy | 1 year | The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data. |
viewed_cookie_policy | 1 year | The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data. |
viewed_cookie_policy | 11 months | The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data. |
VISITOR_INFO1_LIVE | 5 months 27 days | A cookie set by YouTube to measure bandwidth that determines whether the user gets the new or old player interface. |
wmc | 9 years 11 months 30 days 11 hours 59 minutes | No description |
Cookie | Duration | Description |
---|---|---|
__cf_bm | 30 minutes | This cookie, set by Cloudflare, is used to support Cloudflare Bot Management. |
sp_landing | 1 day | The sp_landing is set by Spotify to implement audio content from Spotify on the website and also registers information on user interaction related to the audio content. |
sp_t | 1 year | The sp_t cookie is set by Spotify to implement audio content from Spotify on the website and also registers information on user interaction related to the audio content. |
Cookie | Duration | Description |
---|---|---|
_hjAbsoluteSessionInProgress | 1 year | No description |
_hjAbsoluteSessionInProgress | 1 year | No description |
_hjAbsoluteSessionInProgress | 1 year | No description |
_hjAbsoluteSessionInProgress | 1 year | No description |
_hjFirstSeen | 29 minutes | No description |
_hjFirstSeen | 29 minutes | No description |
_hjFirstSeen | 29 minutes | No description |
_hjFirstSeen | 1 year | No description |
_hjid | 11 months 29 days 23 hours 59 minutes | This cookie is set by Hotjar. This cookie is set when the customer first lands on a page with the Hotjar script. It is used to persist the random user ID, unique to that site on the browser. This ensures that behavior in subsequent visits to the same site will be attributed to the same user ID. |
_hjid | 11 months 29 days 23 hours 59 minutes | This cookie is set by Hotjar. This cookie is set when the customer first lands on a page with the Hotjar script. It is used to persist the random user ID, unique to that site on the browser. This ensures that behavior in subsequent visits to the same site will be attributed to the same user ID. |
_hjid | 1 year | This cookie is set by Hotjar. This cookie is set when the customer first lands on a page with the Hotjar script. It is used to persist the random user ID, unique to that site on the browser. This ensures that behavior in subsequent visits to the same site will be attributed to the same user ID. |
_hjid | 1 year | This cookie is set by Hotjar. This cookie is set when the customer first lands on a page with the Hotjar script. It is used to persist the random user ID, unique to that site on the browser. This ensures that behavior in subsequent visits to the same site will be attributed to the same user ID. |
_hjIncludedInPageviewSample | 1 year | No description |
_hjIncludedInPageviewSample | 1 year | No description |
_hjIncludedInPageviewSample | 1 year | No description |
_hjIncludedInPageviewSample | 1 year | No description |
_hjSession_1776154 | session | No description |
_hjSessionUser_1776154 | session | No description |
_hjTLDTest | 1 year | No description |
_hjTLDTest | 1 year | No description |
_hjTLDTest | session | No description |
_hjTLDTest | session | No description |
_lfa_test_cookie_stored | past | No description |
Cookie | Duration | Description |
---|---|---|
loglevel | never | No description available. |
prism_90878714 | 1 month | No description |
redirectFacebook | 2 minutes | No description |
YSC | session | YSC cookie is set by Youtube and is used to track the views of embedded videos on Youtube pages. |
yt-remote-connected-devices | never | YouTube sets this cookie to store the video preferences of the user using embedded YouTube video. |
yt-remote-device-id | never | YouTube sets this cookie to store the video preferences of the user using embedded YouTube video. |
yt.innertube::nextId | never | This cookie, set by YouTube, registers a unique ID to store data on what videos from YouTube the user has seen. |
yt.innertube::requests | never | This cookie, set by YouTube, registers a unique ID to store data on what videos from YouTube the user has seen. |