diff --git a/code/__DEFINES/~~iris_defines/__HELPERS/global_lists.dm b/code/__DEFINES/~~iris_defines/__HELPERS/global_lists.dm
new file mode 100644
index 000000000000..80f5c2a54e5f
--- /dev/null
+++ b/code/__DEFINES/~~iris_defines/__HELPERS/global_lists.dm
@@ -0,0 +1,2 @@
+GLOBAL_LIST_EMPTY(blooper_list)
+GLOBAL_LIST_EMPTY(blooper_random_list)
diff --git a/code/__DEFINES/~~iris_defines/say.dm b/code/__DEFINES/~~iris_defines/say.dm
new file mode 100644
index 000000000000..528ec2362fc6
--- /dev/null
+++ b/code/__DEFINES/~~iris_defines/say.dm
@@ -0,0 +1,19 @@
+//BLOOPER defines
+#define BLOOPER_DEFAULT_MINPITCH 0.4
+#define BLOOPER_DEFAULT_MAXPITCH 2
+#define BLOOPER_DEFAULT_MINVARY 0.1
+#define BLOOPER_DEFAULT_MAXVARY 0.8
+#define BLOOPER_DEFAULT_MINSPEED 2
+#define BLOOPER_DEFAULT_MAXSPEED 16
+
+#define BLOOPER_SPEED_BASELINE 4 //Used to calculate delay between BLOOPERs, any BLOOPER speeds below this feature higher BLOOPER density, any speeds above feature lower BLOOPER density. Keeps BLOOPERing length consistent
+
+#define BLOOPER_MAX_BLOOPERS 24
+#define BLOOPER_MAX_TIME (1.5 SECONDS) // More or less the amount of time the above takes to process through with a BLOOPER speed of 2.
+
+#define BLOOPER_PITCH_RAND(gend) ((gend == MALE ? rand(60, 120) : (gend == FEMALE ? rand(80, 140) : rand(60,140))) / 100) //Macro for determining random pitch based off gender
+#define BLOOPER_VARIANCE_RAND (rand(BLOOPER_DEFAULT_MINVARY * 100, BLOOPER_DEFAULT_MAXVARY * 100) / 100) //Macro for randomizing BLOOPER variance to reduce the amount of copy-pasta necessary for that
+
+#define BLOOPER_DO_VARY(pitch, variance) (rand(((pitch * 100) - (variance*50)), ((pitch*100) + (variance*50))) / 100)
+
+#define BLOOPER_SOUND_FALLOFF_EXPONENT 0.5 //At lower ranges, we want the exponent to be below 1 so that whispers don't sound too awkward. At higher ranges, we want the exponent fairly high to make yelling less obnoxious
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index d862490c8c1d..98913715a1ad 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -500,6 +500,12 @@
if(NAMEOF(src, glide_size))
set_glide_size(var_value)
. = TRUE
+ //IRIS EDIT - BLOOPER
+ if(NAMEOF(src, blooper))
+ if(isfile(var_value))
+ blooper = sound(var_value) //bark() expects vocal_bark to already be a sound datum, for performance reasons. adminbus QoL!
+ . = TRUE
+ // IRIS EDIT END
if(!isnull(.))
datum_flags |= DF_VAR_EDITED
diff --git a/code/modules/admin/create_mob.dm b/code/modules/admin/create_mob.dm
index 0a8bb993df7d..f045f101de23 100644
--- a/code/modules/admin/create_mob.dm
+++ b/code/modules/admin/create_mob.dm
@@ -25,6 +25,12 @@
human.eye_color_left = random_eye_color()
human.eye_color_right = human.eye_color_left
human.skin_tone = pick(GLOB.skin_tones)
+ // IRIS EDIT
+ human.set_blooper(pick(GLOB.blooper_list))
+ human.blooper_pitch = BLOOPER_PITCH_RAND(human.gender)
+ human.blooper_pitch_range = BLOOPER_VARIANCE_RAND
+ human.blooper_speed = rand(BLOOPER_DEFAULT_MINSPEED, BLOOPER_DEFAULT_MAXSPEED)
+ // IRIS EDIT END
human.dna.species.randomize_active_underwear_only(human)
// Needs to be called towards the end to update all the UIs just set above
human.dna.initialize_dna(newblood_type = random_blood_type(), create_mutation_blocks = randomize_mutations, randomize_features = TRUE)
diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm
index d2bb24c22059..a2df5febeb9d 100644
--- a/code/modules/antagonists/changeling/changeling.dm
+++ b/code/modules/antagonists/changeling/changeling.dm
@@ -553,6 +553,13 @@
new_profile.laugh_type = target.selected_laugh?.type || /datum/laugh_type/none
//NOVA EDIT ADDITION END
+ //IRIS EDIT - Voice Bark
+ new_profile.blooper_id = target.blooper_id
+ new_profile.blooper_pitch = target.blooper_pitch
+ new_profile.blooper_speed = target.blooper_speed
+ new_profile.blooper_pitch_range = target.blooper_pitch_range
+ //IRIS EDIT END
+
// Grab skillchips they have
new_profile.skillchips = target.clone_skillchip_list(TRUE)
diff --git a/goon/LICENSE.md b/goon/LICENSE.md
new file mode 100644
index 000000000000..1c8938f315d9
--- /dev/null
+++ b/goon/LICENSE.md
@@ -0,0 +1 @@
+This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 United States License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/us/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
diff --git a/goon/README.md b/goon/README.md
new file mode 100644
index 000000000000..cae34de90841
--- /dev/null
+++ b/goon/README.md
@@ -0,0 +1,8 @@
+# Goon-ported-assets
+
+All files excluding this one you're reading right now have been most likely, taken from [goonstation's 2016 release](https://github.com/goonstation/goonstation-2016), unless stated otherwise.
+It is very likely that there are modifications to be compatible or on par with our code, however. These changes are licensed under the same license as the other goon files.
+
+## License
+
+See LICENSE.md
diff --git a/goon/sounds/blub.ogg b/goon/sounds/blub.ogg
new file mode 100644
index 000000000000..ee6de6ede445
Binary files /dev/null and b/goon/sounds/blub.ogg differ
diff --git a/goon/sounds/bottalk_1.ogg b/goon/sounds/bottalk_1.ogg
new file mode 100644
index 000000000000..b7272249989a
Binary files /dev/null and b/goon/sounds/bottalk_1.ogg differ
diff --git a/goon/sounds/bottalk_2.ogg b/goon/sounds/bottalk_2.ogg
new file mode 100644
index 000000000000..745b1a4edcc2
Binary files /dev/null and b/goon/sounds/bottalk_2.ogg differ
diff --git a/goon/sounds/bottalk_3.ogg b/goon/sounds/bottalk_3.ogg
new file mode 100644
index 000000000000..e91f6bc15678
Binary files /dev/null and b/goon/sounds/bottalk_3.ogg differ
diff --git a/goon/sounds/bottalk_4.ogg b/goon/sounds/bottalk_4.ogg
new file mode 100644
index 000000000000..3cf58966f7fa
Binary files /dev/null and b/goon/sounds/bottalk_4.ogg differ
diff --git a/goon/sounds/buwoo.ogg b/goon/sounds/buwoo.ogg
new file mode 100644
index 000000000000..45bcbd50c6b8
Binary files /dev/null and b/goon/sounds/buwoo.ogg differ
diff --git a/goon/sounds/cow.ogg b/goon/sounds/cow.ogg
new file mode 100644
index 000000000000..db6c0a63ca2d
Binary files /dev/null and b/goon/sounds/cow.ogg differ
diff --git a/goon/sounds/lizard.ogg b/goon/sounds/lizard.ogg
new file mode 100644
index 000000000000..48638b0f2365
Binary files /dev/null and b/goon/sounds/lizard.ogg differ
diff --git a/goon/sounds/pug.ogg b/goon/sounds/pug.ogg
new file mode 100644
index 000000000000..86d50225a52e
Binary files /dev/null and b/goon/sounds/pug.ogg differ
diff --git a/goon/sounds/pugg.ogg b/goon/sounds/pugg.ogg
new file mode 100644
index 000000000000..90fa070e8b75
Binary files /dev/null and b/goon/sounds/pugg.ogg differ
diff --git a/goon/sounds/radio.ogg b/goon/sounds/radio.ogg
new file mode 100644
index 000000000000..0dcb87214ff4
Binary files /dev/null and b/goon/sounds/radio.ogg differ
diff --git a/goon/sounds/radio2.ogg b/goon/sounds/radio2.ogg
new file mode 100644
index 000000000000..a9364857a533
Binary files /dev/null and b/goon/sounds/radio2.ogg differ
diff --git a/goon/sounds/radio_ai.ogg b/goon/sounds/radio_ai.ogg
new file mode 100644
index 000000000000..97eaf17417da
Binary files /dev/null and b/goon/sounds/radio_ai.ogg differ
diff --git a/goon/sounds/roach.ogg b/goon/sounds/roach.ogg
new file mode 100644
index 000000000000..1bc765fa9897
Binary files /dev/null and b/goon/sounds/roach.ogg differ
diff --git a/goon/sounds/skelly.ogg b/goon/sounds/skelly.ogg
new file mode 100644
index 000000000000..b8e1a2921a42
Binary files /dev/null and b/goon/sounds/skelly.ogg differ
diff --git a/goon/sounds/speak_1.ogg b/goon/sounds/speak_1.ogg
new file mode 100644
index 000000000000..ab8ddde4a66c
Binary files /dev/null and b/goon/sounds/speak_1.ogg differ
diff --git a/goon/sounds/speak_2.ogg b/goon/sounds/speak_2.ogg
new file mode 100644
index 000000000000..a8c9444a52f8
Binary files /dev/null and b/goon/sounds/speak_2.ogg differ
diff --git a/goon/sounds/speak_3.ogg b/goon/sounds/speak_3.ogg
new file mode 100644
index 000000000000..33ec079b84dd
Binary files /dev/null and b/goon/sounds/speak_3.ogg differ
diff --git a/goon/sounds/speak_4.ogg b/goon/sounds/speak_4.ogg
new file mode 100644
index 000000000000..6de26114aee6
Binary files /dev/null and b/goon/sounds/speak_4.ogg differ
diff --git a/modular_iris/code/modules/blooper/atoms_movable.dm b/modular_iris/code/modules/blooper/atoms_movable.dm
new file mode 100644
index 000000000000..478bdbb42270
--- /dev/null
+++ b/modular_iris/code/modules/blooper/atoms_movable.dm
@@ -0,0 +1,92 @@
+/* Originally written by Bhijn & Myr on Citadel[1], with various other contributions since- see the Splurt & Citadel github for a full list of contributors.
+It has also been further modified by Rashcat & other Fluffyfrontier contributors on Fluffy Frontier!
+[1]https://github.com/Citadel-Station-13/Citadel-Station-13/pull/15677
+*/
+/atom/movable
+ // Text-to-blooper sounds
+ // yes. all atoms can have a say.
+ var/sound/blooper
+ var/blooper_id
+ var/blooper_pitch = 1
+ var/blooper_pitch_range = 0.2 //Actual pitch is (pitch - (blooper_pitch_range*0.5)) to (pitch + (blooper_pitch_range*0.5))
+ var/blooper_volume = 50
+ var/blooper_speed = 4 //Lower values are faster, higher values are slower
+ var/blooper_current_blooper //When bloopers are queued, this gets passed to the blooper proc. If blooper_current_blooper doesn't match the args passed to the blooper proc (if passed at all), then the blooper simply doesn't play. Basic curtailing of spam~
+
+/atom/movable/proc/set_blooper(id)
+ if(!id)
+ return FALSE
+ var/datum/blooper/B = GLOB.blooper_list[id]
+ if(!B)
+ return FALSE
+ blooper = sound(initial(B.soundpath))
+ blooper_id = id
+ return blooper
+
+/atom/movable/proc/blooper(list/listeners, distance, volume, pitch, queue_time)
+ if(!GLOB.blooper_allowed)
+ return
+ if(queue_time && blooper_current_blooper != queue_time)
+ return
+ if(!blooper)
+ if(!blooper_id || !set_blooper(blooper_id)) //just-in-time blooper generation
+ return
+ volume = min(volume, 100)
+ var/turf/T = get_turf(src)
+ for(var/mob/M in listeners)
+ M.playsound_local(T, vol = volume, vary = TRUE, frequency = pitch, max_distance = distance, falloff_distance = 0, falloff_exponent = BLOOPER_SOUND_FALLOFF_EXPONENT, sound_to_use = blooper, distance_multiplier = 1)
+
+/atom/movable/send_speech(message, range = 7, obj/source = src, bubble_type, list/spans, datum/language/message_language, list/message_mods = list(), forced = FALSE, tts_message, list/tts_filter)
+ . = ..()
+ var/list/listeners = get_hearers_in_view(range, source)
+ if(blooper || blooper_id)
+ for(var/mob/M in listeners)
+ if(!M.client)
+ continue
+ if(!(M.client.prefs?.read_preference(/datum/preference/toggle/hear_sound_blooper)))
+ listeners -= M
+ var/bloopers = min(round((LAZYLEN(message) / blooper_speed)) + 1, BLOOPER_MAX_BLOOPERS)
+ var/total_delay
+ blooper_current_blooper = world.time //this is juuuuust random enough to reliably be unique every time send_speech() is called, in most scenarios
+ for(var/i in 1 to bloopers)
+ if(total_delay > BLOOPER_MAX_TIME)
+ break
+ addtimer(CALLBACK(src, PROC_REF(blooper), listeners, range, blooper_volume, BLOOPER_DO_VARY(blooper_pitch, blooper_pitch_range), blooper_current_blooper), total_delay)
+ total_delay += rand(DS2TICKS(blooper_speed / BLOOPER_SPEED_BASELINE), DS2TICKS(blooper_speed / BLOOPER_SPEED_BASELINE) + DS2TICKS(blooper_speed / BLOOPER_SPEED_BASELINE)) TICKS
+
+/mob/living/carbon/human/Initialize(mapload)
+ . = ..()
+ // This gives a random vocal bark to a random created person
+ if(!client)
+ set_blooper(pick(GLOB.blooper_list))
+ blooper_pitch = BLOOPER_PITCH_RAND(gender)
+ blooper_pitch_range = BLOOPER_VARIANCE_RAND
+ blooper_speed = rand(BLOOPER_DEFAULT_MINSPEED, BLOOPER_DEFAULT_MAXSPEED)
+
+/mob/living/send_speech(message_raw, message_range = 6, obj/source = src, bubble_type = bubble_icon, list/spans, datum/language/message_language = null, list/message_mods = list(), forced = null, tts_message, list/tts_filter)
+ . = ..()
+ blooper_volume = client?.prefs.read_preference(/datum/preference/numeric/sound_blooper_volume) //volume scales with your volume slider in game preferences.
+ if(HAS_TRAIT(src, TRAIT_SIGN_LANG) && !HAS_TRAIT(src, TRAIT_MUTE)) //if you can speak and you sign, your hands don't make a bark. Unless you are completely mute, you can have some hand bark.
+ return
+ if(message_mods[WHISPER_MODE])
+ blooper_volume = (client?.prefs.read_preference(/datum/preference/numeric/sound_blooper_volume)*0.5) //Whispered barked are half as loud.
+ message_range++
+ var/list/listening = get_hearers_in_view(message_range, source)
+ var/is_yell = (say_test(message_raw) == "2")
+ //Listening gets trimmed here if a blooper blooper's present. If anyone ever makes this proc return listening, make sure to instead initialize a copy of listening in here to avoid wonkiness
+ if(blooper || blooper_id)
+ for(var/mob/M in listening)
+ if(!M.client)
+ continue
+ if(!(M.client.prefs?.read_preference(/datum/preference/toggle/hear_sound_blooper)))
+ listening -= M
+ var/bloopers = min(round((LAZYLEN(message_raw) / blooper_speed)) + 1, BLOOPER_MAX_BLOOPERS)
+ var/total_delay
+ blooper_current_blooper = world.time
+ for(var/i in 1 to bloopers)
+ if(total_delay > BLOOPER_MAX_TIME)
+ break
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/atom/movable, blooper), listening, message_range + 1, (blooper_volume * (is_yell ? 2 : 1)), BLOOPER_DO_VARY(blooper_pitch, blooper_pitch_range), blooper_current_blooper), total_delay) //The function is zero on the seventh tile. This makes it a maximum of 1 more.
+ total_delay += rand(DS2TICKS(blooper_speed / BLOOPER_SPEED_BASELINE), DS2TICKS(blooper_speed / BLOOPER_SPEED_BASELINE) + DS2TICKS((blooper_speed / BLOOPER_SPEED_BASELINE) * (is_yell ? 0.5 : 1))) TICKS
+
+
diff --git a/modular_iris/code/modules/blooper/bark.dm b/modular_iris/code/modules/blooper/bark.dm
new file mode 100644
index 000000000000..ed7872762547
--- /dev/null
+++ b/modular_iris/code/modules/blooper/bark.dm
@@ -0,0 +1,164 @@
+GLOBAL_VAR_INIT(blooper_allowed, TRUE) // For administrators
+
+// we let borgs have some bark too
+/mob/living/silicon/Login()
+ // This is the only found function that updates the client for borgs.
+ set_blooper(client.prefs.read_preference(/datum/preference/choiced/blooper))
+ blooper_pitch = client.prefs.read_preference(/datum/preference/numeric/blooper_speech_pitch)
+ blooper_speed = client.prefs.read_preference(/datum/preference/numeric/blooper_speech_speed)
+ blooper_pitch_range = client.prefs.read_preference(/datum/preference/numeric/blooper_pitch_range)
+ . = ..()
+
+// Mechanics for Changelings
+/datum/changeling_profile
+ var/blooper_id
+ var/blooper_pitch
+ var/blooper_pitch_range
+ var/blooper_speed
+
+/datum/smite/normalblooper
+ name = "Normal blooper"
+
+/datum/smite/normalblooper/effect(client/user, mob/living/carbon/human/target)
+ . = ..()
+ target.blooper = null
+ target.blooper_id = pick(GLOB.blooper_random_list)
+ target.blooper_speed = round((BLOOPER_DEFAULT_MINSPEED + BLOOPER_DEFAULT_MAXSPEED) / 2)
+ target.blooper_pitch = round((BLOOPER_DEFAULT_MINPITCH + BLOOPER_DEFAULT_MAXPITCH) / 2)
+ target.blooper_pitch_range = 0.2
+
+/datum/admins/proc/toggleblooper()
+ set category = "Server"
+ set desc = "Toggle the annoying voices."
+ set name = "Toggle Character Voices"
+ toggle_blooper()
+ log_admin("[key_name(usr)] toggled Voice Barks.")
+ message_admins("[key_name_admin(usr)] toggled Voice Barks.")
+ SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Voice Bark", "[GLOB.blooper_allowed ? "Enabled" : "Disabled"]")) // If you are copy-pasting this, ensure the 4th parameter is unique to the new proc!
+
+/* /world/AVerbsAdmin()
+ . = ..()
+ return . + /datum/admins/proc/toggleblooper */
+
+/proc/toggle_blooper(toggle = null)
+ if(toggle != null)
+ if(toggle != GLOB.blooper_allowed)
+ GLOB.blooper_allowed = toggle
+ else
+ return
+ else
+ GLOB.blooper_allowed = !GLOB.blooper_allowed
+ to_chat(world, "Vocal barks have been globally [GLOB.blooper_allowed ? "enabled" : "disabled"].")
+
+/datum/preference/choiced/blooper
+ category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
+ savefile_identifier = PREFERENCE_CHARACTER
+ savefile_key = "blooper_speech"
+
+/datum/preference/choiced/blooper/init_possible_values()
+ return assoc_to_keys(GLOB.blooper_list)
+
+/datum/preference/choiced/blooper/apply_to_human(mob/living/carbon/human/target, value)
+ target.set_blooper(value)
+
+/datum/preference_middleware/blooper
+ /// Cooldown on requesting a Blooper preview.
+ COOLDOWN_DECLARE(blooper_cooldown)
+
+ action_delegations = list(
+ "play_blooper" = PROC_REF(play_blooper),
+ )
+
+/datum/preference_middleware/blooper/proc/play_blooper(list/params, mob/user)
+ if(!COOLDOWN_FINISHED(src, blooper_cooldown))
+ return TRUE
+ var/atom/movable/blooperbox = new(get_turf(user))
+ blooperbox.set_blooper(preferences.read_preference(/datum/preference/choiced/blooper))
+ blooperbox.blooper_pitch = preferences.read_preference(/datum/preference/numeric/blooper_speech_pitch)
+ blooperbox.blooper_speed = preferences.read_preference(/datum/preference/numeric/blooper_speech_speed)
+ blooperbox.blooper_pitch_range = preferences.read_preference(/datum/preference/numeric/blooper_pitch_range)
+ var/total_delay
+ for(var/i in 1 to (round((32 / blooperbox.blooper_speed)) + 1))
+ addtimer(CALLBACK(blooperbox, TYPE_PROC_REF(/atom/movable, blooper), list(user), 7, 70, BLOOPER_DO_VARY(blooperbox.blooper_pitch, blooperbox.blooper_pitch_range)), total_delay)
+ total_delay += rand(DS2TICKS(blooperbox.blooper_speed/4), DS2TICKS(blooperbox.blooper_speed/4) + DS2TICKS(blooperbox.blooper_speed/4)) TICKS
+ QDEL_IN(blooperbox, total_delay)
+ COOLDOWN_START(src, blooper_cooldown, 2 SECONDS)
+ return TRUE
+
+/datum/preference/numeric/blooper_speech_speed
+ category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
+ savefile_identifier = PREFERENCE_CHARACTER
+ savefile_key = "blooper_speech_speed"
+ minimum = BLOOPER_DEFAULT_MINSPEED
+ maximum = BLOOPER_DEFAULT_MAXSPEED
+ step = 0.01
+
+/datum/preference/numeric/blooper_speech_speed/apply_to_human(mob/living/carbon/human/target, value)
+ target.blooper_speed = value
+
+/datum/preference/numeric/blooper_speech_speed/create_default_value()
+ return round((BLOOPER_DEFAULT_MINSPEED + BLOOPER_DEFAULT_MAXSPEED) / 2)
+
+/datum/preference/numeric/blooper_speech_pitch
+ category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
+ savefile_identifier = PREFERENCE_CHARACTER
+ savefile_key = "blooper_speech_pitch"
+ minimum = BLOOPER_DEFAULT_MINPITCH
+ maximum = BLOOPER_DEFAULT_MAXPITCH
+ step = 0.01
+
+/datum/preference/numeric/blooper_speech_pitch/apply_to_human(mob/living/carbon/human/target, value)
+ target.blooper_pitch = value
+
+/datum/preference/numeric/blooper_speech_pitch/create_default_value()
+ return round((BLOOPER_DEFAULT_MINPITCH + BLOOPER_DEFAULT_MAXPITCH) / 2)
+
+/datum/preference/numeric/blooper_pitch_range
+ category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
+ savefile_identifier = PREFERENCE_CHARACTER
+ savefile_key = "blooper_pitch_range"
+ minimum = BLOOPER_DEFAULT_MINVARY
+ maximum = BLOOPER_DEFAULT_MAXVARY
+ step = 0.01
+
+/datum/preference/numeric/blooper_pitch_range/apply_to_human(mob/living/carbon/human/target, value)
+ target.blooper_pitch_range = value
+
+/datum/preference/numeric/blooper_pitch_range/create_default_value()
+ return 0.2
+
+/// Can I hear everyone else's bloops?
+/datum/preference/toggle/hear_sound_blooper
+ category = PREFERENCE_CATEGORY_GAME_PREFERENCES
+ savefile_key = "hear_sound_blooper"
+ savefile_identifier = PREFERENCE_PLAYER
+ default_value = TRUE
+
+/// Can I have a slider to adjust the volume of the barks?
+/datum/preference/numeric/sound_blooper_volume
+ category = PREFERENCE_CATEGORY_GAME_PREFERENCES
+ savefile_key = "sound_blooper_volume"
+ savefile_identifier = PREFERENCE_PLAYER
+ minimum = 0
+ maximum = 60
+ step = 5
+
+/// Thank you SPLURT, FluffySTG and Citadel
+/datum/blooper
+ var/name = "None"
+ var/id = "No Voice"
+ var/soundpath
+
+ var/minpitch = BLOOPER_DEFAULT_MINPITCH
+ var/maxpitch = BLOOPER_DEFAULT_MAXPITCH
+ var/minvariance = BLOOPER_DEFAULT_MINVARY
+ var/maxvariance = BLOOPER_DEFAULT_MAXVARY
+
+ // Speed vars. Speed determines the number of characters required for each blooper, with lower speeds being faster with higher blooper density
+ var/minspeed = BLOOPER_DEFAULT_MINSPEED
+ var/maxspeed = BLOOPER_DEFAULT_MAXSPEED
+
+ // Visibility vars. Regardless of what's set below, these can still be obtained via adminbus and genetics. Rule of fun.
+ var/list/ckeys_allowed
+ var/ignore = FALSE // If TRUE - only for admins
+ var/allow_random = FALSE
diff --git a/modular_iris/code/modules/blooper/bark_list.dm b/modular_iris/code/modules/blooper/bark_list.dm
new file mode 100644
index 000000000000..6fb3d8469c55
--- /dev/null
+++ b/modular_iris/code/modules/blooper/bark_list.dm
@@ -0,0 +1,192 @@
+/datum/blooper/mutedc2
+ name = "Muted String (Low)"
+ id = "mutedc2"
+ soundpath = 'sound/runtime/instruments/synthesis_samples/guitar/crisis_muted/C2.ogg'
+ allow_random = TRUE
+
+/datum/blooper/mutedc3
+ name = "Muted String (Medium)"
+ id = "mutedc3"
+ soundpath = 'sound/runtime/instruments/synthesis_samples/guitar/crisis_muted/C3.ogg'
+ allow_random = TRUE
+
+/datum/blooper/mutedc4
+ name = "Muted String (High)"
+ id = "mutedc4"
+ soundpath = 'sound/runtime/instruments/synthesis_samples/guitar/crisis_muted/C4.ogg'
+ allow_random = TRUE
+
+/datum/blooper/banjoc3
+ name = "Banjo (Medium)"
+ id = "banjoc3"
+ soundpath = 'sound/runtime/instruments/banjo/Cn3.ogg'
+ allow_random = TRUE
+
+/datum/blooper/banjoc4
+ name = "Banjo (High)"
+ id = "banjoc4"
+ soundpath = 'sound/runtime/instruments/banjo/Cn4.ogg'
+ allow_random = TRUE
+
+/datum/blooper/squeaky
+ name = "Squeaky"
+ id = "squeak"
+ soundpath = 'sound/items/toysqueak1.ogg'
+ maxspeed = 4
+
+/datum/blooper/beep
+ name = "Beepy"
+ id = "beep"
+ soundpath = 'sound/machines/terminal_select.ogg'
+ maxpitch = 1 //Bringing the pitch higher just hurts your ears :<
+ maxspeed = 4 //This soundbyte's too short for larger speeds to not sound awkward
+
+/datum/blooper/synthetic_grunt
+ name = "Synthetic (Grunt)"
+ id = "synthgrunt"
+ soundpath = 'sound/misc/bloop.ogg'
+
+/datum/blooper/synthetic
+ name = "Synthetic (Normal)"
+ id = "synth"
+ soundpath = 'sound/machines/uplinkerror.ogg'
+
+/datum/blooper/bullet
+ name = "Windy"
+ id = "bullet"
+ maxpitch = 1.6
+ soundpath = 'sound/weapons/bulletflyby.ogg'
+
+/datum/blooper/coggers
+ name = "Brassy"
+ id = "coggers"
+ soundpath = 'sound/machines/clockcult/integration_cog_install.ogg'
+
+/datum/blooper/blub
+ name = "Blub"
+ id = "blub"
+ soundpath = 'goon/sounds/blub.ogg'
+
+/datum/blooper/bottalk
+ name = "Bottalk 1"
+ id = "bottalk1"
+ soundpath = 'goon/sounds/bottalk_1.ogg'
+ minspeed = 3
+ maxspeed = 9
+
+/datum/blooper/bottalk/alt1
+ name = "Bottalk 2"
+ id = "bottalk2"
+ soundpath = 'goon/sounds/bottalk_2.ogg'
+
+/datum/blooper/bottalk/alt2
+ name = "Bottalk 3"
+ id = "bottalk3"
+ soundpath = 'goon/sounds/bottalk_3.ogg'
+
+/datum/blooper/bottalk/alt3
+ name = "Bottalk 4"
+ id = "bottalk4"
+ soundpath = 'goon/sounds/bottalk_4.ogg'
+
+/datum/blooper/buwoo
+ name = "Buwoo"
+ id = "buwoo"
+ soundpath = 'goon/sounds/buwoo.ogg'
+
+/datum/blooper/cow
+ name = "Cow"
+ id = "cow"
+ soundpath = 'goon/sounds/cow.ogg'
+
+/datum/blooper/lizard
+ name = "Lizard"
+ id = "lizard"
+ soundpath = 'goon/sounds/lizard.ogg'
+
+/datum/blooper/pug
+ name = "Pug"
+ id = "pug"
+ soundpath = 'goon/sounds/pug.ogg'
+
+/datum/blooper/pugg
+ name = "Pugg"
+ id = "pugg"
+ soundpath = 'goon/sounds/pugg.ogg'
+
+/datum/blooper/radio
+ name = "Radio 1"
+ id = "radio1"
+ soundpath = 'goon/sounds/radio.ogg'
+
+/datum/blooper/radio/short
+ name = "Radio 2"
+ id = "radio2"
+ soundpath = 'goon/sounds/radio2.ogg'
+
+/datum/blooper/radio/ai
+ name = "Radio (AI)"
+ id = "radio_ai"
+ soundpath = 'goon/sounds/radio_ai.ogg'
+
+/datum/blooper/roach
+ name = "Roach"
+ id = "roach"
+ soundpath = 'goon/sounds/roach.ogg'
+
+/datum/blooper/skelly
+ name = "Skelly"
+ id = "skelly"
+ soundpath = 'goon/sounds/skelly.ogg'
+
+/datum/blooper/speak
+ name = "Speak 1"
+ id = "speak1"
+ soundpath = 'goon/sounds/speak_1.ogg'
+
+/datum/blooper/speak/alt1
+ name = "Speak 2"
+ id = "speak2"
+ soundpath = 'goon/sounds/speak_2.ogg'
+
+/datum/blooper/speak/alt2
+ name = "Speak 3"
+ id = "speak3"
+ soundpath = 'goon/sounds/speak_3.ogg'
+
+/datum/blooper/speak/alt3
+ name = "Speak 4"
+ id = "speak4"
+ soundpath = 'goon/sounds/speak_4.ogg'
+
+/datum/blooper/moff/short
+ name = "Moth Squeak"
+ id = "moffsqueak"
+ soundpath = 'modular_iris/code/modules/blooper/voice/bloopers/mothsqueak.ogg'
+ allow_random = TRUE
+ ignore = FALSE
+
+/datum/blooper/meow //Meow blooper?
+ name = "Meow"
+ id = "meow"
+ allow_random = TRUE
+ soundpath = 'modular_iris/code/modules/blooper/voice/bloopers/meow1.ogg'
+ minspeed = 5
+ maxspeed = 11
+
+/datum/blooper/chirp
+ name = "Chirp"
+ id = "chirp"
+ allow_random = TRUE
+ soundpath = 'modular_iris/code/modules/blooper/voice/bloopers/chirp.ogg'
+
+/datum/blooper/caw
+ name = "Caw"
+ id = "caw"
+ allow_random = TRUE
+ soundpath = 'modular_iris/code/modules/blooper/voice/bloopers/caw.ogg'
+
+/datum/blooper/chitter/alt
+ name = "Chittery"
+ id = "chitter2"
+ soundpath = 'modular_iris/code/modules/blooper/voice/bloopers/moth/mothchitter2.ogg'
diff --git a/modular_iris/code/modules/blooper/voice/bloopers/caw.ogg b/modular_iris/code/modules/blooper/voice/bloopers/caw.ogg
new file mode 100644
index 000000000000..a560d00908ca
Binary files /dev/null and b/modular_iris/code/modules/blooper/voice/bloopers/caw.ogg differ
diff --git a/modular_iris/code/modules/blooper/voice/bloopers/chirp.ogg b/modular_iris/code/modules/blooper/voice/bloopers/chirp.ogg
new file mode 100644
index 000000000000..73cb0376d215
Binary files /dev/null and b/modular_iris/code/modules/blooper/voice/bloopers/chirp.ogg differ
diff --git a/modular_iris/code/modules/blooper/voice/bloopers/chitter.ogg b/modular_iris/code/modules/blooper/voice/bloopers/chitter.ogg
new file mode 100644
index 000000000000..0d162fa30352
Binary files /dev/null and b/modular_iris/code/modules/blooper/voice/bloopers/chitter.ogg differ
diff --git a/modular_iris/code/modules/blooper/voice/bloopers/meow1.ogg b/modular_iris/code/modules/blooper/voice/bloopers/meow1.ogg
new file mode 100644
index 000000000000..be9393d7d6e7
Binary files /dev/null and b/modular_iris/code/modules/blooper/voice/bloopers/meow1.ogg differ
diff --git a/modular_iris/code/modules/blooper/voice/bloopers/moth/mothchitter2.ogg b/modular_iris/code/modules/blooper/voice/bloopers/moth/mothchitter2.ogg
new file mode 100644
index 000000000000..07619ba1437f
Binary files /dev/null and b/modular_iris/code/modules/blooper/voice/bloopers/moth/mothchitter2.ogg differ
diff --git a/modular_iris/code/modules/blooper/voice/bloopers/mothsqueak.ogg b/modular_iris/code/modules/blooper/voice/bloopers/mothsqueak.ogg
new file mode 100644
index 000000000000..5a345df09d2b
Binary files /dev/null and b/modular_iris/code/modules/blooper/voice/bloopers/mothsqueak.ogg differ
diff --git a/modular_nova/modules/customization/__HELPERS/global_lists.dm b/modular_nova/modules/customization/__HELPERS/global_lists.dm
index 143eba03b26a..b9584b0cb5e8 100644
--- a/modular_nova/modules/customization/__HELPERS/global_lists.dm
+++ b/modular_nova/modules/customization/__HELPERS/global_lists.dm
@@ -20,6 +20,13 @@
var/datum/laugh_type/L = new spath()
GLOB.laugh_types[L.name] = spath
sort_list(GLOB.laugh_types, GLOBAL_PROC_REF(cmp_typepaths_asc))
+ //IRIS EDIT ADDITION
+ for(var/sound_blooper_path in subtypesof(/datum/blooper))
+ var/datum/blooper/B = new sound_blooper_path()
+ GLOB.blooper_list[B.id] = sound_blooper_path
+ if(B.allow_random)
+ GLOB.blooper_random_list[B.id] = sound_blooper_path
+ //IRIS EDIT END
/proc/make_default_mutant_bodypart_references()
// Build the global list for default species' mutant_bodyparts
diff --git a/tgstation.dme b/tgstation.dme
index 67367a9c9a18..d55e33777dde 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -489,6 +489,8 @@
#include "code\__DEFINES\~nova_defines\_HELPERS\lighting.dm"
#include "code\__DEFINES\~nova_defines\_HELPERS\offset_index.dm"
#include "code\__DEFINES\~nova_defines\traits\declarations.dm"
+#include "code\__DEFINES\~~iris_defines\say.dm"
+#include "code\__DEFINES\~~iris_defines\__HELPERS\global_lists.dm"
#include "code\__HELPERS\_auxtools_api.dm"
#include "code\__HELPERS\_dreamluau.dm"
#include "code\__HELPERS\_lists.dm"
@@ -6460,6 +6462,9 @@
#include "interface\fonts\spess_font.dm"
#include "interface\fonts\tiny_unicode.dm"
#include "interface\fonts\vcr_osd_mono.dm"
+#include "modular_iris\code\modules\blooper\atoms_movable.dm"
+#include "modular_iris\code\modules\blooper\bark.dm"
+#include "modular_iris\code\modules\blooper\bark_list.dm"
#include "modular_iris\code\modules\mob\dead\new_player\sprite_accessories.dm"
#include "modular_iris\maps\biodome\area.dm"
#include "modular_iris\maps\biodome\beach.dm"
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/iris/character_voice.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/iris/character_voice.tsx
new file mode 100644
index 000000000000..0ee25c85b668
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/iris/character_voice.tsx
@@ -0,0 +1,74 @@
+import { Button, Stack } from '../../../../../../components';
+import {
+ CheckboxInput,
+ Feature,
+ FeatureChoiced,
+ FeatureChoicedServerData,
+ FeatureNumberInput,
+ FeatureNumeric,
+ FeatureSliderInput,
+ FeatureToggle,
+ FeatureValueProps,
+} from '../../base';
+import { FeatureDropdownInput } from '../../dropdowns';
+
+const FeatureBlooperDropdownInput = (
+ props: FeatureValueProps,
+) => {
+ return (
+
+
+
+
+
+
+
+ );
+};
+
+export const blooper_pitch_range: FeatureNumeric = {
+ name: 'Character Voice Range',
+ description:
+ '[0.1 - 0.8] Lower number, less range. Higher number, more range.',
+ component: FeatureNumberInput,
+};
+
+export const blooper_speech: FeatureChoiced = {
+ name: 'Character Voice',
+ component: FeatureBlooperDropdownInput,
+};
+
+export const blooper_speech_speed: FeatureNumeric = {
+ name: 'Character Voice Speed',
+ description:
+ '[2 - 16] Lower number, faster speed. Higher number, slower voice.',
+ component: FeatureNumberInput,
+};
+
+export const blooper_speech_pitch: FeatureNumeric = {
+ name: 'Character Voice Pitch',
+ description:
+ '[0.4 - 2] Lower number, deeper pitch. Higher number, higher pitch.',
+ component: FeatureNumberInput,
+};
+
+export const hear_sound_blooper: FeatureToggle = {
+ name: 'Enable Character Voice hearing',
+ category: 'SOUND',
+ component: CheckboxInput,
+};
+
+export const sound_blooper_volume: Feature = {
+ name: 'Character Voice Volume',
+ category: 'SOUND',
+ description: 'The volume that the Vocal Barks sounds will play at.',
+ component: FeatureSliderInput,
+};