diff --git a/MosswartMassacre/MosswartMassacre.csproj b/MosswartMassacre/MosswartMassacre.csproj
index 49bb9f0..0304df2 100644
--- a/MosswartMassacre/MosswartMassacre.csproj
+++ b/MosswartMassacre/MosswartMassacre.csproj
@@ -347,6 +347,7 @@
+
diff --git a/MosswartMassacre/NearbyObjectsTracker.cs b/MosswartMassacre/NearbyObjectsTracker.cs
new file mode 100644
index 0000000..1d2b8cd
--- /dev/null
+++ b/MosswartMassacre/NearbyObjectsTracker.cs
@@ -0,0 +1,175 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using Decal.Adapter;
+using Decal.Adapter.Wrappers;
+using Newtonsoft.Json;
+
+namespace MosswartMassacre
+{
+ ///
+ /// On-demand tracker that polls nearby world objects and streams them
+ /// to the backend via WebSocket. Activated/deactivated by start_radar
+ /// and stop_radar commands from the browser.
+ ///
+ /// Uses System.Windows.Forms.Timer so the tick fires on the main UI
+ /// thread — DECAL COM objects are apartment-threaded (STA) and must
+ /// only be accessed from the game's main thread.
+ ///
+ public class NearbyObjectsTracker : IDisposable
+ {
+ private const int PollIntervalMs = 1000;
+
+ private readonly IPluginLogger _logger;
+ private System.Windows.Forms.Timer _timer;
+ private bool _active;
+ private bool _disposed;
+
+ public bool IsActive => _active;
+
+ public NearbyObjectsTracker(IPluginLogger logger)
+ {
+ _logger = logger;
+ }
+
+ public void Start()
+ {
+ if (_active) return;
+ _active = true;
+
+ _timer = new System.Windows.Forms.Timer();
+ _timer.Interval = PollIntervalMs;
+ _timer.Tick += OnTick;
+ _timer.Start();
+
+ _logger?.Log("[Radar] Started nearby objects tracker");
+ }
+
+ public void Stop()
+ {
+ if (!_active) return;
+ _active = false;
+
+ if (_timer != null)
+ {
+ _timer.Stop();
+ _timer.Tick -= OnTick;
+ _timer.Dispose();
+ _timer = null;
+ }
+
+ _logger?.Log("[Radar] Stopped nearby objects tracker");
+ }
+
+ private async void OnTick(object sender, EventArgs e)
+ {
+ try
+ {
+ var payload = BuildNearbyObjectsPayload();
+ if (payload == null) return;
+
+ var json = JsonConvert.SerializeObject(payload);
+ await WebSocket.SendNearbyObjectsAsync(json);
+ }
+ catch (Exception ex)
+ {
+ _logger?.Log($"[Radar] Tick error: {ex.Message}");
+ }
+ }
+
+ private object BuildNearbyObjectsPayload()
+ {
+ try
+ {
+ var playerCoords = Coordinates.Me;
+ double playerHeading = CoreManager.Current.Actions.Heading;
+ string characterName = CoreManager.Current.CharacterFilter.Name;
+ int playerId = CoreManager.Current.CharacterFilter.Id;
+
+ var objects = new List