The personal website of Scott W Harden
May 29th, 2022

Show A Progress Bar as your Blazor App Loads

Today I created a Blazor WebAssembly app that shows a progress bar while the page loads. This is especially useful for users on slow connections because Blazor apps typically require several megabytes of DLL and DAT files to be downloaded before meaningful content appears on the page.

Live Demo: LJPcalc (source code)

Step 1: Add a progress bar

Edit index.html and identify your app's main div:

<div id="app">Loading...</div>

Add a progress bar inside it:

<div id="app">
    <h2>Loading...</h2>
    <div class="progress mt-2" style="height: 2em;">
        <div id="progressbar" class="progress-bar progress-bar-striped progress-bar-animated"
            style="width: 10%; background-color: #204066;"></div>
    </div>
    <div>
        <span id="progressLabel" class="text-muted">Downloading file list</span>
    </div>
</div>

See Bootstrap's progressbar page for extensive customization and animation options and best practices when working with progress indicators. Also ensure the version of Bootstrap in your Blazor app is consistent with the documentation/HTML you are referencing.

Step 2: Disable Blazor AutoStart

Edit index.html and identify where your app loads Blazor resources:

<script src="_framework/blazor.webassembly.js"></script>

Update that script so it does not download automatically:

<script src="_framework/blazor.webassembly.js" autostart="false"></script>

Step 3: Create a Blazor Startup Script

Add a script to the bottom of the page to start Blazor manually, identifying all the resources needed and incrementally downloading them while updating the progressbar along the way.

<script>
    function StartBlazor() {
        let loadedCount = 0;
        const resourcesToLoad = [];
        Blazor.start({
            loadBootResource:
                function (type, filename, defaultUri, integrity) {
                    if (type == "dotnetjs")
                        return defaultUri;

                    const fetchResources = fetch(defaultUri, {
                        cache: 'no-cache',
                        integrity: integrity,
                        headers: { 'MyCustomHeader': 'My custom value' }
                    });

                    resourcesToLoad.push(fetchResources);

                    fetchResources.then((r) => {
                        loadedCount += 1;
                        if (filename == "blazor.boot.json")
                            return;
                        const totalCount = resourcesToLoad.length;
                        const percentLoaded = 10 + parseInt((loadedCount * 90.0) / totalCount);
                        const progressbar = document.getElementById('progressbar');
                        progressbar.style.width = percentLoaded + '%';
                        const progressLabel = document.getElementById('progressLabel');
                        progressLabel.innerText = `Downloading ${loadedCount}/${totalCount}: ${filename}`;
                    });

                    return fetchResources;
                }
        });
    }

    StartBlazor();
</script>

Resources

Markdown source code last modified on May 29th, 2022
---
title: Show A Progress Bar as your Blazor App Loads
description: How to add a progress bar to your client-side Blazor WebAssembly app to indicate page load progress.
date: 2022-05-29 16:05:00 EST
tags: blazor, csharp
---

# Show A Progress Bar as your Blazor App Loads

**Today I created a Blazor WebAssembly app that shows a progress bar while the page loads.** This is especially useful for users on slow connections because Blazor apps typically require several megabytes of DLL and DAT files to be downloaded before meaningful content appears on the page.

Live Demo: [LJPcalc](https://swharden.com/LJPcalc/) ([source code](https://github.com/swharden/LJPcalc))

<img src="blazor-load-progress-v2.gif" class="mx-auto d-block border shadow my-5">

## Step 1: Add a progress bar

Edit index.html and identify your app's main div:

```html
<div id="app">Loading...</div>
```

Add a progress bar inside it:
```html
<div id="app">
	<h2>Loading...</h2>
	<div class="progress mt-2" style="height: 2em;">
		<div id="progressbar" class="progress-bar progress-bar-striped progress-bar-animated"
			style="width: 10%; background-color: #204066;"></div>
	</div>
	<div>
		<span id="progressLabel" class="text-muted">Downloading file list</span>
	</div>
</div>
```

See [Bootstrap's progressbar page](https://getbootstrap.com/docs/5.2/components/progress/) for extensive customization and animation options and best practices when working with progress indicators. Also ensure the version of Bootstrap in your Blazor app is consistent with the documentation/HTML you are referencing.

## Step 2: Disable Blazor AutoStart

Edit index.html and identify where your app loads Blazor resources:

```html
<script src="_framework/blazor.webassembly.js"></script>
```

Update that script so it does _not_ download automatically:
```html
<script src="_framework/blazor.webassembly.js" autostart="false"></script>
```

## Step 3: Create a Blazor Startup Script

Add a script to the bottom of the page to start Blazor manually, identifying all the resources needed and incrementally downloading them while updating the progressbar along the way.

```html
<script>
	function StartBlazor() {
		let loadedCount = 0;
		const resourcesToLoad = [];
		Blazor.start({
			loadBootResource:
				function (type, filename, defaultUri, integrity) {
					if (type == "dotnetjs")
						return defaultUri;

					const fetchResources = fetch(defaultUri, {
						cache: 'no-cache',
						integrity: integrity,
						headers: { 'MyCustomHeader': 'My custom value' }
					});

					resourcesToLoad.push(fetchResources);

					fetchResources.then((r) => {
						loadedCount += 1;
						if (filename == "blazor.boot.json")
							return;
						const totalCount = resourcesToLoad.length;
						const percentLoaded = 10 + parseInt((loadedCount * 90.0) / totalCount);
						const progressbar = document.getElementById('progressbar');
						progressbar.style.width = percentLoaded + '%';
						const progressLabel = document.getElementById('progressLabel');
						progressLabel.innerText = `Downloading ${loadedCount}/${totalCount}: ${filename}`;
					});

					return fetchResources;
				}
		});
	}

	StartBlazor();
</script>
```

## Resources

* [ASP.NET Core Blazor startup](https://docs.microsoft.com/en-us/aspnet/core/blazor/fundamentals/startup) describes the `Blazor.start()` process and `loadBootResource()`.

* Code here inspired by [@EdmondShtogu's comment](https://github.com/dotnet/aspnetcore/issues/25165#issuecomment-781683925) on [dotnet/aspnetcore #25165](https://github.com/dotnet/aspnetcore/issues/25165) from Feb 18, 2021
March 27th, 2021

WSPR Code Generator

WSPR (Weak Signal Propagation Reporter) is a protocol for weak-signal radio communication. Transmissions encode a station's callsign, location (grid square), and transmitter power into a frequency-shift-keyed (FSK) signal that hops between 4 frequencies to send 162 tones in just under two minutes. Signals can be decoded with S/N as low as −34 dB! WSJT-X, the most commonly-used decoding software, reports decoded signals to wsprnet.org so propagation can be assessed all over the world.

WsprSharp is a WSPR encoding library implemented in C#. I created this library learn more about the WSPR protocol. I also made a web application (using Blazor WebAssembly) to make it easy to generate WSPR transmissions online: WSPR Code Generator

WSPR Transmission Information

  • One packet is 110.6 sec continuous wave
  • Frequency shifts between 4 tones
  • Each tone keys for 0.683 sec
  • Tones are separated by 1.4648 Hz
  • Total bandwidth is about 6 Hz
  • 50 bits of information are packaged into a 162 bit message with FEC
  • Transmissions always begin 1 sec after even minutes (UTC)

Resources

Markdown source code last modified on September 12th, 2021
---
Title: WSPR Code Generator
Description: An online resource for generating encoded WSPR messages
Date: 2021-03-27 8PM EST
Tags: blazor, amateur radio, qrss
---

# WSPR Code Generator

**[WSPR](https://en.wikipedia.org/wiki/WSPR_(amateur_radio_software)) (Weak Signal Propagation Reporter) is a protocol for weak-signal radio communication.** Transmissions encode a station's callsign, location (grid square), and transmitter power into a frequency-shift-keyed (FSK) signal that hops between 4 frequencies to send 162 tones in just under two minutes. Signals can be decoded with S/N as low as −34 dB! [WSJT-X](https://physics.princeton.edu/pulsar/k1jt/wsjtx.html), the most commonly-used decoding software, reports decoded signals to [wsprnet.org](https://wsprnet.org/) so propagation can be assessed all over the world.

**[WsprSharp](https://github.com/swharden/WsprSharp) is a WSPR encoding library implemented in C#.** I created this library learn more about the WSPR protocol. I also made a web application (using Blazor WebAssembly) to make it easy to generate WSPR transmissions online: [WSPR Code Generator](https://swharden.com/software/wspr-code-generator)

[![](wspr-code-generator.png)](https://swharden.com/software/wspr-code-generator)


### WSPR Transmission Information

![](wspr-spectrogram.png)

* One packet is 110.6 sec continuous wave
* Frequency shifts between 4 tones 
* Each tone keys for 0.683 sec
* Tones are separated by 1.4648 Hz
* Total bandwidth is about 6 Hz
* 50 bits of information are packaged into a 162 bit message with [FEC](https://en.wikipedia.org/wiki/Error_correction_code)
* Transmissions always begin 1 sec after even minutes (UTC)

### Resources
* [WSPR Code Generator](https://swharden.com/software/wspr-code-generator) - encode WSPR messages from your browser
* [WsprSharp](https://github.com/swharden/WsprSharp) - a .NET library for encoding and decoding WSPR transmissions using C#
* [FSKview](https://swharden.com/software/FSKview/) - spectrogram for viewing frequency-shift keyed (FSK) signals in real time
* [Anatomy of a WSPR transmission](https://swharden.com/software/FSKview/wspr/)
* [WSPR](https://en.wikipedia.org/wiki/WSPR_(amateur_radio_software)) on Wikipedia
* [K1JT Program](http://physics.princeton.edu/pulsar/K1JT/devel.html)
* [Grid square lookup](http://www.levinecentral.com/ham/grid_square.php?Grid=FN20)
* [WSJT](https://sourceforge.net/projects/wsjt/) on SourceForge
* [WSPR Mode in WSJT-X](https://wsprnet.org/drupal/node/5563)
January 12th, 2021

Google Charts in Blazor

Google Charts is a free interactive JavaScript charting framework. The Google Chart Gallery showcases many of the available chart types and options. This project shows how to create data from C# functions in Blazor, then use JavaScript interop to pass arrays into JavaScript for display with Google Charts.

index.razor

This page contains a div where the chart will be displayed, controls for customizing settings, and a few functions in the code-behind to call a JavaScript function that updates the chart.

@page "/"
@inject IJSRuntime JsRuntime

<!-- this is where the Google Chart is displayed -->
<div id="chart_div" style="height: 400px;"></div>

<!-- buttons call C# functions -->
<button @onclick="PlotSin">Sin</button>
<button @onclick="PlotRandom">Random</button>
<button @onclick="PlotWalk">Walk</button>
<button @onclick="PlotRandomXY">RandomXY</button>

<!-- a slider bound to a C# field -->
<input type="range" @bind="PointCount" @bind:event="oninput">
@code{
    private int PointCount = 123;
    Random Rand = new Random();

    private void PlotData(double[] xs, double[] ys)
    {
        // This function calls a JavaScript function to update the chart.
        // Notice how multiple parameters are passed in.
        JsRuntime.InvokeVoidAsync("createNewChart", new { xs, ys });
    }

    private void PlotSin()
    {
        double[] xs = Enumerable.Range(0, PointCount).Select(x => (double)x).ToArray();
        double[] ys = xs.Select(x => Math.Sin(x / 10)).ToArray();
        PlotData(xs, ys);
    }

    private void PlotRandom()
    {
        double[] xs = Enumerable.Range(0, PointCount).Select(x => (double)x).ToArray();
        double[] ys = xs.Select(x => (Rand.NextDouble() - .5) * 1000).ToArray();
        PlotData(xs, ys);
    }

    private void PlotWalk()
    {
        double[] xs = Enumerable.Range(0, PointCount).Select(x => (double)x).ToArray();
        double[] ys = new double[PointCount];
        for (int i = 1; i < ys.Length; i++)
            ys[i] = ys[i - 1] + Rand.NextDouble() - .5;
        PlotData(xs, ys);
    }

    private void PlotRandomXY()
    {
        double[] xs = Enumerable.Range(0, PointCount).Select(x => Rand.NextDouble()).ToArray();
        double[] ys = Enumerable.Range(0, PointCount).Select(x => Rand.NextDouble()).ToArray();
        PlotData(xs, ys);
    }
}

index.html

These JavaScript blocks were added just before the closing </body> tag

<script type='text/javascript' src='https://www.gstatic.com/charts/loader.js'></script>

<script>
    google.charts.load('current', { packages: ['corechart', 'line'] });

    // draw an empty chart when the page first loads
    google.charts.setOnLoadCallback(initChart);
    function initChart() {
        var xs = [];
        var ys = [];
        window.createNewChart({xs, ys});
    }

    // draw a new chart given X/Y values
    window.createNewChart = (params) => {
        var xs = params.xs;
        var ys = params.ys;

        var data = new google.visualization.DataTable();
        data.addColumn('number', 'X');
        data.addColumn('number', 'Y');

        for (var i = 0; i < ys.length; i++) {
            data.addRow([xs[i], ys[i]]);
        }

        var options = {
            hAxis: { title: 'Horizontal Axis Label' },
            vAxis: { title: 'Vertical Axis Label' },
            title: 'This is a Google Chart in Blazor',
            legend: { position: 'none' },
        };

        var chart = new google.visualization.LineChart(document.getElementById('chart_div'));

        chart.draw(data, options);
    };

</script>

Automatic Resizing

Plots look bad when the window is resized because Google Charts adopts a fixed size on each render. To give the appearance of fluid charts (that resize to fit their container as its size changes), add some JavaScript to re-render on every resize.

window.onresize = function () { initChart(); };

We can't call our existing createNewChart() method because that expects data (from C#/Blazor) passed-in as a parameter. To support this type of resizing, the call from C# must be modified to store data arrays at the window level so they can be later accessed when the chart is plotted again. This would take some re-structuring of this project, but it's possible.

Conclusions

  • Google Charts is a straightforward way to display data in mouse-interactive graphs on the web.

  • JS interop allows Blazor to pass data to a JavaScript function that can plot it on a Google Chart.

  • Extra steps are required to automatically resize a Google Chart when its container size changes.

  • For interactive graphs in desktop applications, check out ScottPlot (an open-source plotting library for .NET that makes it easy to interactively display large datasets).

Resources

Markdown source code last modified on January 18th, 2021
---
title: Google Charts in Blazor
date: 2021-01-12 19:15:00
tags: blazor, csharp
---

# Google Charts in Blazor

**Google Charts is a free interactive JavaScript charting framework.** The [Google Chart Gallery](https://developers.google.com/chart/interactive/docs/gallery) showcases many of the available chart types and options. This project shows how to create data from C# functions in Blazor, then use JavaScript interop to pass arrays into JavaScript for display with Google Charts.

<div class="text-center">

[![](blazor-google-charts.jpg)](app)

</div>

* [Google Charts with Blazor Demo](app) 👈 Try it live!

* Source code: [blazor-google-charts.zip](blazor-google-charts.zip)

## index.razor

This page contains a `div` where the chart will be displayed, controls for customizing settings, and a few functions in the code-behind to call a JavaScript function that updates the chart.

```html
@page "/"
@inject IJSRuntime JsRuntime

<!-- this is where the Google Chart is displayed -->
<div id="chart_div" style="height: 400px;"></div>

<!-- buttons call C# functions -->
<button @onclick="PlotSin">Sin</button>
<button @onclick="PlotRandom">Random</button>
<button @onclick="PlotWalk">Walk</button>
<button @onclick="PlotRandomXY">RandomXY</button>

<!-- a slider bound to a C# field -->
<input type="range" @bind="PointCount" @bind:event="oninput">
```

```cs
@code{
    private int PointCount = 123;
    Random Rand = new Random();

    private void PlotData(double[] xs, double[] ys)
    {
        // This function calls a JavaScript function to update the chart.
		// Notice how multiple parameters are passed in.
        JsRuntime.InvokeVoidAsync("createNewChart", new { xs, ys });
    }

    private void PlotSin()
    {
        double[] xs = Enumerable.Range(0, PointCount).Select(x => (double)x).ToArray();
        double[] ys = xs.Select(x => Math.Sin(x / 10)).ToArray();
        PlotData(xs, ys);
    }

    private void PlotRandom()
    {
        double[] xs = Enumerable.Range(0, PointCount).Select(x => (double)x).ToArray();
        double[] ys = xs.Select(x => (Rand.NextDouble() - .5) * 1000).ToArray();
        PlotData(xs, ys);
    }

    private void PlotWalk()
    {
        double[] xs = Enumerable.Range(0, PointCount).Select(x => (double)x).ToArray();
        double[] ys = new double[PointCount];
        for (int i = 1; i < ys.Length; i++)
            ys[i] = ys[i - 1] + Rand.NextDouble() - .5;
        PlotData(xs, ys);
    }

    private void PlotRandomXY()
    {
        double[] xs = Enumerable.Range(0, PointCount).Select(x => Rand.NextDouble()).ToArray();
        double[] ys = Enumerable.Range(0, PointCount).Select(x => Rand.NextDouble()).ToArray();
        PlotData(xs, ys);
    }
}
```

## index.html

These JavaScript blocks were added just before the closing `</body>` tag

```js
<script type='text/javascript' src='https://www.gstatic.com/charts/loader.js'></script>

<script>
    google.charts.load('current', { packages: ['corechart', 'line'] });

    // draw an empty chart when the page first loads
    google.charts.setOnLoadCallback(initChart);
    function initChart() {
        var xs = [];
        var ys = [];
        window.createNewChart({xs, ys});
    }

    // draw a new chart given X/Y values
    window.createNewChart = (params) => {
        var xs = params.xs;
        var ys = params.ys;

        var data = new google.visualization.DataTable();
        data.addColumn('number', 'X');
        data.addColumn('number', 'Y');

        for (var i = 0; i < ys.length; i++) {
            data.addRow([xs[i], ys[i]]);
        }

        var options = {
            hAxis: { title: 'Horizontal Axis Label' },
            vAxis: { title: 'Vertical Axis Label' },
            title: 'This is a Google Chart in Blazor',
            legend: { position: 'none' },
        };

        var chart = new google.visualization.LineChart(document.getElementById('chart_div'));

        chart.draw(data, options);
    };

</script>
```

## Automatic Resizing

Plots look bad when the window is resized because Google Charts adopts a fixed size on each render. To give the appearance of fluid charts (that resize to fit their container as its size changes), add some JavaScript to re-render on every resize.

```js
window.onresize = function () { initChart(); };
```

We can't call our existing `createNewChart()` method because that expects data (from C#/Blazor) passed-in as a parameter. To support this type of resizing, the call from C# must be modified to _store_ data arrays at the window level so they can be later accessed when the chart is plotted again. This would take some re-structuring of this project, but it's possible.

## Conclusions

* Google Charts is a straightforward way to display data in mouse-interactive graphs on the web. 

* JS interop allows Blazor to pass data to a JavaScript function that can plot it on a Google Chart.

* Extra steps are required to automatically resize a Google Chart when its container size changes.

* For interactive graphs in desktop applications, check out [ScottPlot](https://swharden.com/scottplot) (an open-source plotting library for .NET that makes it easy to interactively display large datasets).

## Resources

* Live demo: [Google Charts with Blazor Demo](app)

* Source code: [blazor-google-charts.zip](blazor-google-charts.zip)

* Source code on GitHub: [/examples/2021-01-10-blazor-google-charts](https://github.com/swharden/Csharp-Data-Visualization/tree/master/examples/2021-01-10-blazor-google-charts)

* [C# Data Visualization](https://github.com/swharden/Csharp-Data-Visualization) on GitHub
January 9th, 2021

Mystify your Browser with Blazor

The project simulates the classic Mystify your Mind screensaver from Windows 3 using client-side Blazor. The graphics model logic is entirely C# and the web interface has user-facing options (razor components) to customize its behavior in real time. To render a frame JavaScript calls a C# function that returns JSON containing an array of line details (colors and coordinates) so a JavaScript render function can draw them on a HTML canvas. While .NET APIs for drawing on canvases exist, I find this method to be a bit more versatile because it does not require any external libraries, and it only makes a single JS interop call on each render so it is appreciably faster.

Live Demo

The C# Graphics Model

The point of this article is to discuss strategies for rendering graphics using client-side Blazor and WebAssembly, so I won't detail how the polygons are tracked here. Source code for these classes can be viewed on GitHub (MystifyBlazor/Models) or downloaded with this project (blazor-mystify.zip).

  • Models/Color.cs - Represents a color and has methods to help with hue-shifting

  • Models/Corner.cs - Represents a corner of a polygon (position, direction, and velocity) and has logic to move forward in time and bounce off walls

  • Models/Polygon.cs - A polygon is just a color and a list of corners that are connected by a single line.

  • Models/Shape.cs - A Shape is a moving polygon, and this class contains a list of polygons (a historical record of a single polygon's configuration as it changed over time).

  • Models/Field.cs - Represents the rectangular render area and holds multiple shapes.

    • This is the only class Blazor directly interacts with3
    • Configuration options (like rainbow mode) are public fields that can be bound to inputs on Razor pages
    • This class can return rendering instructions as JSON. This is just an array of lines, each with a color and an array of X/Y points.

index.razor

Canvas and Holder

This is the HTML that holds the HTML canvas. By using CSS to make the canvas background black I don't have to concern myself with manually drawing a black background on every new frame.

Note that I'm using Bootstrap 5 (not the Bootstrap 4 that currently comes with new Blazor projects), so the classes may be slightly different.

@page "/"

<div class="bg-white shadow mt-3" id="myCanvasHolder">
    <canvas id="myCanvas" style="background-color: black;"></canvas>
</div>

Code-Behind

The only goals here are to start the JavaScript render loop and provide a method JavaScript can call to get line data in JSON format. Notice the method called by JavaScript accepts width and height arguments so the data model can properly adjust to canvases that change size as the browser window changes dimensions.

@inject IJSRuntime JsRuntime

@code
{
    Models.Field MystifyField = new Models.Field();

    protected override void OnInitialized()
    {
        JsRuntime.InvokeAsync<object>("initRenderJS", DotNetObjectReference.Create(this));
    }

    [JSInvokable]
    public string UpdateModel(double width, double height)
    {
        MystifyField.Advance(width, height);
        return MystifyField.GetJSON();
    }
}

Data Binding

User-facing configuration is achieved by binding public fields of the graphics model directly to input elements.

<input type="range" 
    @bind="MystifyField.Speed" 
    @bind:event="oninput">

<input type="range" min="1"max="20"
    @bind="MystifyField.ShapeCount" 
    @bind:event="oninput">

<input type="range" min="3" max="20"
    @bind="MystifyField.CornerCount" 
    @bind:event="oninput" >

<input type="range" min="1" max="50"
    @bind="MystifyField.HistoryCount"
    @bind:event="oninput">

<button @onclick="MystifyField.RandomizeColors">
    Random Colors
</button>

<input type="checkbox" @bind="MystifyField.Rainbow">

index.html

This file contains JavaScript to start the renderer (an infinite loop) and request line data as JSON from a C# function in each render.

Notice that the aspect ratio of the canvas is maintained by setting its with to be a fraction of its height. The canvas width and height are passed as arguments to the C# function which updates the model.

<script>

    function renderJS() {

        // resize the canvas to fit its parent (resizing clears the canvas too)
        var holder = document.getElementById('myCanvasHolder');
        var canvas = document.getElementById('myCanvas');
        canvas.width = holder.clientWidth;
        canvas.height = canvas.width * .6;

        // tell C# about the latest dimensions, advance the model, and parse the new data
        var dataString = window.theInstance.invokeMethod('UpdateModel', canvas.width, canvas.height);
        var polys = JSON.parse(dataString);

        // render each polygon
        var lineCount = 0;
        var ctx = document.getElementById('myCanvas').getContext('2d');
        ctx.lineWidth = 2;
        for (var i = 0; i < polys.length; i++) {
            var poly = polys[i];
            var color = poly.shift();

            ctx.beginPath();
            for (var j = 0; j < poly.length; j++) {
                x = parseFloat(poly[j][0]);
                y = parseFloat(poly[j][1]);
                if (j == 0) {
                    ctx.moveTo(x, y);
                } else {
                    ctx.lineTo(x, y);
                }
            }
            ctx.strokeStyle = color;
            ctx.closePath();
            ctx.stroke();
            lineCount += 1;
        }

        window.requestAnimationFrame(renderJS);
    }

    window.initRenderJS = (instance) => {
        window.theInstance = instance;
        window.requestAnimationFrame(renderJS);
    };

</script>

JSON

The JSON that C# passes to the JavaScript renderer is just an array of lines, each with a color and an array of X/Y points. This is an example JSON packet to render a single frame:

["#00FFB4",[665.26,767.31], [1098.58,250.94], [1159.48,206.49], [717.52,194.45]],
["#00FFB4",[660.94,757.61], [1089.18,257.06], [1166.11,203.76], [720.90,201.92]],
["#00FFB4",[656.62,747.92], [1079.78,263.18], [1172.73,201.03], [724.29,209.40]],
["#00FFB4",[652.30,738.22], [1070.38,269.31], [1179.36,198.30], [727.67,216.88]],
["#00FFB4",[647.98,728.53], [1060.98,275.43], [1185.99,195.57], [731.05,224.36]],
["#0049FF",[1219.26,656.86], [5.34,599.35], [454.87,716.81], [276.93,416.92]],
["#0049FF",[1224.78,648.28], [0.00,602.83], [455.97,708.21], [286.48,408.14]],
["#0049FF",[1230.30,639.69], [6.55,606.32], [457.08,699.61], [296.03,399.35]],
["#0049FF",[1235.81,631.11], [13.10,609.80], [458.19,691.02], [305.58,390.57]],
["#0049FF",[1241.33,622.53], [19.65,613.28], [459.29,682.42], [315.13,381.79]]

Alternatively you can Render from C#

There are wrappers to let you draw on a HTML canvas from .NET, allowing you to write your render function in C# instead of JavaScript. It is a bit of added complexity (requires a package) and is a bit slower (additional JavaScript interop calls), so I did not choose to use it for this project.

See my earlier article Draw Animated Graphics in the Browser with Blazor WebAssembly for an example of how to draw on a canvas from C# using the Blazor.Extensions.Canvas package.

Performance Notes

Vanilla JavaScript will probably always be faster at rendering graphics in the browser than Blazor. If your application requires fast graphics, you'll have to write all your graphics model logic and render system in JavaScript. The great advantage of Blazor is that existing C# code that has already been developed (and extensively tested) can be used in the browser. At the time of writing, Speed is not Blazor's strong suit.

  • This system starts to choke when the model represents more than a few lines. The bottleneck seems to be encoding all the data as JSON on the C# side.

  • I tried using StringBuilder everywhere and also using int instead of float, but it did not produce large improvements.

  • I also tired using the Blazor.Extensions.Canvas package so I could make all the rendering calls from C# (eliminating the conversion to JSON step). Because this required so many individual interop calls (even with batching), it was about twice as slow as the JS renderer implemented in the code above.

Resources

Markdown source code last modified on January 18th, 2021
---
title: Mystify your Browser with Blazor
date: 2021-01-09 22:16:00
tags: blazor, csharp
---

# Mystify your Browser with Blazor

**The project simulates the classic _Mystify your Mind_ screensaver from Windows 3 using client-side Blazor.** The graphics model logic is entirely C# and the web interface has user-facing options (razor components) to customize its behavior in real time. To render a frame JavaScript calls a C# function that returns JSON containing an array of line details (colors and coordinates) so a JavaScript render function can draw them on a HTML canvas. While [.NET APIs for drawing on canvases](https://swharden.com/blog/2021-01-07-blazor-canvas-animated-graphics/) exist, I find this method to be a bit more versatile because it does not require any external libraries, and it only makes a single JS interop call on each render so it is appreciably faster.

<div class="text-center">

[![](blazor-mystify.gif)](app)

</div>

### Live Demo

* [Mystify your Browser with Blazor](app/) 👈 ***Launch the demo***

## The C# Graphics Model

The point of this article is to discuss strategies for rendering graphics using client-side Blazor and WebAssembly, so I won't detail how the polygons are tracked here. Source code for these classes can be viewed on GitHub ([MystifyBlazor/Models](https://github.com/swharden/Csharp-Data-Visualization/tree/master/examples/2021-01-09-blazor-mystify/MystifyBlazor/Models)) or downloaded with this project ([blazor-mystify.zip](blazor-mystify.zip)).

* [`Models/Color.cs`](https://github.com/swharden/Csharp-Data-Visualization/blob/master/examples/2021-01-09-blazor-mystify/MystifyBlazor/Models/Color.cs) - Represents a color and has methods to help with hue-shifting

* [`Models/Corner.cs`](https://github.com/swharden/Csharp-Data-Visualization/blob/master/examples/2021-01-09-blazor-mystify/MystifyBlazor/Models/Corner.cs) - Represents a corner of a polygon (position, direction, and velocity) and has logic to move forward in time and bounce off walls

* [`Models/Polygon.cs`](https://github.com/swharden/Csharp-Data-Visualization/blob/master/examples/2021-01-09-blazor-mystify/MystifyBlazor/Models/Polygon.cs) - A polygon is just a color and a list of corners that are connected by a single line.

* [`Models/Shape.cs`](https://github.com/swharden/Csharp-Data-Visualization/blob/master/examples/2021-01-09-blazor-mystify/MystifyBlazor/Models/Shape.cs) - A Shape is a moving polygon, and this class contains a list of polygons (a historical record of a single polygon's configuration as it changed over time).

* [`Models/Field.cs`](https://github.com/swharden/Csharp-Data-Visualization/blob/master/examples/2021-01-09-blazor-mystify/MystifyBlazor/Models/Field.cs) - Represents the rectangular render area and holds multiple shapes. 
  * This is the only class Blazor directly interacts with3
  * Configuration options (like rainbow mode) are public fields that can be bound to inputs on Razor pages
  * This class can return rendering instructions as JSON. This is just an array of lines, each with a color and an array of X/Y points.

## index.razor

### Canvas and Holder

This is the HTML that holds the HTML canvas. By using CSS to make the canvas background black I don't have to concern myself with manually drawing a black background on every new frame.

Note that I'm using [Bootstrap 5](https://getbootstrap.com/) (not the Bootstrap 4 that currently comes with new Blazor projects), so the classes may be slightly different.

```html
@page "/"

<div class="bg-white shadow mt-3" id="myCanvasHolder">
    <canvas id="myCanvas" style="background-color: black;"></canvas>
</div>
```

### Code-Behind

The only goals here are to start the JavaScript render loop and provide a method JavaScript can call to get line data in JSON format. Notice the method called by JavaScript accepts `width` and `height` arguments so the data model can properly adjust to canvases that change size as the browser window changes dimensions.

```cs
@inject IJSRuntime JsRuntime

@code
{
    Models.Field MystifyField = new Models.Field();

    protected override void OnInitialized()
    {
        JsRuntime.InvokeAsync<object>("initRenderJS", DotNetObjectReference.Create(this));
    }

    [JSInvokable]
    public string UpdateModel(double width, double height)
    {
        MystifyField.Advance(width, height);
        return MystifyField.GetJSON();
    }
}
```

### Data Binding

User-facing configuration is achieved by binding public fields of the graphics model directly to input elements.

```html
<input type="range" 
    @bind="MystifyField.Speed" 
    @bind:event="oninput">

<input type="range" min="1"max="20"
    @bind="MystifyField.ShapeCount" 
    @bind:event="oninput">
    
<input type="range" min="3" max="20"
    @bind="MystifyField.CornerCount" 
    @bind:event="oninput" >
    
<input type="range" min="1" max="50"
    @bind="MystifyField.HistoryCount"
    @bind:event="oninput">

<button @onclick="MystifyField.RandomizeColors">
    Random Colors
</button>

<input type="checkbox" @bind="MystifyField.Rainbow">
```

## index.html

This file contains JavaScript to start the renderer (an infinite loop) and request line data as JSON from a C# function in each render.

Notice that the aspect ratio of the canvas is maintained by setting its with to be a fraction of its height. The canvas width and height are passed as arguments to the C# function which updates the model.

```html
<script>

    function renderJS() {

        // resize the canvas to fit its parent (resizing clears the canvas too)
        var holder = document.getElementById('myCanvasHolder');
        var canvas = document.getElementById('myCanvas');
        canvas.width = holder.clientWidth;
        canvas.height = canvas.width * .6;

        // tell C# about the latest dimensions, advance the model, and parse the new data
        var dataString = window.theInstance.invokeMethod('UpdateModel', canvas.width, canvas.height);
        var polys = JSON.parse(dataString);

        // render each polygon
        var lineCount = 0;
        var ctx = document.getElementById('myCanvas').getContext('2d');
        ctx.lineWidth = 2;
        for (var i = 0; i < polys.length; i++) {
            var poly = polys[i];
            var color = poly.shift();

            ctx.beginPath();
            for (var j = 0; j < poly.length; j++) {
                x = parseFloat(poly[j][0]);
                y = parseFloat(poly[j][1]);
                if (j == 0) {
                    ctx.moveTo(x, y);
                } else {
                    ctx.lineTo(x, y);
                }
            }
            ctx.strokeStyle = color;
            ctx.closePath();
            ctx.stroke();
            lineCount += 1;
        }

        window.requestAnimationFrame(renderJS);
    }

    window.initRenderJS = (instance) => {
        window.theInstance = instance;
        window.requestAnimationFrame(renderJS);
    };

</script>
```

## JSON

The JSON that C# passes to the JavaScript renderer is just an array of lines, each with a color and an array of X/Y points. This is an example JSON packet to render a single frame:

```
["#00FFB4",[665.26,767.31], [1098.58,250.94], [1159.48,206.49], [717.52,194.45]],
["#00FFB4",[660.94,757.61], [1089.18,257.06], [1166.11,203.76], [720.90,201.92]],
["#00FFB4",[656.62,747.92], [1079.78,263.18], [1172.73,201.03], [724.29,209.40]],
["#00FFB4",[652.30,738.22], [1070.38,269.31], [1179.36,198.30], [727.67,216.88]],
["#00FFB4",[647.98,728.53], [1060.98,275.43], [1185.99,195.57], [731.05,224.36]],
["#0049FF",[1219.26,656.86], [5.34,599.35], [454.87,716.81], [276.93,416.92]],
["#0049FF",[1224.78,648.28], [0.00,602.83], [455.97,708.21], [286.48,408.14]],
["#0049FF",[1230.30,639.69], [6.55,606.32], [457.08,699.61], [296.03,399.35]],
["#0049FF",[1235.81,631.11], [13.10,609.80], [458.19,691.02], [305.58,390.57]],
["#0049FF",[1241.33,622.53], [19.65,613.28], [459.29,682.42], [315.13,381.79]]
```

## Alternatively you can Render from C# 

There are wrappers to let you draw on a HTML canvas from .NET, allowing you to write your render function in C# instead of JavaScript. It is a bit of added complexity (requires a package) and is a bit slower (additional JavaScript interop calls), so I did not choose to use it for this project. 

See my earlier article [_Draw Animated Graphics in the Browser with Blazor WebAssembly_](https://swharden.com/blog/2021-01-07-blazor-canvas-animated-graphics) for an example of how to draw on a canvas from C# using the [`Blazor.Extensions.Canvas`](https://github.com/BlazorExtensions/Canvas) package.

## Performance Notes

Vanilla JavaScript will probably always be faster at rendering graphics in the browser than Blazor. If your application requires fast graphics, you'll have to write all your graphics model logic and render system in JavaScript. The great advantage of Blazor is that existing C# code that has already been developed (and extensively tested) can be used in the browser. At the time of writing, Speed is not Blazor's strong suit.

* This system starts to choke when the model represents more than a few lines. The bottleneck seems to be encoding all the data as JSON on the C# side.

* I tried using StringBuilder everywhere and also using `int` instead of `float`, but it did not produce large improvements.

* I also tired using the [`Blazor.Extensions.Canvas`](https://github.com/BlazorExtensions/Canvas) package so I could make all the rendering calls from C# (eliminating the conversion to JSON step). Because this required so many individual interop calls (even with batching), it was about twice as slow as the JS renderer implemented in the code above.

## Resources

* Download this project: [blazor-mystify.zip](blazor-mystify.zip)

* Launch the demo: [Mystify your Browser with Blazor](app/)

* Browse source on GitHub: [C# Data Visualization: MystifyBlazor](https://github.com/swharden/Csharp-Data-Visualization/tree/master/examples/2021-01-09-blazor-mystify/)
January 8th, 2021

Boids in your Browser with Blazor

This project implements the Boids flocking algorithm in C# and uses Blazor WebAssembly to render it in the browser. Drone birds (bird-oids, or boids) follow a simple set of rules to determine their angle and velocity. When many boids are placed in a field together, interesting group behaviors emerge. Details about the boid flocking algorithm are discussed in depth elsewhere. This article summarizes how to configure a Blazor application to model graphics with C# and render them with JavaScript.

Live Demo

  • Boids with default settings: app/

  • Boids with custom settings: app/?boids=100&predators=5

  • Pro tip: resize your window really small and watch what happens!

The C# Boids Model

The code that manages the field of boids is entirely written in C#. It tracks the positions of boids and advances their positions and directions over time, but it does not manage rendering. Briefly,

  • A BoidField has a width, height, and array of Boids
  • Each Boid has a position, direction, and velocity
  • Each Boid can advance itself by considering the positions of all the other boids:
    • Rule 1: boids steer toward the center of mass of nearby boids
    • Rule 2: boids adjust direction to match nearby boids
    • Rule 3: boids adjust speed to match a match a target
    • Rule 4: boids steer away from very close boids
    • Rule 5: boids steer away from boids marked as predators
    • Rule 6: boids are repelled by the edge of the window

Model with C#, Render with JavaScript

At the time of writing, the most performant way to render high frame rates in the browser is to write your model and business logic entirely in JavaScript. The purpose of this project is not to make the most performant model possible, but rather to explore what can be done with client-side Blazor and WebAssembly. There is a strong advantage to being able to keep keeping your graphics model and business logic in C#, especially if it is already written and extensively tested.

In my Draw Animated Graphics in the Browser with Blazor WebAssembly article we used Blazor.Extensions.Canvas to draw on a HTML Canvas from C# code. Although this worked, it was limited by the fact that interop calls can be slow. Even though they can be batched, they still represent a significant bottleneck when thousands of calls need to be made on every frame.

In this project I used C# to model the field of boids, converted the field to JSON (with each boid having a X/Y position and a rotation), and had JavaScript parse the boid array and render each boid using a rendering function inside JavaScript.

Source Code

The full source code can be downloaded here (blazor-boids.zip) or navigated on GitHub C# Data Visualization: Blazor Boids. The C# code for Boid and BoidField can be found there. What I'll focus on here is the key Blazor code required to manage the model and render it using JavaScript.

index.razor

There are a lot of different things going on here. Most of them can be figured out with a little visual inspection. Here are the highlights:

  • A canvas is created inside a div, each with a unique ID so they can be referenced from JavaScript.

  • A IJSRuntime is injected so JS functions can be called from C#. Specifically, the initRenderJS is called when the component is first rendered.

  • A NavigationManager is injected to let us easily work with query strings (Note that QueryHelpers requires the Microsoft.AspNetCore.WebUtilities NuGet package).

  • A boidField graphics model is created as a private field and initialized OnAfterRender using query strings to customize the initializer parameters.

  • The UpdateModel() method is decorated with JSInvokable so it can be called from JavaScript. It accepts a width and height (which may change if the browser size changes) and can resize the boidField as needed. It advances the model then converts the positions and directions of all boids to JSON and returns it as a string.

@page "/"
@using System.Text;
@inject IJSRuntime JsRuntime
@inject NavigationManager NavManager
@using Microsoft.AspNetCore.WebUtilities

<div id="boidsHolder" style="position: fixed; width: 100%; height: 100%">
    <canvas id="boidsCanvas"></canvas>
</div>

@code{
    private Random rand = new Random();
    private Models.Field boidField;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        // call the initialization JavaScript function
        await JsRuntime.InvokeAsync<object>("initRenderJS", DotNetObjectReference.Create(this));
        await base.OnInitializedAsync();

        // use a query string to customize the number of boids
        int boidCount = 75;
        var uri = NavManager.ToAbsoluteUri(NavManager.Uri);
        if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("boids", out var boidCountString))
            if (int.TryParse(boidCountString, out int parsedBoidCount))
                boidCount = parsedBoidCount;

        // use a query string to customize the number of predators
        int predatorCount = 3;
        if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("predators", out var predatorCountString))
            if (int.TryParse(predatorCountString, out int parsedPredatorCount))
                predatorCount = parsedPredatorCount;

        // create the model using the custom settings
        boidField = new Models.Field(800, 600, boidCount, predatorCount);
    }

    [JSInvokable]
    public string UpdateModel(double width, double height)
    {
        if (boidField is null)
            return "";

        boidField.Resize(width, height);
        boidField.Advance(bounceOffWalls: true, wrapAroundEdges: false);

        StringBuilder sb = new StringBuilder("[");
        foreach (var boid in boidField.Boids)
        {
            double x = boid.X;
            double y = boid.Y;
            double r = boid.GetAngle() / 360;
            sb.Append($"[{x:0.0},{y:0.0},{r:0.000}],");
        }

        sb.Append(boidField.PredatorCount.ToString());
        sb.Append("]");
        return sb.ToString();
    }
}

index.html

Two JavaScript functions are added to the HTML:

  • renderJS() - this function calls C# to update the model and request the latest data, then calls itself to create an infinite render loop. Each time it executes it:
    • resizes the canvas to fit the div it's inside of
    • calls the C# UpdateModel() method (passing in the latest canvas size)
    • parses the JSON the C# method returns to obtain an array of boid details
    • clears the canvas by filling it with a blue color
    • renders each boid (by translating and rotating the canvas, not the boid)
    • requests itself be rendered again so rendering continues infinitely
  • initRenderJS - this function is called from Blazor so the running instance can be referenced in the future. It also starts the infinite render loop.
<script>

    function renderJS() {

        // resize the canvas to fit its parent (resizing clears the canvas too)
        var holder = document.getElementById('boidsHolder');
        var canvas = document.getElementById('boidsCanvas');
        canvas.width = holder.clientWidth;
        canvas.height = holder.clientHeight;

        // tell C# about the latest dimensions, advance the model, and parse the new data
        var boidsString = window.theInstance.invokeMethod('UpdateModel', canvas.width, canvas.height);
        var boids = JSON.parse(boidsString);
        var predatorCount = boids.pop();

        // render each boid
        var ctx = document.getElementById('boidsCanvas').getContext('2d');
        for (var i = 0; i < boids.length; i++) {
            var predator = i < predatorCount;
            var boid = boids[i];
            var x = boid[0];
            var y = boid[1];
            var rotation = boid[2];
            ctx.save();
            ctx.translate(x, y);
            ctx.rotate(rotation * 2 * Math.PI);
            ctx.beginPath();
            ctx.moveTo(0, 0);
            ctx.lineTo(4, -2);
            ctx.lineTo(0, 10);
            ctx.lineTo(-4, -2);
            ctx.lineTo(0, 0);
            ctx.closePath();
            ctx.fillStyle = predator ? '#FFFF00' : '#FFFFFF';
            ctx.fill();
            ctx.restore();
        }

        // call this same function to render the next frame
        window.requestAnimationFrame(renderJS);
    }

    window.initRenderJS = (instance) => {
        window.theInstance = instance;
        window.requestAnimationFrame(renderJS);
    };

</script>

Resources

Blazor Boids

Blazor Source Code

Boids in C# (Windows Application)

  • Boids in C# - a Windows application that runs much faster than this Blazor app. It has controls to customize flocking parameters. This was the source of the C# model I used for this Blazor app.

JavaScript Boids Simulators

Obviously a native JavaScript Boids simulator will be much faster. Implementing this Blazor app in JavaScript would have meant translating the model from C# to JavaScript. For performance-critical rendering-intensive applications, this is the way to go at the time of writing.

Literature

Markdown source code last modified on January 18th, 2021
---
title: Boids in your Browser with Blazor
date: 2021-01-08 22:58:00
tags: blazor, csharp
---

# Boids in your Browser with Blazor

**This project implements the [Boids flocking algorithm](https://en.wikipedia.org/wiki/Boids) in C# and uses Blazor WebAssembly to render it in the browser.** Drone birds (bird-oids, or boids) follow a simple set of rules to determine their angle and velocity. When many boids are placed in a field together, interesting group behaviors emerge. Details about the boid flocking algorithm are discussed in depth elsewhere. This article summarizes how to configure a Blazor application to _model_ graphics with C# and _render_ them with JavaScript.

<div class="text-center">

[![](blazor-boids.gif)](app)

</div>

### Live Demo

* Boids with default settings: [app/](app/)

* Boids with custom settings: [app/?boids=100&predators=5](app/?boids=100&predators=5)

* Pro tip: resize your window really small and watch what happens!

## The C# Boids Model

The code that manages the field of boids is entirely written in C#. It tracks the positions of boids and advances their positions and directions over time, but it does not manage rendering. Briefly, 

* A `BoidField` has a width, height, and array of Boids
* Each `Boid` has a position, direction, and velocity
* Each `Boid` can advance itself by considering the positions of all the other boids:
  * Rule 1: boids steer toward the center of mass of nearby boids
  * Rule 2: boids adjust direction to match nearby boids
  * Rule 3: boids adjust speed to match a match a target
  * Rule 4: boids steer away from very close boids
  * Rule 5: boids steer away from boids marked as predators
  * Rule 6: boids are repelled by the edge of the window

## Model with C#, Render with JavaScript

At the time of writing, the most performant way to render high frame rates in the browser is to write your model and business logic entirely in JavaScript. The purpose of this project is _not_ to make the most performant model possible, but rather to explore what can be done with client-side Blazor and WebAssembly. There is a strong advantage to being able to keep keeping your graphics model and business logic in C#, especially if it is already written and extensively tested.

In my _[Draw Animated Graphics in the Browser with Blazor WebAssembly](https://swharden.com/blog/2021-01-07-blazor-canvas-animated-graphics/)_ article we used `Blazor.Extensions.Canvas` to draw on a HTML Canvas from C# code. Although this worked, it was limited by the fact that interop calls can be slow. Even though they can be batched, they still represent a significant bottleneck when thousands of calls need to be made on every frame.

In this project I used C# to model the field of boids, converted the field to JSON (with each boid having a X/Y position and a rotation), and had JavaScript parse the boid array and render each boid using a rendering function inside JavaScript.

## Source Code

The full source code can be downloaded here ([blazor-boids.zip](blazor-boids.zip)) or navigated on GitHub [C# Data Visualization: Blazor Boids](https://github.com/swharden/Csharp-Data-Visualization/tree/master/examples/2021-01-08-blazor-boids/BlazorBoids). The C# code for `Boid` and `BoidField` can be found there. What I'll focus on here is the key Blazor code required to manage the model and render it using JavaScript.

### index.razor

There are a lot of different things going on here. Most of them can be figured out with a little visual inspection. Here are the highlights:

* A canvas is created inside a div, each with a unique ID so they can be referenced from JavaScript.

* A `IJSRuntime` is injected so JS functions can be called from C#. Specifically, the `initRenderJS` is called when the component is first rendered.

* A `NavigationManager` is injected to let us easily work with query strings (Note that `QueryHelpers` requires the `Microsoft.AspNetCore.WebUtilities` NuGet package).

* A `boidField` graphics model is created as a private field and initialized `OnAfterRender` using query strings to customize the initializer parameters.

* The `UpdateModel()` method is decorated with `JSInvokable` so it can be called from JavaScript. It accepts a width and height (which may change if the browser size changes) and can resize the `boidField` as needed. It advances the model then converts the positions and directions of all boids to JSON and returns it as a string.

```cs
@page "/"
@using System.Text;
@inject IJSRuntime JsRuntime
@inject NavigationManager NavManager
@using Microsoft.AspNetCore.WebUtilities

<div id="boidsHolder" style="position: fixed; width: 100%; height: 100%">
    <canvas id="boidsCanvas"></canvas>
</div>

@code{
    private Random rand = new Random();
    private Models.Field boidField;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        // call the initialization JavaScript function
        await JsRuntime.InvokeAsync<object>("initRenderJS", DotNetObjectReference.Create(this));
        await base.OnInitializedAsync();

        // use a query string to customize the number of boids
        int boidCount = 75;
        var uri = NavManager.ToAbsoluteUri(NavManager.Uri);
        if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("boids", out var boidCountString))
            if (int.TryParse(boidCountString, out int parsedBoidCount))
                boidCount = parsedBoidCount;

        // use a query string to customize the number of predators
        int predatorCount = 3;
        if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("predators", out var predatorCountString))
            if (int.TryParse(predatorCountString, out int parsedPredatorCount))
                predatorCount = parsedPredatorCount;

        // create the model using the custom settings
        boidField = new Models.Field(800, 600, boidCount, predatorCount);
    }

    [JSInvokable]
    public string UpdateModel(double width, double height)
    {
        if (boidField is null)
            return "";

        boidField.Resize(width, height);
        boidField.Advance(bounceOffWalls: true, wrapAroundEdges: false);

        StringBuilder sb = new StringBuilder("[");
        foreach (var boid in boidField.Boids)
        {
            double x = boid.X;
            double y = boid.Y;
            double r = boid.GetAngle() / 360;
            sb.Append($"[{x:0.0},{y:0.0},{r:0.000}],");
        }

        sb.Append(boidField.PredatorCount.ToString());
        sb.Append("]");
        return sb.ToString();
    }
}
```

### index.html

Two JavaScript functions are added to the HTML:

* `renderJS()` - this function calls C# to update the model and request the latest data, then calls itself to create an infinite render loop. Each time it executes it:
  * resizes the canvas to fit the div it's inside of
  * calls the C# `UpdateModel()` method (passing in the latest canvas size)
  * parses the JSON the C# method returns to obtain an array of boid details
  * clears the canvas by filling it with a blue color
  * renders each boid (by translating and rotating the canvas, not the boid)
  * requests itself be rendered again so rendering continues infinitely
* `initRenderJS` - this function is called from Blazor so the running instance can be referenced in the future. It also starts the infinite render loop.

```html
<script>

    function renderJS() {

        // resize the canvas to fit its parent (resizing clears the canvas too)
        var holder = document.getElementById('boidsHolder');
        var canvas = document.getElementById('boidsCanvas');
        canvas.width = holder.clientWidth;
        canvas.height = holder.clientHeight;

        // tell C# about the latest dimensions, advance the model, and parse the new data
        var boidsString = window.theInstance.invokeMethod('UpdateModel', canvas.width, canvas.height);
        var boids = JSON.parse(boidsString);
        var predatorCount = boids.pop();

        // render each boid
		var ctx = document.getElementById('boidsCanvas').getContext('2d');
        for (var i = 0; i < boids.length; i++) {
            var predator = i < predatorCount;
            var boid = boids[i];
            var x = boid[0];
            var y = boid[1];
            var rotation = boid[2];
            ctx.save();
            ctx.translate(x, y);
            ctx.rotate(rotation * 2 * Math.PI);
            ctx.beginPath();
            ctx.moveTo(0, 0);
            ctx.lineTo(4, -2);
            ctx.lineTo(0, 10);
            ctx.lineTo(-4, -2);
            ctx.lineTo(0, 0);
            ctx.closePath();
            ctx.fillStyle = predator ? '#FFFF00' : '#FFFFFF';
            ctx.fill();
            ctx.restore();
        }

        // call this same function to render the next frame
        window.requestAnimationFrame(renderJS);
    }

    window.initRenderJS = (instance) => {
        window.theInstance = instance;
        window.requestAnimationFrame(renderJS);
    };

</script>
```

## Resources

### Blazor Boids

* Launch _Blazor Boids_ with default settings: [app/](app/)

* Launch _Blazor Boids_ with custom settings: [app/?boids=100&predators=5](app/?boids=100&predators=5)

### Blazor Source Code

* Download source: [blazor-boids.zip](blazor-boids.zip)

* View source on GitHub: [C# Data Visualization: Blazor Boids](https://github.com/swharden/Csharp-Data-Visualization/tree/master/examples/2021-01-08-blazor-boids/BlazorBoids)

* [Draw Animated Graphics in the Browser with Blazor WebAssembly](https://swharden.com/blog/2021-01-07-blazor-canvas-animated-graphics/) uses a render loop written in C# (instead of JavaScript) using `Blazor.Extensions.Canvas`. It's a bit slower as a result, but for simple models it has the advantage of not having to write a JavaScript rendering method.

### Boids in C# (Windows Application)

* [Boids in C#](https://swharden.com/CsharpDataVis/boids/boids.md.html) - a Windows application that runs much faster than this Blazor app. It has controls to customize flocking parameters. This was the source of the C# model I used for this Blazor app.

<div class="text-center">

[![](boids-csharp-opengl.png)](https://swharden.com/CsharpDataVis/boids/boids.md.html)

</div>

### JavaScript Boids Simulators

**Obviously a native JavaScript Boids simulator will be much faster.** Implementing this Blazor app in JavaScript would have meant translating the model from C# to JavaScript. For performance-critical rendering-intensive applications, this is the way to go at the time of writing.

* [Boids algorithm demonstration](https://eater.net/boids) by Ben Eater (featured in SmarterEveryDay's [YouTube Video](https://youtu.be/4LWmRuB-uNU?t=187) about flocking birds)
* [Boids: Flocking made simple](http://www.harmendeweerd.nl/boids/) by Harmen de Weerd
* [Flocking](https://processing.org/examples/flocking.html) by Daniel Shiffman
* [Flocking Simulation](http://www.emergentmind.com/boids) by Matt Mazur
* [Simulate How Birds Flock in Processing](https://medium.com/swlh/boids-a-simple-way-to-simulate-how-birds-flock-in-processing-69057930c229) by Takuma Kakehi

### Literature
* [Boids: Background and Update](https://www.red3d.com/cwr/boids/) by Craig Reynolds (the inventor of boids)
* [Boids on Wikipedia](https://en.wikipedia.org/wiki/Boids)
* [3 Simple Rules of Flocking Behaviors: Alignment, Cohesion, and Separation](https://gamedevelopment.tutsplus.com/tutorials/3-simple-rules-of-flocking-behaviors-alignment-cohesion-and-separation--gamedev-3444)
* [Boids Code Golf](https://codegolf.stackexchange.com/questions/154277/implement-the-boids-algorithm)
Pages