A .NET MAUI library to display particle effects (confetti, radial bursts, etc.). Works on Android, iOS, macOS, and Windows.
Originally created as Particle.Forms for Xamarin.Forms by Marius Muntean, then ported to .NET MAUI as Particle.Maui by Jeff Bowman. This fork maintains the library for .NET 9/.NET 10 and SkiaSharp 3.x.
The starting point is the class ParticleView.
| BindableProperty | Default | Description |
|---|---|---|
IsActive |
true |
Whether or not the control is displaying particles. Use this property to stop and restart the particles. |
IsRunning |
true |
Whether or not the control is animating particles. Use this property to pause and resume the particles. |
HasFallingParticles |
false |
Whether or not falling particles should be shown. |
FallingParticlesPerSecond |
60 |
Amount of new particles to be added every second when HasFallingParticles is true. |
AddParticlesOnTap |
false |
Whether or not to add particles on tap. |
TapParticleCount |
30 |
Amount of particles to add on tap when AddParticlesOnTap is true. |
AddParticlesOnDrag |
false |
Whether or not to add particles on drag. |
DragParticleCount |
60 |
Amount of particles to add on drag when AddParticlesOnDrag is true. |
DragParticleMoveType |
ParticleMovetype.Fall |
Particle movement type while dragging. |
UseSKGLView |
False on all platforms except Android |
Whether or not to use the hardware-accelerated view for drawing. |
ShowDebugInfo |
False |
Whether or not to show debug information. |
DebugInfoColor |
LawnGreen |
Color to use when displaying debug information. |
Particles normally come from touch or from the falling generator. Emit adds a burst
wherever you want one, which is what you need for effects the app drives itself — a
celebration, a score landing, an item collected.
| Method | Description |
|---|---|
Emit(SKPoint position, int count, ParticleMoveType moveType = Radiate) |
Burst at a point in canvas pixels. |
Emit(Point position, int count, ParticleMoveType moveType = Radiate) |
Same, in device-independent units, converted for display scale. |
EmitAtCenter(int count, ParticleMoveType moveType = Radiate) |
Burst in the middle of the view. |
// celebrate where the child just finished, in layout coordinates
MyParticleCanvas.Emit(new Point(letterCentreX, letterCentreY), 40);
// or drop confetti over the whole surface
MyParticleCanvas.EmitAtCenter(60, ParticleMoveType.Fall);AddParticlesOnTap is not required — Emit works regardless, and uses ParticleColors
like every other effect.
EmitTo flies a star from one point to another along an arc and tells you when it lands.
Where a burst says something happened, this says where it went — points travelling to a
score, an item to a basket.
| Method | Description |
|---|---|
EmitTo(SKPoint from, SKPoint to, Action onArrived = null, SKColor? color = null, float size = 14, float durationMillis = 600, float arcHeight = 0.4f) |
Canvas pixels |
EmitTo(Point from, Point to, ...) |
Device-independent units |
// a reward leaving the thing the user just finished, landing on the score
MyParticleCanvas.EmitTo(itemCentre, scoreCounter, onArrived: () => Score += 10);onArrived is raised on the main thread on the frame the star reaches the destination, so
the target can react at the moment of arrival. arcHeight is a fraction of the distance
travelled: 0 is a straight line, the default lobs noticeably.
These properties aren't bindable.
| Property | Default | Description |
|---|---|---|
TouchParticleGenerator |
SimpleParticleGenerator |
A ParticleBase generator to be used when interacting with the ParticleView |
FallingParticleGenerator |
FallingParticleGenerator |
A ParticleBase generator to be used when showing particles that fall from the top edge to the bottom |
CanvasSize |
Contains the current canvas size |
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:particle="clr-namespace:Particle.Maui;assembly=Particle.Maui"
x:Class="Particle.Maui.Sample.Demo2.Demo2"
Title="Custom Particles">
<ContentPage.Resources>
<x:Array Type="{x:Type Color}" x:Key="ConfettiColors">
<Color>#a864fd</Color>
<Color>#29cdff</Color>
<Color>#78ff44</Color>
<Color>#ff718d</Color>
<Color>#fdff6a</Color>
<Color>#ffcbf2</Color>
</x:Array>
</ContentPage.Resources>
<Grid VerticalOptions="FillAndExpand"
BackgroundColor="White"
Margin="0 ,0, 0, 5">
<particle:ParticleView x:Name="MyParticleCanvas"
IsActive="True"
IsRunning="True"
HasFallingParticles="True"
FallingParticlesPerSecond="20"
Margin="0, 20"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
ParticleColors="{StaticResource ConfettiColors}" />
</Grid>
</ContentPage>To conserve resources you can pause the particles when the Page is about to disappear and resume when the Page is about to appear.
protected override void OnAppearing()
{
base.OnAppearing();
MyParticleCanvas.IsRunning = true;
}
protected override void OnDisappearing()
{
base.OnDisappearing();
MyParticleCanvas.IsRunning = false;
}Tag the commit and the workflow does the rest:
git tag v2.1.0
git push origin v2.1.0.github/workflows/release.yml builds on windows-latest, packs, verifies the package
carries an Android, iOS, MacCatalyst and Windows target, pushes to NuGet and creates the
GitHub release. The tag supplies the version, so the csproj and the published package
cannot disagree.
Pack on Windows. The Windows target frameworks are conditioned on the host OS in the csproj, so packing on macOS or Linux produces a package that quietly lacks them — which would drop Windows support for anyone already using the library. The workflow fails the build rather than publish such a package.
Publishing uses NuGet trusted publishing, so there is no long-lived API key to store or rotate — the workflow proves its identity with a short-lived OIDC token issued to that run.
On nuget.org → your account → Trusted Publishing, add a policy:
| Field | Value |
|---|---|
| Repository owner | mos379 |
| Repository | Particles.Maui |
| Workflow file | release.yml |
| Environment | (leave empty) |
Then add a repository variable (not a secret) under Settings → Secrets and variables →
Actions → Variables: NUGET_USER, set to your nuget.org username. It is only a username,
which is why it is a variable rather than a secret.
- Added
EmitTo: a star that arcs to a target and raisesArrivedwhen it lands
- Added
EmitandEmitAtCenterso particles can be triggered from code, not only by touch
- Multi-targets .NET 9 and .NET 10 with SkiaSharp 3.x
- Replaced deprecated
Device.BeginInvokeOnMainThreadwithMainThread.BeginInvokeOnMainThread - Replaced deprecated
Device.RuntimePlatformwithDeviceInfo.Platform - Migrated SKMatrix44 APIs to SkiaSharp 3.x static factory methods
- Migrated debug text rendering to SKFont + SKPaint (SkiaSharp 3.x text API)
- Removed unused UpgradeAssistant analyzer package
If you find this project useful, consider supporting development:
MIT License - see LICENSE for details.
Copyright (c) 2020 Marius Muntean, 2022 Jeff Bowman, 2026 ITs4BM
