Using a web camera with fun filters in your ASP.NET Core Blazor WebAssembly application

Spread the love

There are many things that you can do with Web Camera in your Web Application. For example, you can make a portrait picture and send it to the ML service to identify your age. Or you can take a picture of the sky and try to identify whether outside. Or you can apply some photo filters and get some fun :).

And this time I’ve decided to create small Blazor WebAssembly application which can use the web camera of your PC or laptop and which can apply some funny filters to the real-time picture.

But what options do we have with Blazor apps to use the Web camera? A quick investigation gives us quite bad news. Only JavaScript again :(. So, let’s try.

Creating ASP.NET Core Blazor WebAssembly application project

It’s quite simple and I’m almost sure you know how to do this. Just move to required directory and put the next command in your Visual Studio Code Terminal Window

dotnet new blazorwasm -o BlazorWebCamApp

To be sure everything is OK, use the next commands in your command line

dotnet build
dotnet run

Project Clean Up

Just created ASP.Net Core 3 Blazor WebAssembly project has few unnecessary files created as an example. So, it’s time to clean up the project. The screenshot below shows files and directories to delete.

Than, change Index.razor file content to the next:

@page "/"

<h1>Fun Blazor Web Camera Application</h1>

And finally remove unnecessary links to deleted pages from NavMenu.razor

<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">Blazor WebCam Application</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home

@code {
    private bool collapseNavMenu = true;

    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
        collapseNavMenu = !collapseNavMenu;

After finishing cleaning up, the application should look like below

Adding JavaScript code to access the camera with WebRTC support

There are a lot of JavaScript examples of how to use the camera. Our code will be based on an example from Mozilla Developer Network which can be found in the article “Taking still photos with WebRTC“.

So, create the new JavaScript file (let’s call it webcam.js) in the ../wwwroot/js directory inside your project and put the next code inside.

let video = null;
let canvas = null;
let context = null;
let streaming = false;

let width = 100;    // We will scale the photo width to this.
let height = 0;     // This will be computed based on the input stream
let filter = 'sepia(1)';

function onStart(options) {
    video = document.getElementById(options.videoID);
    canvas = document.getElementById(options.canvasID);
    context = canvas.getContext('2d');
    width = options.width;
    filter = options.filter;

    navigator.mediaDevices.getUserMedia({ video: true, audio: false })
        .then(function (stream) {
            video.srcObject = stream;
        .catch(function (err) {
            console.log("An error occurred: " + err);

    video.addEventListener('canplay', function () {
        if (!streaming) {
            height = video.videoHeight / (video.videoWidth / width);

            if (isNaN(height)) {
                height = width / (4 / 3);

            video.setAttribute('width', width);
            video.setAttribute('height', height);
            canvas.setAttribute('width', width);
            canvas.setAttribute('height', height);
            streaming = true;
    }, false);

    video.addEventListener("play", function () {
    }, false);

function timercallback() {
    if (video.paused || video.ended) {
    setTimeout(function () {
    }, 0);

function computeFrame() {
    context.drawImage(video, 0, 0, width, height);
    context.filter = filter;

window.WebCamFunctions = {
    start: (options) => { onStart(options); } 

As you can see, there is one global object window.WebCamFunctions with only one property start with a function assigned to it. This function takes an options parameter which is an object received from our C# code. The options object should contain identifiers of the video and canvas HTML elements, their width, and filter string which is applied to the canvas context.

Do not forget to add link to the JavaScript file inside ../wwwroot/index.html.

<!DOCTYPE html>

    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>Blazor WebCamera Application</title>
    <base href="/" />
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet" />


    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    <script src="_framework/blazor.webassembly.js"></script>
    <script type="module" src="js/webcam.js"></script>


Adding Web camera options class

This class intended to be used to transfer options from C# to JavaScript code. Just create new WebCamOptions.cs file in your project and put the next code inside.

namespace BlazorWebCamApp
    public class WebCamOptions
        public int Width { get; set; } = 320;
        public string VideoID { get; set; }
        public string CanvasID { get; set; }
        public string Filter { get; set; } = null;

Looks pretty simple, like any model class, isn’t it?

Putting things together inside the main razor page

It’s not very difficult, indeed. See the code below

@page "/"
@inject IJSRuntime JSRuntime

<h1>Fun Blazor Web Camera Application</h1>

    <canvas id="@options.CanvasID" 
    <button @onclick="Start">Start</button>
    <video id="@options.VideoID"
           style="background-color:lightblue; visibility:hidden;"
           width="@options.Width">Video stream not available.


    WebCamOptions options = new WebCamOptions() 
            CanvasID = "canvas",
             VideoID = "video"

    protected override void OnInitialized()
        options.Filter = 
            "contrast(1.4) sepia(0.2) blur(3px) saturate(200%) hue-rotate(200deg)";
        options.Width = 480;

    public async Task Start()
        await JSRuntime.InvokeVoidAsync("WebCamFunctions.start", options);

As you can see code is very simple. We’ve added three HTML elements to markup, video, canvas, and button. The WebCamOptions object controls the canvas and video ids and width. Also, it contains the filters string. It’s not necessary, because as you remember, the size of these elements is controlled by JavaScript code. But if do not want the size changed on the button click, you have to do this in C# code.

And, of cause, the JavaScript code is called on button click.

So, that’s all. Is my face color the same as Avatar has? And it’s quite simple to change the color to Hulk or Peppa has. Just try to change canvas filters :-).

You can find the complete source code of this Blazor WebAssembly application in my GitHub repository.

When debugging with Visual Studio Code on MacOS with Chrome 85,
you may catch a crash after clicking "Allow" dialog to allow browser to use the camera.
This happens only in debugging mode and only on MacOS. 
So, just use dotnet run in this case for now.
Since I don't have Mac, it's hard to understand what's going on.
Thanks to Christian Weyer, who created the issue in the example repository. 

Roman Simuta Blog