diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm
index 0db33374ecf..5fc6a3614cf 100644
--- a/code/game/objects/items/devices/traitordevices.dm
+++ b/code/game/objects/items/devices/traitordevices.dm
@@ -465,3 +465,58 @@
GLOB.mirrors -= src
QDEL_NULL(appearance_changer_holder)
return ..()
+
+/// An admin-spawn item that will tell you roughly how close the nearest loyal Nanotrasen crewmember is.
+/obj/item/syndi_scanner
+ name = "syndicate scanner"
+ desc = "The Syndicate seem to have modified this T-ray scanner for a more nefarious purpose, allowing it to detect all loyal Nanotrasen crew."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "syndi-scanner"
+ throwforce = 5
+ w_class = WEIGHT_CLASS_SMALL
+ throw_speed = 4
+ throw_range = 10
+ flags = CONDUCT
+ item_state = "electronic"
+ /// Split points for range_messages.
+ var/list/ranges = list(5, 15, 30)
+ /// Messages to output to the user.
+ var/list/range_messages = list(
+ "Very strong signal detected. Range: Within 5 meters.",
+ "Strong signal detected. Range: Within 15 meters.",
+ "Weak signal detected. Range: Within 30 meters.",
+ "No signal detected."
+ )
+ var/cooldown_length = 10 SECONDS
+ COOLDOWN_DECLARE(scan_cooldown)
+ var/on_hit_sound = 'sound/effects/ping_hit.ogg'
+
+/obj/item/syndi_scanner/attack_self(mob/user)
+ if(!COOLDOWN_FINISHED(src, scan_cooldown))
+ to_chat(user, "[src] is recharging!")
+ return
+
+ COOLDOWN_START(src, scan_cooldown, cooldown_length)
+ var/turf/user_turf = get_turf(user)
+ var/min_dist = INFINITY
+ for(var/mob/living/player in GLOB.player_list)
+ if(player.stat == DEAD || isnull(player.mind))
+ continue
+ if(!isnull(player.mind.special_role))
+ continue
+ var/turf/target_turf = get_turf(player)
+ if(target_turf.z != user_turf.z)
+ continue
+ min_dist = min(min_dist, get_dist(target_turf, user_turf))
+
+ // By default, we're in the first range, less than any split point.
+ var/range_index = 1
+ for(var/test_range in ranges)
+ if(min_dist > test_range)
+ // Past this split point, move to the next.
+ range_index++
+ else
+ // Found the right split point, and we're not past all of them, so play the on-hit sound effect.
+ playsound(user, on_hit_sound, 75, TRUE)
+ break
+ to_chat(user, "[range_messages[range_index]]")
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index 0d83d8d9816..c64f237b91b 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ