diff --git a/code/modules/client/preference_setup/global/setting_datums.dm b/code/modules/client/preference_setup/global/setting_datums.dm
index 8fefa8c929e..1703d582a23 100644
--- a/code/modules/client/preference_setup/global/setting_datums.dm
+++ b/code/modules/client/preference_setup/global/setting_datums.dm
@@ -104,6 +104,12 @@ var/list/_client_preferences_by_type
key = "EMOTE_NOISES"
enabled_description = "Noisy"
disabled_description = "Silent"
+/datum/client_preference/whisubtle_vis
+ description = "Whi/Subtles Ghost Visible"
+ key = "WHISUBTLE_VIS"
+ enabled_description = "Visible"
+ disabled_description = "Hidden"
+ enabled_by_default = FALSE
//VOREStation Add End
/datum/client_preference/weather_sounds
description ="Weather sounds"
diff --git a/code/modules/client/preferences_vr.dm b/code/modules/client/preferences_vr.dm
index ae7801b30c0..7d90e451b60 100644
--- a/code/modules/client/preferences_vr.dm
+++ b/code/modules/client/preferences_vr.dm
@@ -1,5 +1,4 @@
-//TFF 5/8/19 - minor refactoring of this thing from 09_misc.dm to call this for preferences.
-datum/preferences
+/datum/preferences
var/show_in_directory = 1 //Show in Character Directory
var/sensorpref = 5 //Set character's suit sensor level
@@ -54,3 +53,18 @@ datum/preferences
SScharacter_setup.queue_preferences_save(prefs)
feedback_add_details("admin_verb","TEmoteNoise") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+/client/verb/toggle_ghost_quiets()
+ set name = "Whisper/Subtle Vis"
+ set category = "Preferences"
+ set desc = "Toggle ghosts viewing your subtles/whispers."
+
+ var/pref_path = /datum/client_preference/whisubtle_vis
+
+ toggle_preference(pref_path)
+
+ to_chat(src, "Ghosts will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear subtles/whispers made by you.")
+
+ SScharacter_setup.queue_preferences_save(prefs)
+
+ feedback_add_details("admin_verb","TWhisubtleVis") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index c0b28cc1e2b..f980601d392 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -363,6 +363,12 @@ proc/get_radio_key_from_channel(var/channel)
spawn(0) //Using spawns to queue all the messages for AFTER this proc is done, and stop runtimes
if(M && src) //If we still exist, when the spawn processes
+ //VOREStation Add - Ghosts don't hear whispers
+ if(whispering && !is_preference_enabled(/datum/client_preference/whisubtle_vis) && isobserver(M) && !M.client?.holder)
+ M.show_message("[src.name] [w_not_heard].", 2)
+ return
+ //VOREStation Add End
+
var/dst = get_dist(get_turf(M),get_turf(src))
if(dst <= message_range || (M.stat == DEAD && !forbid_seeing_deadchat)) //Inside normal message range, or dead with ears (handled in the view proc)
diff --git a/code/modules/mob/say_vr.dm b/code/modules/mob/say_vr.dm
index 5dbf8537452..bd81c9b347d 100644
--- a/code/modules/mob/say_vr.dm
+++ b/code/modules/mob/say_vr.dm
@@ -42,6 +42,7 @@
return
if (message)
+ var/undisplayed_message = "[src] does something too subtle for you to see."
message = encode_html_emphasis(message)
var/list/vis = get_mobs_and_objs_in_view_fast(get_turf(src),1,2) //Turf, Range, and type 2 is emote
@@ -50,8 +51,12 @@
for(var/vismob in vis_mobs)
var/mob/M = vismob
- spawn(0)
- M.show_message(message, 2)
+ if(isobserver(M) && !is_preference_enabled(/datum/client_preference/whisubtle_vis) && !M.client?.holder)
+ spawn(0)
+ M.show_message(undisplayed_message, 2)
+ else
+ spawn(0)
+ M.show_message(message, 2)
for(var/visobj in vis_objs)
var/obj/O = visobj