When Blazor Meets SkiaSharp

It’s been a while since I posted anything. I wish this one is about JitHub as it’s definitely due for an update, but that’ll have to wait as I’m still working on the next feature. Today we are talking about something different, Skia and Blazor. I’ve always wondered about these two and the possibilities of combining them. Due to the lack of experience in, well, anything related to them, I’ve never been able to make any real progress. That is, until this pull request made by the awesome Matthew Leibowitz added Blazor support to SkiaSharp. What does that enable? Well, keep reading~

Background

A little background here. Skia is a cross platform high performance 2D rendering engine made by Google. It’s written and often consumed in C++. In order to use it in the .NET world, Mono made the wrapper for Skia in C# called SkiaSharp. It made 2D rendering in Skia a possibility in many .NET based application types like Windows app or Xamarin app. Ever since Blazor came into play, many people wondered the possibility of using SkiaSharp in Blazor to render 2D content in HTML canvas. Matthew’s pull request to the SkiaSharp repo made this possible.

So What Did I Do With It?

I’ll save the boring bits of what this can enable and what it all means to the end. First, let’s look at what I made.

Flappy Bird implemented in C# in Blazor with SkiaSharp rendering

Several years back when I was looking for my first job, I did some practice projects to put in my resume. I heard that the author of Flappy Bird only took a few days to create the initial version of the game, so I wondered if I could do the same. With some luck, I was able to get a very simple version of the game working with JavaScript and rendering in HTML canvas in only a few hours. I felt good about it and put in on my website as an easter egg (no longer there). When I heard that SkiaSharp is available in Blazor, I felt that it makes sense to have this game to be my first experiment again. As a result, what you see in the GIF above is the exact same game I implemented years ago ported to C#/Blazor/SkiaSharp. The performance is very good. It’s much better compared to the original JavaScript version. Any lag you may see from the GIF is from recording the video and converting to GIF. The actual game itself is buttery smooth. I’ll put the code up later.

The “How”

Originally implemented in this test repo, Matthew implemented the Blazor support for SkiaSharp. Quickly after that, he made a PR into the SkiaSharp repo, and this great rendering engine is suddenly available to Blazor developers. As of the writing of this blog, SkiaSharp in Blazor is still in pre-release move, so I had to use it through this nuget package.

dotnet add package SkiaSharp.Views.Blazor --version 2.88.0-preview.179
dotnet add package SkiaSharp --version 2.88.0-preview.179

After that, I just had to port the original JavaScript version to C#.

JavaScript version game loop running at 30 FPS

Implementing the game in JavaScript was not hard. Essentially I set a callback to run 30 times every second, and I calculate where each sprite should be then draw them with calls to the HTML canvas. I won’t put too much code from the JavaScript side as it’s not too relevant to this post. My implementation was probably not even a good one since I was not able to get it to run smoothly in 60 FPS, so I kept it at 30 FPS. The porting work from JavaScript to C# was very easy. Most things are just naming convention and syntax changes. The actual calls to the canvas is very similar with SkiaSharp, so my code logic is largely the same. One thing that is different is that, in SkiaSharp, I no longer need to define FPS or manually set the callback with time interval. SkiaSharp supports render loop so it just calls the render function at max speed one after another. All I have to do is to define the logic of each render.

The HTML/Blazor element
The render function for each loop
MoveAll

The MoveAll function is very simple. I check if the current position of the bird is colliding with any pipes. If so, the game is over. Otherwise, I try to add pipes to the list of pipes that I need to render on the screen based on if there’s needs to be a new one appearing at the right edge of the screen. I then try to remove the ones that are already off screen. I then calculate how far should each pipe move based on the constant pipe moving speed. Finally, I calculate the new position of the bird based on the speed, gravity and user input.

DrawAll

The DrawAll function is even more simply. I basically just go through each sprite’s position and render them on the screen. I don’t even know if this is worth a separate paragraph.

What Does This Mean?

Originally I thought, oh wow, this is just like Flutter using Skia to render all the controls so developers can write cross platform apps in Dart and have it render in a pixel perfect way. Except this is for .NET, C# and Blazor, developers! This will target the web using WebAssembly instead of transpilation to JacaScript. Plus, there are already libraries that renders cross platform controls using SkiaSharp, so it must not be hard to port then to Blazor. All that needs to be done is to implement text rendering, scrolling, lazy page loading, media, accessibilities. You know, all the things that the browser already does really well. I think it’s clear what point I’m trying to make here. There is no need to implement everything in SkiaSharp again just to have it to render everything the same way the browser does. The whole point of programming is to avoid doing the same thing twice, naturally when it comes to a new library, the answer is not “to implement everything with it again”. Instead, I think the ability to use SkiaSharp in Blazor means us .NET and C# developer can finally have a decent option to render high performance custom control in the browser if we need to. It could be a game, a custom control that you want to share with other platform, an animation that plays in the background or anything that requires calling into a 2D rendering engine and paint a picture onto a surface. Comparing to what a Blazor developer had to do before this existed, which is to call into the JavaScript thread for anything related to the DOM, this option greatly improves the performance. This will help developers improve their existing app. For people who are making an app from scratch, they can use the existing web capabilities and all the features that Blazor provides without thinking about if they need to learn a completely new way to render the app.

So… I Can’t Use A New Framework Even If One Exist?

That definitely not what I meant. If, one day, an end-all-be-all framework was created and all the feature of the web and native platform are implemented properly in this one way to write and render app on any platform that support a common rendering engine, that’d be the best day for all developers. But until then, we shouldn’t throw away years of learning, features, bug fixes just for a idea that’s still pretty far out. That being said, if you are one of those talented people who can help making this future arrive faster, here are some repos that you can consider contributing to.

  • SkiaSharp: https://github.com/mono/SkiaSharp
    • The C# wrapper of the awesome 2D rendering engine Skia. With it being available on Blazor, more and more people are going to use it and start to submit bug reports or feature requests. It’s a good time to jump in and start looking for ways to contribute.
  • AvaloniaUI: https://github.com/AvaloniaUI/Avalonia
    • Cross platform app framework for .NET. They are also adding Skia support for some of their controls. There are exciting possibilities given maybe one day we’ll be able to run Avalonia app in the browser (or we already can? I’m not quite sure on their progress)
  • .NET Comet: https://github.com/dotnet/Comet
    • Comet is a cross platform app framework in .NET C# that allows you to write the UI in C#. It promotes the MVU pattern, and it’s the experimental playground of what .NET MAUI came to be. It supports rendering using SkiaSharp, and it used to have a Blazor target as well. With Blazor support in SkiaSharp, there might be hope to revive the Blazor target with SkiaSharp one day.
  • MAUI Graphics & MAUI Graphics Control: https://github.com/dotnet/Microsoft.Maui.Graphics & https://github.com/dotnet/Microsoft.Maui.Graphics.Controls
    • This is a cross platform 2D rendering library that uses each platforms’ native rendering layer. It also has a Skia target, which means Blazor could one day be a target. The control repo is a set of controls in different style rendered using the MAUI Graphics.
  • Uno Platform: https://github.com/unoplatform/uno
    • Based on the C# UWP framework, Uno compiles and runs the app on any platform that supports .NET. It’s in very active development and there are lots of community support.

Conclusion

SkiaSharp has very high performance, and Blazor support is one of the best things that happened to the .NET Blazor community. I can’t wait to see what happens in this space as I believe the future of web is with WebAssembly and web based rendering engine. With all the different repos that targets web or Skia, I bet it can be helpful to you as well.

One-Time
Monthly
Yearly

Make a one-time donation

Make a monthly donation

Make a yearly donation

Choose an amount

$5.00
$15.00
$100.00
$5.00
$15.00
$100.00
$5.00
$15.00
$100.00

Or enter a custom amount

$

Your contribution is appreciated.

Your contribution is appreciated.

Your contribution is appreciated.

DonateDonate monthlyDonate yearly

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s