Build the Game of Life with Blazor
Tags: blazor, gameoflife, canvas
Published: 2021-08-29 17:28:03

---
title: Build the Game of Life with Blazor
published: true
description: Build the Game of Life with Blazor
series: Build the Game of Life
tags: blazor, gameoflife, canvas
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1mxpauk6h3d84m7nmnto.png
 
 
---
Welcome to my series of posts where I'm building the famous [Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life) with different UI technologies.
In this post I'll show you how it is done in [Blazor Webassembly](https://docs.microsoft.com/en-us/aspnet/core/blazor/?view=aspnetcore-6.0).

Let me know in the comments if you're interested in seeing implementations in Xamarin.Forms/MAUI, WPF or Flutter.
________________

![Game of Life in motion](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y96bkbaz620taydfy6wx.gif)

Here's the code: https://github.com/mariusmuntean/GameOfLife
________________

## Prerequisites
* .NET - I bet you knew this one. Out of curiosity I went with .NET 6 Preview 7.
* Visual Studio Code or Visual Studio ('proper' or 'for Mac') or Rider - I really wanted to use VSCode for this but OmniSharp would not play ball so I resorted to Rider.

## Create the Blazor Webassembly project
If you're using Visual Studio proper or Visual Studio for Mac or even Rider, creating the project is just a series of clicks to create a new solution, then choose Blazor Webassembly App as the project type and give it a name.

I went the CLI way and used this command to create the project
```bash
dotnet new blazorwasm -o gol.blazorwasm
```
Start the project, just to make sure that everything works as expected.
You now have the option to choose between a normal run with or without the debugger and the hot-reload feature.
I went with hot-reloading. Just open a terminal/CLI window (my IDE has an integrated one) and run this command
```bash
dotnet watch
```
You should now be able to update files in your IDE and upon saving, they should be picked up automagically and the browser window will refresh.
At some point you'll be asked this
```bash
Do you want to restart your app - Yes (y) / No (n) / Always (a) / Never (v)?
```
I chose `Always`.

## Business Logic
In your project create a new directory and call it `Models`. Inside `Models` create an enum called `CellState`
```csharp
public enum CellState
{
    Dead,
    Alive
}
```
The game consists of a 2D grid where each slot is taken up by a cell. A cell can be either dead or alive. Now add the `Cell` class, in another file
```csharp
using System;
using System.Text.Json.Serialization;

namespace gol.blazorwasm.Models
{
    public class Cell
    {
        /// <summary>
        /// For the deserializer.
        /// </summary>
        /// <param name="currentState"></param>
        /// <param name="nextState"></param>
        [JsonConstructorAttribute]
        public Cell(CellState currentState, CellState nextState)
        {
            CurrentState = currentState;
            NextState = nextState;
        }

        public Cell(CellState currentCellState = CellState.Dead)
        {
        }

        public CellState CurrentState { get; private set; } = CellState.Dead;
        public CellState NextState { get; set; } = CellState.Dead;

        public void Tick()
        {
            CurrentState = NextState;
            NextState = CellState.Dead;
        }

        public void Toggle() => CurrentState = CurrentState switch
        {
            CellState.Alive => CellState.Dead,
            CellState.Dead => CellState.Alive,
            _ => throw new ArgumentOutOfRangeException()
        };
    }
}
```
The `CurrentState` of a `Cell` tells us how the cell is currently doing. Later we'll have to compute the new state of each `Cell` based on the state of its neighbors. To make the code simpler, I decided to store the next state of the `Cell` in the `NextState` property.
When the game is ready to transition each `Cell` into its next state, it can call `Tick()` on the `Cell` instance and the `NextState` becomes the `CurrentState`.
The method `Toggle()` will allow us to click somewhere on the 2D grid and kill or revive a `Cell`.

Let's talk about life. At the risk of sounding too reductionist, it's just a bunch of interacting cells. So we'll create one too

```csharp
using System;
using System.Collections.Generic;
using System.Linq;

namespace gol.blazorwasm.Models
{
    using CellsChanged = Action<Cell[][]>;

    public class Life
    {
        private readonly Cell[][] _cells;
        private readonly int _rows;
        private readonly int _cols;

        public Life(int rows, int cols)
        {
            if (rows <= 0 || cols <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(rows) + " " + nameof(cols), "the rows and columns cannot be 0 or less");
            }

            _rows = rows;
            _cols = cols;
            _cells = new Cell[rows][];
            for (var row = 0; row < rows; row++)
            {
                _cells[row] ??= new Cell[cols];
                for (var col = 0; col < cols; col++)
                {
                    _cells[row][col] = new Cell(CellState.Dead);
                }
            }
        }

        public Life(Cell[][] initialCells, CellsChanged onNewGeneration = null)
        {
            var newRows = initialCells.GetLength(0);
            var newCols = initialCells.GetLength(0);

            if (newRows <= 0 || newCols <= 0)
            {
                throw new ArgumentOutOfRangeException("one or both dimensions of the provided 2d array is 0");
            }

            _cells = initialCells;
            _rows = newRows;
            _cols = newCols;
        }

        public Cell[][] Cells => _cells;
    }
}
```
Let's break down what we just created. `Life` is a class that keeps track of a bunch of cells. For that we're using `Cell[][] _cells` which is just a 2D array of our simple `Cell` class. Having a 2D array allows us to know exactly where each cell is and who its neighbors are.
Traversing the 2D array can be cumbersome so I keep track of its dimensions with the fields `_rows` and `_columns`.

There are two ways in which I want to be able to create a new `Life`instance

* From scratch - meaning I just tell it how many rows and columns of `Cell`s I want and the `Life` just initializes its 2D `_cells` array with `Cell`s in the `Dead` state.
Basically, that's what the first constructor does.


* From a file - think of a saved game state. We'll later save the state of the game into a file and then load it up. When loading the saved game state, we need to tell the `Life` instance what each of its `Cell`'s state should be.

At this point we can create a new `Life`, where all the cells are either dead (first constructor) or in a state that we received from 'outside' (second constructor).

Our `Life` needs a bit more functionality and then it is complete. The very first time we load up the game, all the cells will be dead. So it would be nice to be able to just breathe some life into the dead cells.
For that, `Life` needs a method that takes the location of a `Cell` and toggles its state to the opposite value.

```csharp
    public void Toggle(int row, int col)
    {
        if (row < 0 || row >= _rows)
        {
            throw new ArgumentOutOfRangeException(nameof(row), row, "Row value invalid");
        }

        if (col < 0 || col >= _cols)
        {
            throw new ArgumentOutOfRangeException(nameof(col), col, "Column value invalid");
        }

        _cells[row][col].Toggle();
    }
```
The `Life` instance just makes sure that the specified location of the `Cell` makes sense and then tells that `Cell` to toggle its state. If you remember, the `Cell` class can toggle its state, if told to do so.

The last and most interesting method of `Life` implements the 3 [rules](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life#Rules) of the Game of Life.

1. Any live cell with two or three live neighbours survives.
2. Any dead cell with three live neighbours becomes a live cell.
3. All other live cells die in the next generation. Similarly, all other dead cells stay dead.

```csharp
public void Tick()
{
    // Compute the next state for each cell
    for (var row = 0; row < _rows; row++)
    {
        for (var col = 0; col < _cols; col++)
        {
            var currentCell = _cells[row][col];
            var cellNeighbors = GetCellNeighbors(row, col);
            var liveCellNeighbors = cellNeighbors.Count(cell => cell.CurrentState == CellState.Alive);

            // Rule source - https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life#Rules
            if (currentCell.CurrentState == CellState.Alive && (liveCellNeighbors == 2 || liveCellNeighbors == 3))
            {
                currentCell.NextState = CellState.Alive;
            }
            else if (currentCell.CurrentState == CellState.Dead && liveCellNeighbors == 3)
            {
                currentCell.NextState = CellState.Alive;
            }
            else
            {
                currentCell.NextState = CellState.Dead;
            }
        }
    }

    // Switch to the next state for each cell
    for (var row = 0; row < _rows; row++)
    {
        for (var col = 0; col < _cols; col++)
        {
            var currentCell = _cells[row][col];
            currentCell.Tick();
        }
    }
}

private List<Cell> GetCellNeighbors(int row, int col)
{
    var neighbors = new List<Cell>(8);

    for (var rowOffset = -1; rowOffset <= 1; rowOffset++)
    {
        for (var colOffset = -1; colOffset <= 1; colOffset++)
        {
            if (rowOffset == 0 && colOffset == 0)
            {
                // Skip self
                continue;
            }

            var neighborRow = row + rowOffset;
            var neighborCol = col + colOffset;
            if (neighborRow < 0 || neighborRow >= _rows)
            {
                continue;
            }

            if (neighborCol < 0 || neighborCol >= _cols)
            {
                continue;
            }

            neighbors.Add(_cells[neighborRow][neighborCol]);
        }
    }

    return neighbors;
}
```

Let me quickly walk you through the code. I'm traversing the 2D array of `Cell`s, making use of the rows and columns. For each cell I'm looking at its neighbors and based on the 3 game rules I'm computing the next state of the `Cell`.
When I'm done with that, I'm traversing the 2D grid again (I know, not very efficient of me, but I went for readable code) and telling each `Cell` to switch to its next state.

We're done with the business logic. It's time for the UI.

## UI

Similarly to React (see the previous post in this series), Blazor also uses a hierarchy of components. The similarities don't end here, in Blazor you can also mix code with markup and even go code-only, though I wouldn't recommend this.

When you're mixing code and markup you're making use of the Razor syntax. There's usually a `@code{}'` block that holds UI logic and some markup which can also be sprinkled with `@`directives that allow you to write inline code that emits HTML.

As far as I know the location of the `@code{}` block is not important and I prefer having it immediately after the `@using` and `@inject` statements. The markup lives just below the `@code{}` directive. That's also similar to React.

### Canvas drawing in Blazor
My first attempt was to use one of the Blazor libraries for drawing onto the canvas. These two looked the most promising
* Blazor.Extensions.Canvas - https://github.com/BlazorExtensions/Canvas
* Excubo.Blazor.Canvas https://github.com/excubo-ag/Blazor.Canvas

They're both easy to set up and you'll have some pixels on your canvas in no time. The issue is that I needed to draw hundreds of rectangles every time the data in the game changes. To produce each rectangle, a call from the C# code to Javascript is necessary, which still has a big overhead.
I tried batching too, but no dice.

In the end I gave up on these libraries and conceded that in Blazor development you still have to use Javascript, despite Microsoft's promise "Use only C# in the browser, no more JavaScript!".

## SimpleLife.razor
This component will use a `Life` instance to render the game. The `Life` instance will be created by the component or be loaded from a file.

Let's start by adding a `Components` directory. Inside this directory add a new razor Component and call it `SimpleLife.razor`. If you're adding it as a simple text file and then renaming its, just make sure that the Build Action is set to "Content".

Add these `@using` statements to the top of the file
```csharp
@using gol.blazorwasm.Models
@using Microsoft.AspNetCore.Components.Web
```
The first one just helps us in using our modes. The second one made Rider (my IDE) happy.

Now let the framework inject the Javascript runtime interop service. Add this `@inject` directive below your `@using`s
```csharp
@inject IJSRuntime _jsRuntime;
```
We'll use this interop service in a bit.

Get ready, we're adding the `@code` block. Below `@inject` add this this

```csharp
@code
{
    private const int SpaceForButtons = 30;

    ElementReference _canvasRef;

    private Life? _life;
    private int _cellEdgeAndSpacingLength;
    private double _cellEdgeLength;
    private int _canvasWidth;
    private int _canvasHeight;

    [Parameter]
    public int Columns { get; set; }

    [Parameter]
    public int Rows { get; set; }

    [Parameter]
    public int PixelWidth { get; set; }

    [Parameter]
    public int PixelHeight { get; set; }

    protected override void OnParametersSet()
    {
        InitData();
    }

    private void InitData()
    {
        _life = new Life(Rows, Columns);

        // Glider
        _life.Toggle(2, 2);
        _life.Toggle(3, 2);
        _life.Toggle(4, 2);
        _life.Toggle(4, 1);
        _life.Toggle(3, 0);

        UpdateCellAndCanvasSize();
    }

    private void UpdateCellAndCanvasSize()
    {
        _cellEdgeAndSpacingLength = Math.Min(PixelWidth / Columns, (PixelHeight - SpaceForButtons) / Rows);
        _cellEdgeLength = 0.9 * _cellEdgeAndSpacingLength;

        _canvasWidth = _cellEdgeAndSpacingLength * Columns;
        _canvasHeight = _cellEdgeAndSpacingLength * Rows;
    }
}
```

(Read in young Keanu Reeves' voice) Whoa what's going on here?
We're declaring a constant for the vertical space to reserve for some buttons.
Then we're declaring a variable `_life` for the `Life` instance that's going to be rendered.
The next two variables hold the values for a rendered `Cell`'s edge length, with and without the space between cells.
The last variables will hold the exact canvas dimensions, in pixels.

Next up are the properties. If you pay close attention you'll notice the `[Parameter]` attributes. This means that they're going to be passed in from outside, when another component uses this one. 
The `Rows` and `Columns` properties let us know how many rows and columns our game should have. 
The `PixelWidth` and `PixelHeight` properties tell our component what the available space is. We could've made the component responsive to layout changes, but I went for simple code. I'm open to simple solutions, so please let me know in the comments how to make it responsive.

As its name implies, `OnParametersSet()` will be called when the parent component provided values for our parameter properties. I'm using this component lifecycle method to initialize a new `Life` and compute the values for the variables in `InitData()`.
`_life` is assigned to a new instance of `Life` that directly uses the `Rows` and `Columns` that were passed to our component. Immediately after that I'm making use of the `Life`'s `Toggle()` method to toggle a few cells to the `Alive` state. They form a so-called 'Glider', one of the [canonical shapes](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life#Examples_of_patterns) in the Game of Life.
Then I'm doing a bit of computation to figure out the necessary `Cell` edge length so as to fit the requested columns and rows in the available space.


Before continuing with the `@code` block, let's add some markup. Add this code just below your `@code` block
```html
@if (_life != null)
{
    <div>
        <canvas width="@_canvasWidth"
                height="@_canvasHeight"
                @ref="@_canvasRef">
        </canvas>
    </div>
}
else
{
    <div>get a Life</div>
}
```

Yay, some Razor syntax! 🎉
So, if our code that initializes the `Life` instance ran, then `_life` is set and the other values were computed. In that case render the canvas with the computed dimensions and store a reference to that canvas in `_canvasRef`. Otherwise just emit a `<div>`with a sad message.

Before continuing, let's recap: we react to the component parameters being set by initializing an instance of `Life` with the requested number of rows and columns, then we're computing the size of the canvas that's going to contain the game's cells and the optimal size `Cell` edge length to fit them all on the canvas.

Phew, that was a lot! We're almost ready to see the little `Cells`s.

### Crossing boundaries
As I mentioned before we won't be using any drawing library for Blazor. Instead we're going to use the elegant [canvas api](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API).
That's not directly possible from WebAssembly, so we're making use of the Javascript interop here. 
We're going to write a Javascript function that can use the canvas API to draw our cells and we're going to attach it to the window object.
In your `wwwroot/index.html` add the following script tag to the body of the document
```HTML
<script>
    window.renderCellsOnCanvas = (canvas, mask, maskColors, _cellEdgeAndSpacingLength, _cellEdgeLength) => {
        mask.forEach((row, rowIndex) => {
            row.forEach((cellState, colIndex) => {
                var ctx = canvas.getContext("2d");
                ctx.beginPath();
                ctx.fillStyle = maskColors[cellState];
                ctx.rect(colIndex * _cellEdgeAndSpacingLength, rowIndex * _cellEdgeAndSpacingLength, _cellEdgeLength, _cellEdgeLength);
                ctx.fill();
            })
        })
    }
</script>
```
The function takes:
* `canvas` - a reference to an HTML `<canvas>`
* `mask` - a 2D array where each element stands for the state of the `Cell` at those coordinates. 0 means the `Cell` is dead and 1 means the `Cell` is alive.
* `maskColors` - a 1D array containing the color to use for each `Cell` state. So it is a mapping from the values from `mask` to usable colors.
* `_cellEdgeAndSpacingLength` - the length of a `Cell`'s edge, including the space to the next `Cell`.
* `_cellEdgeLength`- the length of a `Cell`'s edge.

It then loops over the 2D array and maps every value to a color and then draws a square of that color, at the current `Cell` position.

### Back to the SimpleLife
With this Javascript function available on the window object, we can go back to the `SimpleLife` component and add another lifecycle method that computes the necessary parameters for the Javascript function and then calls it. Below `OnParametersSet()` add this override
```csharp
   protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        var mask = new byte[Rows][];
        for (var row = 0; row < Rows; row++)
        {
            mask[row] = new byte[Columns];
            for (var col = 0; col < Columns; col++)
            {
                var currentCell = _life.Cells[row][col];
                mask[row][col] = (byte)currentCell.CurrentState;
            }
        }
        var maskColors = new[] { "black", "red" };

        await _jsRuntime.InvokeVoidAsync("renderCellsOnCanvas", _canvasRef, mask, maskColors, _cellEdgeAndSpacingLength, _cellEdgeLength);
    }
``` 
It builds the `mask` parameter by adding a 0 or a 1 for each `Cell` in our `_life`. This is also the place where we decide the colors for each `CellState`.

All that's left is to call the Javascript function with our injected Javascript interop service.

### Render the SimpleLife
To see the `SimpleLife` component in action we need to use it on a page.
In your `Pages` directory, add a new one called `GOL.razor` and give it this content
```csharp
@page "/"
@using gol.blazorwasm.Components
@inject IJSRuntime _jsRuntime

@code
{
    public int PixelWidth { get; set; }
    public int PixelHeight { get; set; }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            // This only works because I added an appropriate Javascript function to the "window" object. See index.html.
            var dimensions = await _jsRuntime.InvokeAsync<WindowDimensions>("getWindowDimensions");
            PixelWidth = Math.Min(dimensions.Width, dimensions.Height);
            PixelHeight = Math.Min(dimensions.Width, dimensions.Height);
            StateHasChanged();
        }
    }

    public class WindowDimensions
    {
        public int Width { get; set; }
        public int Height { get; set; }
    }
}

<SimpleLife Rows="50"
            Columns="50"
            PixelWidth="@PixelWidth"
            PixelHeight="@PixelHeight" />

```
This page also makes use of a Javascript function that returns the window dimensions. Now that you know how to do it, just add this new `<script>` tag to your `index.html`'s body
```HTML
<script>window.getWindowDimensions = function () {
    return {
        width: window.innerWidth,
        height: window.innerHeight,
    };
};</script>
```

Replace the content of your `App.razor` with this
```HTML
<Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData"/>
    </Found>
    <NotFound>
        <p role="alert">Sorry, there's nothing at this address.</p>
    </NotFound>
</Router>
```
If you still have your `dotnet watch` command running you should now see a new instance of the Game of Life with a simple Glider.

### Back to the SimpleLife, part 2
That was a lot! I know, but you should get away with copy pasting code. 😉

We're ready to make the `SimpleLife` component interactive. First is toggling `Cell`s from dead to alive and back.

#### Dead or Alive
In the markup section, locate the `<canvas>` element and add this attribute 
```csharp
@onclick="@(e => OnCanvasClick(e.OffsetX, e.OffsetY))"
```

And now implement the method that we're referencing. Just add this method to the `@code` block
```csharp
    private void OnCanvasClick(double pixelX, double pixelY)
    {
        if (pixelX < 0 || pixelX > _canvasWidth)
        {
            return;
        }
        if (pixelY < 0 || pixelY > _canvasHeight)
        {
            return;
        }

        // Translate pixel coordinates to rows and columns
        var clickedRow = (int)((pixelY / _canvasHeight) * Rows);
        var clickedCol = (int)((pixelX / _canvasWidth) * Columns);

        _life?.Toggle(clickedRow, clickedCol);
    }
```
The coordinates of a click event on the canvas as passed to the `OnCanvasClick` method, which converts pixel coordinates to `Cell` coordinates, i.e. row and column. Then the appropriate `Cell` is told to toggle its state.
You should now be able to click anywhere on the canvas to make ⬛️ cells 🟥 and vice versa.

That's nice and dandy, but we want to actually play the Game.
of Life. 

#### They're alive!
For that we want to apply the 3 rules of the game to the current game state and progress to the next game state.

Below the `<div>`that wraps the `<canvas>` add a new button like so
```csharp
<button @onclick=@(e => OnTickClicked()) class="btn btn-primary">Tick</button>
```
And in the `@code` block, add this method
```csharp
    private void OnTickClicked()
    {
        _life?.Tick();
    }
```
Whenever you click the new button, the `Life`'s `Tick()` method is invoked. That method applies the game rules and moves every `Cell` from its current state to its next state.
Through Blazor magic, a re-render is triggered which calls our overriden `OnAfterRenderAsync()` method which paints the current state onto the canvas.

By the way, if you wonder where the CSS classes come from you should know that Bootstrap is baked into Blazor.

This `Tick` business was easy! Now it pays off that we've encapsulated that logic in `Life`.

Let's continue and keep the critters under control.

#### Restoring order
Let's assume that the game got out of hand and the `Cell`s are plotting to overthrow the current world order. We need a way to clear the game board.
Start by adding a new button, below the previous one
```csharp
<button @onclick=@(e => OnClear()) class="btn btn-primary">Clear</button>
```

In the `@code` block add this method
```csharp
    private void OnClear()
    {
        InitData();
    }
```
There's not much to it, the method just calls our old `InitData()` method which overwrite out `Life` instance with a fresh one.

#### Saving game state
Come on, admit it, you've grown fond of some of the critters and you want to play with them later.
No problemo! we'll save the game state to a file. 
Below the existing `@using` statements, add this one 
```csharp
@using System.Text.Json;
```
this gives us access to the new Json serializer from Asp.Net.

Next step is to add yet another button like so
```csharp
<button @onclick=@OnDownload class="btn btn-primary">Save</button>
```

The referenced method should look like this
```csharp
    private async Task OnDownload()
    {
        var cellsJsonStr = JsonSerializer.Serialize(_life?.Cells);
        var fileName = $"game state {DateTime.Now.ToShortDateString()} {DateTime.Now.ToShortTimeString()}.json";
        await _jsRuntime.InvokeAsync<bool>("downloadStringAsFile", cellsJsonStr, fileName);
    }
```
The method serializes the `Life`s cells as a JSON string, generates a name for the file that will hold the saved game state and ... huh, Javascript interop again. 
You already know the drill. Add this new Javascript function
```HTML
<script>window.downloadStringAsFile = function (content, fileName) {
    const anchor = document.createElement("a");
    anchor.href = "data:text/plain;charset=utf-8," + encodeURIComponent(content);
    anchor.download = fileName;
    document.body.appendChild(anchor);
    try {
        anchor.click();
    } catch (e) {
        console.log(e);
        return false;
    } finally {
        document.body.removeChild(anchor);
    }
    return true;
};</script>
```
The Javascript function uses a pretty standard way of downloading dynamically generated content.
And bam! Your browser should inform you that a new file will be downloaded. Just save if somewhere you remember because we'll need it shortly.

#### Loading saved game state
Loading saved game state will follow these high-level operations
1. Pick a file with the game state that you want to restore.
2. Deserialize its content to a 2D array of `Cell`s.
3. Create a new `Life` instance with that 2D array of `Cells`.

Add this element just bellow the last button
```csharp
<InputFile OnChange="@OnSelectedFileChanged" class="btn"></InputFile>
```

Now add the referenced method 

```csharp
    private async Task OnSelectedFileChanged(InputFileChangeEventArgs eventArgs)
    {
        if (!eventArgs.File.Name.EndsWith(".json"))
        {
            Console.WriteLine("Stick to JSON files");
            return;
        }

        using var fileStream = eventArgs.File.OpenReadStream();
        var deserializedCells = await JsonSerializer.DeserializeAsync<Cell[][]>(fileStream);
        if (deserializedCells == null)
        {
            Console.WriteLine("Couldn't deserialize the cells. So sad.");
        }

        if (deserializedCells.Length != Rows || deserializedCells[0].Length != Columns)
        {
            Console.WriteLine($"Expected to load cells with {Rows} rows and {Columns} columns");
            return;
        }

        InitData(deserializedCells);
    }
```
The method is invoked when you click the `InputFile` and pick a file. It makes sure you chose a JSON file and then opens a .NET Stream to the file content.
Using the JSON Serializer from before, it tries to deserialize the file content to a `Cell[][]` and if that works and the resulting 2D array of `Cell`s has the correct shape it is passed to yet another method that should look like this
```csharp
    private void InitData(Cell[][] cells)
    {
        _life = new Life(cells);

        UpdateCellAndCanvasSize();
    }
```
This new method makes use of the second constructor of `Life` which takes the `Cell`s from 'outside' and just works with them.

## Conclusion
For this second post in the series I had even more fun than before, but I also was frustrated a lot more than expected.
I was disappointed by the bad interop performance while trying the two Blazor canvas libraries. That's not a fault of the libs, but of the general state of Blazor.
Nonetheless with a little Javascript foo I got around that and the performance of this Blazor implementation is as good as that of the React implementation.

Another way of improving the performance that I found early on was to use Ahead-of-Time compilation.
For that you need to add this to your .csproj file, right inside the first `<PropertyGroup>` tag
```XML
<RunAOTCompilation>true</RunAOTCompilation>
```
And then publish a Release build of your app, which looks like this from the CLI
```bash
dotnet publish -c Release
```
If it complains about a missing workload, you can install it with 
```bash
sudo dotnet workload install wasm-tools
```

After 10 minutes you'll have an AOT-compiled app that should be much faster than the one you saw while developing.

To try out the AOT version locally either use
```bash
npx serve
```
if you have it, or if you're like me and like to stay in the .NET world as much as possible you can use `dotnet-serve`. Fist install it 
```bash
dotnet tool install --global dotnet-serve
```
Then navigate to the publish folder and run 
```bash
dotnet-serve
```

Thanks for reading!