diff --git a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/KristofferStrube.Blazor.WebAudio.WasmExample.csproj b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/KristofferStrube.Blazor.WebAudio.WasmExample.csproj
index bf22fde..78be7a9 100644
--- a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/KristofferStrube.Blazor.WebAudio.WasmExample.csproj
+++ b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/KristofferStrube.Blazor.WebAudio.WasmExample.csproj
@@ -21,11 +21,4 @@
-
-
-
- true
-
-
-
diff --git a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Pages/DotNetBot.razor b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Pages/DotNetBot.razor
new file mode 100644
index 0000000..72bbb05
--- /dev/null
+++ b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Pages/DotNetBot.razor
@@ -0,0 +1,162 @@
+@page "/DotNetBot"
+@using KristofferStrube.Blazor.MediaCaptureStreams;
+@using KristofferStrube.Blazor.WebIDL;
+@using KristofferStrube.Blazor.WebIDL.Exceptions;
+@implements IAsyncDisposable
+@inject IJSRuntime JSRuntime
+@inject IMediaDevicesService MediaDevicesService
+WebAudio - DotNet Bot
+
.NET Bot
+
+
+ On this page we will use your microphone input and then use an analyzer to make the .NET BOT "dance" to the tune of whatever music you hear.
+
+
+
+
+
+@if (legValues.Count > 0)
+{
+
+}
+
+@if (error is { } errorMessage)
+{
+ @errorMessage
+}
+else if (mediaStreamAudioSourceNode is null)
+{
+
+}
+else
+{
+ @if (audioOptions.Count > 0)
+ {
+
+
+ }
+}
+
+@code {
+ AudioContext audioContext = default!;
+ string? error;
+ bool stopped;
+
+ private byte[] frequencies = Array.Empty();
+
+ Queue legValues = new();
+ byte arms = 0;
+
+ double CalcLeftLeg => Math.Round(legValues.Count > 0 ? ((legValues.Take(5).Average() - legValues.Min()) / (legValues.Max() - (float)legValues.Min())) * 255 : 1);
+
+ double CalcRightLeg => Math.Round(legValues.Count > 0 ? (1 - ((legValues.Take(5).Average() - legValues.Min()) / (legValues.Max() - (float)legValues.Min()))) * 255 : 1);
+
+ MediaDevices? mediaDevices;
+ MediaStream? mediaStream;
+ MediaStreamAudioSourceNode? mediaStreamAudioSourceNode;
+ List<(string label, string id)> audioOptions = new();
+ string? selectedAudioSource;
+
+ async Task OpenAudio()
+ {
+ await StopAudioTrack();
+
+ try
+ {
+ if (audioContext is null)
+ {
+ audioContext = await AudioContext.CreateAsync(JSRuntime);
+ }
+ if (mediaDevices is null)
+ {
+ mediaDevices = await MediaDevicesService.GetMediaDevicesAsync();
+ }
+
+ MediaTrackConstraints mediaTrackConstraints = new MediaTrackConstraints
+ {
+ EchoCancellation = false,
+ NoiseSuppression = false,
+ AutoGainControl = false,
+ DeviceId = selectedAudioSource is null ? null : new ConstrainDomString(selectedAudioSource)
+ };
+ mediaStream = await mediaDevices.GetUserMediaAsync(new MediaStreamConstraints() { Audio = mediaTrackConstraints });
+
+ var deviceInfos = await mediaDevices.EnumerateDevicesAsync();
+ audioOptions.Clear();
+ foreach (var device in deviceInfos)
+ {
+ if (await device.GetKindAsync() is MediaDeviceKind.AudioInput)
+ {
+ audioOptions.Add((await device.GetLabelAsync(), await device.GetDeviceIdAsync()));
+ }
+ }
+
+ mediaStreamAudioSourceNode = await audioContext.CreateMediaStreamSourceAsync(mediaStream);
+
+ var analyserNode = await audioContext.CreateAnalyserAsync();
+
+ await mediaStreamAudioSourceNode.ConnectAsync(analyserNode);
+
+ await Task.Delay(500);
+
+ int bufferLength = (int)await analyserNode.GetFrequencyBinCountAsync();
+ var dataArray = await Uint8Array.CreateAsync(JSRuntime, bufferLength);
+
+ for (int i = 0; i < 200; i++)
+ {
+ legValues.Enqueue(0);
+ }
+
+ stopped = false;
+ while (!stopped)
+ {
+ await analyserNode.GetByteFrequencyDataAsync(dataArray);
+
+ frequencies = await dataArray.GetAsArrayAsync();
+ legValues.Enqueue(frequencies[20]);
+ legValues.Dequeue();
+ await Task.Delay(10);
+ StateHasChanged();
+ }
+ }
+ catch (Exception ex)
+ {
+ error = $"{ex.GetType().Name}: {ex.Message}";
+ }
+ }
+
+ async Task StopAudioTrack()
+ {
+ stopped = true;
+ if (mediaStream is null) return;
+ var audioTrack = (await mediaStream.GetAudioTracksAsync()).FirstOrDefault();
+ if (audioTrack is not null)
+ {
+ await audioTrack.StopAsync();
+ }
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ await StopAudioTrack();
+ }
+}
+
+