1. Home
  2. Docs
  3. Getting Started
  4. Installation – .NET MAUI

Installation – .NET MAUI

The LightBuzz SDK now supports Microsoft’s .NET MAUI under .NET 8 LTS.

Supported platforms

  • Android 29+ (use 34+)
  • iOS 14+
  • Windows 10+

Mac Catalyst support will be available as a future update. Tizen support is not planned.

Installation

To install the LightBuzz SDK for MAUI, you need a NuGet package that contains the required C# infrastructure, the native platform-specific plugins, as well as the AI models. Here’s the complete process:

1) Import the NuGet package

Before installing the MAUI package, you first need to configure your project to search for local NuGet packages.

2) Import the native plugins & resources

The LightBuzz native plugins and resources are platform-specific binaries required for body tracking. It’s essential to install these files in order for your app to run properly.

In your MAUI project, navigate to the Platforms directory and create a folder named libs under the iOS and Windows platforms. Copy and paste the plugins from the downloaded folder into the respective platform folders. Your project structure should look like this:

Then, navigate to the Platforms directory and create a folder named Resources/Raw under the Android and Windows platforms only. Here’s how your project structure should look like:

3) Update your .csproj file

For MAUI to load those files, add the following entries in your .csproj file. You’ll need to edit it directly using a text editor:

<Project>
	<!-- Your project entries -->
	<!-- ... -->
	<!-- LightBuzz SDK entries -->
	<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
		<MauiAsset Include="Platforms\Android\Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
	</ItemGroup>
	<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
		<NativeReference Include="Platforms/iOS/libs/lightbuzz_bodytracking.framework/*" Pack="true">
			<Kind>Framework</Kind>
			<SmartLink>True</SmartLink>
			<ForceLoad>True</ForceLoad>
		</NativeReference>
	</ItemGroup>
	<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">
		<None Include="Platforms/Windows/libs/*.dll" Pack="true">
			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
			<Link>%(Filename)%(Extension)</Link>
		</None>
		<MauiAsset Include="Platforms\Windows\Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" Pack="true" />
	</ItemGroup>
</Project>

The above entries will load the native plugins and resource files in their proper runtime locations.

4) Modify your manifest files

Most body-tracking apps use a live camera feed. Mobile devices require explicit permission to access the camera. To avoid exceptions, you must request OS-specific permissions.

👉 For iOS, you need to configure your Info.plist file to include the Camera permission entry (required to access the cameras).

<key>NSCameraUsageDescription</key>
<string>Camera is required for body-tracking.</string>

👉 For Android, you need to configure your AndroidManifest.xml file to include the OpenCL library (required for boosting performance) and the Camera permission entry (required to access the cameras).

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
	<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true">
		<uses-native-library android:name="libOpenCL.so" android:required="false" />
		<uses-native-library android:name="libOpenCL-pixel.so" android:required="false" />
	</application>
	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
	<uses-permission android:name="android.permission.INTERNET" />
	<uses-permission android:name="android.permission.CAMERA" />
</manifest>

Run a sample project

Here’s a sample project that opens the camera, displays the feed, and overlays the detected skeletons using the custom LightBuzzViewer XAML control:

<?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:lightbuzz="clr-namespace:MauiApp1"
             x:Class="MauiApp1.MainPage">
    <Grid>
        <GraphicsView x:Name="_viewer">
            <GraphicsView.Drawable>
                <lightbuzz:LightBuzzViewer />
            </GraphicsView.Drawable>
        </GraphicsView>
    </Grid>
</ContentPage>
using LightBuzz.BodyTracking;
using Microsoft.Maui.Graphics.Platform;
namespace MauiApp1
{
    public partial class MainPage : ContentPage
    {
        private Sensor _sensor = Sensor.Create(new SensorConfiguration
        {
            SensorType = SensorType.Webcam,
            DeviceIndex = 0,
            RequestedColorWidth = 640,
            RequestedColorHeight = 360,
            RequestedFPS = 30,
            Threading = ThreadingMode.Parallel,
        });
        public MainPage()
        {
            InitializeComponent();
            Loaded += OnLoaded;
            Unloaded += MainPage_Unloaded;
        }
        private async void OnLoaded(object? sender, EventArgs e)
        {
            PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.Camera>();
            if (status == PermissionStatus.Granted)
            {
                Open();
            }
            else
            {
                status = await Permissions.RequestAsync<Permissions.Camera>();
                if (status == PermissionStatus.Granted)
                {
                    Open();
                }
                else
                {
                    System.Diagnostics.Debug.WriteLine("User permission is required to access the camera.");
                }
            }
        }
        private void MainPage_Unloaded(object? sender, EventArgs e)
        {
            _sensor.FrameDataArrived -= Sensor_FrameDataArrived;
            _sensor.Close();
            _sensor.Dispose();
        }
        private void Open()
        {
            _sensor.FrameDataArrived += Sensor_FrameDataArrived;
            _sensor.Open();
        }
        private void Sensor_FrameDataArrived(object? sender, FrameData e)
        {
            List<Body> bodies = e.BodyData;
            byte[] jpeg = e.ToJpeg();
            int width = e.Width;
            int height = e.Height;
            int channels = e.ColorFormat.Channels();
            Dispatcher.Dispatch(() =>
            {
                _viewer.Load(bodies, jpeg, width, height);
            });
        }
    }
}

Lastly, here’s the source code for the LightBuzzViewer control (not required, but still useful):

using LightBuzz.BodyTracking;
using Microsoft.Maui.Graphics.Platform;
namespace MauiApp1
{
    public class LightBuzzViewer : IDrawable
    {
        public float FrameWidth { get; set; }
        public float FrameHeight { get; set; }
        public float CanvasScale { get; set; }
        public byte[] JpegData { get; set; }
        public List<Body> BodyData { get; set; } = new List<Body>();
        public void Draw(ICanvas canvas, RectF dirtyRect)
        {
            DrawColor(canvas);
            DrawBodies(canvas);
        }
        private void DrawColor(ICanvas canvas)
        {
            if (JpegData == null || FrameWidth <= 0.0f || FrameHeight <= 0.0f) return;
            using (var stream = new MemoryStream(JpegData))
            {
                var image = PlatformImage.FromStream(stream, ImageFormat.Jpeg);
                canvas.DrawImage(image, 0, 0, FrameWidth * CanvasScale, FrameHeight * CanvasScale);
            }
        }
        private void DrawBodies(ICanvas canvas)
        {
            if (BodyData == null) return;
            canvas.FillColor = Colors.White;
            canvas.StrokeColor = Colors.White;
            canvas.StrokeSize = 2;
            foreach (Body body in BodyData)
            {
                DrawBody(canvas, body);
            }
        }
        private void DrawBody(ICanvas canvas, Body body)
        {
            Joint head = body.Joints[JointType.Head];
            Joint nose = body.Joints[JointType.Nose];
            Joint eyeLeft = body.Joints[JointType.EyeLeft];
            Joint eyeRight = body.Joints[JointType.EyeRight];
            Joint earLeft = body.Joints[JointType.EarLeft];
            Joint earRight = body.Joints[JointType.EarRight];
            Joint neck = body.Joints[JointType.Neck];
            Joint chest = body.Joints[JointType.Chest];
            Joint waist = body.Joints[JointType.Waist];
            Joint pelvis = body.Joints[JointType.Pelvis];
            Joint clavicleLeft = body.Joints[JointType.ClavicleLeft];
            Joint clavicleRight = body.Joints[JointType.ClavicleRight];
            Joint shoulderLeft = body.Joints[JointType.ShoulderLeft];
            Joint shoulderRight = body.Joints[JointType.ShoulderRight];
            Joint elbowLeft = body.Joints[JointType.ElbowLeft];
            Joint elbowRight = body.Joints[JointType.ElbowRight];
            Joint wristLeft = body.Joints[JointType.WristLeft];
            Joint wristRight = body.Joints[JointType.WristRight];
            Joint hipLeft = body.Joints[JointType.HipLeft];
            Joint hipRight = body.Joints[JointType.HipRight];
            Joint kneeLeft = body.Joints[JointType.KneeLeft];
            Joint kneeRight = body.Joints[JointType.KneeRight];
            Joint ankleLeft = body.Joints[JointType.AnkleLeft];
            Joint ankleRight = body.Joints[JointType.AnkleRight];
            Joint footLeft = body.Joints[JointType.FootLeft];
            Joint footRight = body.Joints[JointType.FootRight];
            Joint heelLeft = body.Joints[JointType.HeelLeft];
            Joint heelRight = body.Joints[JointType.HeelRight];
            DrawJoint(canvas, head);
            DrawJoint(canvas, nose);
            DrawJoint(canvas, eyeLeft);
            DrawJoint(canvas, eyeRight);
            DrawJoint(canvas, earLeft);
            DrawJoint(canvas, earRight);
            DrawJoint(canvas, neck);
            DrawJoint(canvas, chest);
            DrawJoint(canvas, waist);
            DrawJoint(canvas, pelvis);
            DrawJoint(canvas, clavicleLeft);
            DrawJoint(canvas, clavicleRight);
            DrawJoint(canvas, shoulderLeft);
            DrawJoint(canvas, shoulderRight);
            DrawJoint(canvas, elbowLeft);
            DrawJoint(canvas, elbowRight);
            DrawJoint(canvas, wristLeft);
            DrawJoint(canvas, wristRight);
            DrawJoint(canvas, hipLeft);
            DrawJoint(canvas, hipRight);
            DrawJoint(canvas, kneeLeft);
            DrawJoint(canvas, kneeRight);
            DrawJoint(canvas, ankleLeft);
            DrawJoint(canvas, ankleRight);
            DrawJoint(canvas, footLeft);
            DrawJoint(canvas, footRight);
            DrawJoint(canvas, heelLeft);
            DrawJoint(canvas, heelRight);
            DrawLine(canvas, earLeft, eyeLeft);
            DrawLine(canvas, eyeLeft, nose);
            DrawLine(canvas, nose, eyeRight);
            DrawLine(canvas, eyeRight, earRight);
            DrawLine(canvas, nose, neck);
            DrawLine(canvas, neck, chest);
            DrawLine(canvas, chest, waist);
            DrawLine(canvas, waist, pelvis);
            DrawLine(canvas, chest, clavicleLeft);
            DrawLine(canvas, chest, clavicleRight);
            DrawLine(canvas, clavicleLeft, shoulderLeft);
            DrawLine(canvas, clavicleRight, shoulderRight);
            DrawLine(canvas, shoulderLeft, elbowLeft);
            DrawLine(canvas, shoulderRight, elbowRight);
            DrawLine(canvas, elbowLeft, wristLeft);
            DrawLine(canvas, elbowRight, wristRight);
            DrawLine(canvas, pelvis, hipLeft);
            DrawLine(canvas, pelvis, hipRight);
            DrawLine(canvas, hipLeft, kneeLeft);
            DrawLine(canvas, hipRight, kneeRight);
            DrawLine(canvas, kneeLeft, ankleLeft);
            DrawLine(canvas, kneeRight, ankleRight);
            DrawLine(canvas, ankleLeft, footLeft);
            DrawLine(canvas, ankleRight, footRight);
            DrawLine(canvas, ankleLeft, heelLeft);
            DrawLine(canvas, ankleRight, heelRight);
            DrawHand(canvas, body.HandLeft);
            DrawHand(canvas, body.HandRight);
        }
        private void DrawHand(ICanvas canvas, Hand hand)
        {
            if (hand == null) return;
            FingerJoint root = hand.FingerJoints[FingerType.Root];
            FingerJoint thumbCmc = hand.FingerJoints[FingerType.ThumbCMC];
            FingerJoint thumbMcp = hand.FingerJoints[FingerType.ThumbMCP];
            FingerJoint thumbIp = hand.FingerJoints[FingerType.ThumbIP];
            FingerJoint thumbTip = hand.FingerJoints[FingerType.ThumbTip];
            FingerJoint indexMcp = hand.FingerJoints[FingerType.IndexMCP];
            FingerJoint indexPip = hand.FingerJoints[FingerType.IndexPIP];
            FingerJoint indexDip = hand.FingerJoints[FingerType.IndexDIP];
            FingerJoint indexTip = hand.FingerJoints[FingerType.IndexTip];
            FingerJoint middleMcp = hand.FingerJoints[FingerType.MiddleMCP];
            FingerJoint middlePip = hand.FingerJoints[FingerType.MiddlePIP];
            FingerJoint middleDip = hand.FingerJoints[FingerType.MiddleDIP];
            FingerJoint middleTip = hand.FingerJoints[FingerType.MiddleTip];
            FingerJoint ringMcp = hand.FingerJoints[FingerType.RingMCP];
            FingerJoint ringPip = hand.FingerJoints[FingerType.RingPIP];
            FingerJoint ringDip = hand.FingerJoints[FingerType.RingDIP];
            FingerJoint ringTip = hand.FingerJoints[FingerType.RingTip];
            FingerJoint pinkyMcp = hand.FingerJoints[FingerType.PinkyMCP];
            FingerJoint pinkyPip = hand.FingerJoints[FingerType.PinkyPIP];
            FingerJoint pinkyDip = hand.FingerJoints[FingerType.PinkyDIP];
            FingerJoint pinkyTip = hand.FingerJoints[FingerType.PinkyTip];
            FingerJoint palm = hand.FingerJoints[FingerType.Palm];
            DrawPath(canvas, palm, thumbCmc, thumbMcp, thumbIp, thumbTip);
            DrawPath(canvas, palm, indexMcp, indexPip, indexDip, indexTip);
            DrawPath(canvas, palm, middleMcp, middlePip, middleDip, middleTip);
            DrawPath(canvas, palm, ringMcp, ringPip, ringDip, ringTip);
            DrawPath(canvas, palm, pinkyMcp, pinkyPip, pinkyDip, pinkyTip);
            DrawPath(canvas, thumbMcp, indexMcp, middleMcp, ringMcp, pinkyMcp, root, thumbCmc);
            DrawPath(canvas, root, palm);
        }
        private void DrawPath(ICanvas canvas, params FingerJoint[] joints)
        {
            if (joints == null) return;
            PathF path = new PathF();
            foreach (var joint in joints)
            {
                Vector2D point = joint.Position2D * CanvasScale;
                path.LineTo(point.X, point.Y);
            }
            canvas.DrawPath(path);
        }
        private void DrawJoint(ICanvas canvas, Joint joint)
        {
            if (joint.TrackingState == TrackingState.Inferred || joint.TrackingState == TrackingState.Inferred) return;
            Vector2D point = joint.Position2D * CanvasScale;
            if (point == Vector2D.Zero) return;
            canvas.DrawCircle(point.X, point.Y, 4);
        }
        private void DrawLine(ICanvas canvas, Joint joint1, Joint joint2)
        {
            if (joint1.TrackingState == TrackingState.Inferred || joint2.TrackingState == TrackingState.Inferred)
                return;
            Vector2D point1 = joint1.Position2D * CanvasScale;
            Vector2D point2 = joint2.Position2D * CanvasScale;
            if (point1 == Vector2D.Zero || point2 == Vector2D.Zero) return;
            canvas.DrawLine(point1.X, point1.Y, point2.X, point2.Y);
        }
    }
    public static class LightBuzzViewerExtensions
    {
        public static void Load(this GraphicsView view, List<Body> bodies, byte[] jpegData, int width, int height)
        {
            if (view == null) return;
            var canvas = view.Drawable as LightBuzzViewer;
            if (canvas == null) return;
            double windowWidth = view.Window.Width;
            double windowHeight = view.Window.Height;
            double w = Math.Max(windowWidth, windowHeight);
            double h = Math.Min(windowWidth, windowHeight);
            bool isLandscape = width > height;
            double wWidth = isLandscape ? w : h;
            float scale = (float)wWidth / (float)width;
            canvas.BodyData = bodies;
            canvas.JpegData = jpegData;
            canvas.FrameWidth = width;
            canvas.FrameHeight = height;
            canvas.CanvasScale = scale;
            view.Scale = scale;
            view.Invalidate();
        }
    }
}

For questions and support, contact support@lightbuzz.com.

How can we help?