u/leandroecorrea

▲ 39 r/dotnet

I built a multiplayer browser game entirely in .NET (Blazor WASM, Canvas 2D, SignalR, EF Core)

https://preview.redd.it/6dd8ojxbpltg1.png?width=1610&format=png&auto=webp&s=d1037ed05ae3a7ed27c2dfd387640492aca6d904

Hey everyone! I want to share a project I been working on. This one's a browser game inspired by Chocobo Hot & Cold minigame from Final Fantasy IX,built as a full multiplayer web game using only .NET stack. You can play it directly in the browser, no install needed. As for the stack:

- Blazor WASM for the frontend (running in browser at 60 fps)

- HTML5 Canvas 2D for rendering through JS interop (no WebGL, no Unity, just canvas draw calls from C#)

- ASP.NET Core backend API (leaderboard, SignalR for multiplayer battles)

- PostgreSQL with EF Core

- Google and Discord OAuth for login (this was not actually as bad as I thought, though I hit some issues from time to time)

So, long story short, this game started as a bootcamp project when I've started coding. Just a Console App based on SFML. Kept working on that for a while, playing with inmutable ECS pattern (yeah this was an overkill but wanted to better understand it). The entire game state is a single record called World that gets replaced every frame, never mutated. All systems are static pure functions so they take a World in and return a new World out.

I've captured side effects like audio as data (Effect records) and executed after all systems run.

The game uses a Frame carrier pattern that threads (World, ImmutableList<Effect>) through the system pipeline, and a Pipe pattern for composing state transforms:

var result = new MotionState(pos, scale, accel)
  .Pipe(ctx, Horizontal)
  .Pipe(ctx, Vertical)
  .Pipe(ctx, Perspective);

The game engine lives in a shared library (ChocoboDig.Core) with no dependency on any rendering framework, so same logic that ran on the desktop SFML version now runs in WASM without changes.

As for WASM, the Canvas rendering is all through IJSRuntime interop. C# dictates what to draw, JS does the actual canvas calls. Pixel art with imageSmoothingEnabled = false, screen shake, some particle effects, floating text, etc.

I was honestly impressed about Blazor WASM. It can absolutely handle a real-time game loop, the performance was better than I expected. Initial load time is probably what I still need to nail but everything else looks pretty good.

JS interop for Canvas is not as slow as people think if you batch your calls

As for SignalR: it just works great for this kind of real-time sync, but I'd like to see multiple battles at the same time to see if it actually blows up or not lol.

If anyone want to see how a specific part works I can share code snippets in the comments, just ask!

reddit.com
u/leandroecorrea — 18 hours ago