diff --git a/addons/dialogic/Core/DialogicGameHandler.gd b/addons/dialogic/Core/DialogicGameHandler.gd
index a6e6e0303..30ba65255 100644
--- a/addons/dialogic/Core/DialogicGameHandler.gd
+++ b/addons/dialogic/Core/DialogicGameHandler.gd
@@ -138,6 +138,9 @@ var Portraits := preload("res://addons/dialogic/Modules/Character/subsystem_port
 var Save := preload("res://addons/dialogic/Modules/Save/subsystem_save.gd").new():
 	get: return get_subsystem("Save")
 
+var ScreenShake := preload("res://addons/dialogic/Modules/ScreenShake/subsystem_screen_shake.gd").new():
+	get: return get_subsystem("ScreenShake")
+
 var Settings := preload("res://addons/dialogic/Modules/Settings/subsystem_settings.gd").new():
 	get: return get_subsystem("Settings")
 
diff --git a/addons/dialogic/Modules/Clear/clear_shake.svg b/addons/dialogic/Modules/Clear/clear_shake.svg
new file mode 100644
index 000000000..e413ff7e1
--- /dev/null
+++ b/addons/dialogic/Modules/Clear/clear_shake.svg
@@ -0,0 +1,76 @@
+
+
diff --git a/addons/dialogic/Modules/Clear/clear_shake.svg.import b/addons/dialogic/Modules/Clear/clear_shake.svg.import
new file mode 100644
index 000000000..1ec3d43f5
--- /dev/null
+++ b/addons/dialogic/Modules/Clear/clear_shake.svg.import
@@ -0,0 +1,38 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bw5d6gq4xj4fc"
+path="res://.godot/imported/clear_shake.svg-7755f0aa1227e0b25e8ad6fa6ac6cbe5.ctex"
+metadata={
+"has_editor_variant": true,
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/dialogic/Modules/Clear/clear_shake.svg"
+dest_files=["res://.godot/imported/clear_shake.svg-7755f0aa1227e0b25e8ad6fa6ac6cbe5.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=true
+editor/convert_colors_with_editor_theme=true
diff --git a/addons/dialogic/Modules/Clear/event_clear.gd b/addons/dialogic/Modules/Clear/event_clear.gd
index 9b1b3d7aa..c59248ff6 100644
--- a/addons/dialogic/Modules/Clear/event_clear.gd
+++ b/addons/dialogic/Modules/Clear/event_clear.gd
@@ -12,6 +12,7 @@ var clear_textbox := true
 var clear_portraits := true
 var clear_style := true
 var clear_music := true
+var clear_screen_shake := true
 var clear_portrait_positions := true
 var clear_background := true
 
@@ -49,6 +50,11 @@ func _execute() -> void:
 			dialogic.Audio.stop_all_channels(final_time)
 			if step_by_step: await dialogic.get_tree().create_timer(final_time).timeout
 
+	if clear_screen_shake and dialogic.has_subsystem('ScreenShake'):
+		dialogic.ScreenShake.update_shake_x(0.0, 0.0, final_time)
+		dialogic.ScreenShake.update_shake_y(0.0, 0.0, final_time)
+		if step_by_step: await dialogic.get_tree().create_timer(final_time).timeout
+
 	if clear_style and dialogic.has_subsystem('Styles'):
 		dialogic.Styles.change_style()
 
@@ -88,6 +94,7 @@ func get_shortcode_parameters() -> Dictionary:
 		"text"		: {"property": "clear_textbox",		"default": true},
 		"portraits"	: {"property": "clear_portraits", 	"default": true},
 		"music"		: {"property": "clear_music", 		"default": true},
+		"shake"		: {"property": "clear_screen_shake",	"default": true},
 		"background": {"property": "clear_background", 	"default": true},
 		"positions"	: {"property": "clear_portrait_positions", 	"default": true},
 		"style"		: {"property": "clear_style", 		"default": true},
@@ -110,5 +117,6 @@ func build_event_editor() -> void:
 	add_body_edit('clear_portraits', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_characters.svg"), 'tooltip':'Clear Portraits'})
 	add_body_edit('clear_background', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_background.svg"), 'tooltip':'Clear Background'})
 	add_body_edit('clear_music', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_music.svg"), 'tooltip':'Clear Audio'})
+	add_body_edit('clear_screen_shake', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_shake.svg"), 'tooltip':'Clear Screen Shake'})
 	add_body_edit('clear_style', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_style.svg"), 'tooltip':'Clear Style'})
 	add_body_edit('clear_portrait_positions', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_positions.svg"), 'tooltip':'Clear Portrait Positions'})
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/icon.svg b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/icon.svg
new file mode 100644
index 000000000..4fe7970d3
--- /dev/null
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/icon.svg
@@ -0,0 +1,58 @@
+
+
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/icon.svg.import b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/icon.svg.import
new file mode 100644
index 000000000..8b76f35dc
--- /dev/null
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/icon.svg.import
@@ -0,0 +1,38 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dp28d3waw6sh8"
+path="res://.godot/imported/icon.svg-c4786c180265dc13234ef8ceb5240800.ctex"
+metadata={
+"has_editor_variant": true,
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/icon.svg"
+dest_files=["res://.godot/imported/icon.svg-c4786c180265dc13234ef8ceb5240800.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=true
+editor/convert_colors_with_editor_theme=true
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/part_config.cfg b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/part_config.cfg
new file mode 100644
index 000000000..275f8d95c
--- /dev/null
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/part_config.cfg
@@ -0,0 +1,7 @@
+[style]
+type = "Layer"
+name = "Screen Shake Layer"
+author = "SaliaNifo"
+description = "A layer for applying screen shake to the layers below it."
+scene = "screen_shake_layer.tscn"
+icon = "icon.svg"
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/preview.png b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/preview.png
new file mode 100644
index 000000000..488729ba8
Binary files /dev/null and b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/preview.png differ
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/preview.png.import b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/preview.png.import
new file mode 100644
index 000000000..94989c8df
--- /dev/null
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/preview.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cwhucf14pvkfs"
+path="res://.godot/imported/preview.png-f4b273872c0dd56a22386a20ccb82f99.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/preview.png"
+dest_files=["res://.godot/imported/preview.png-f4b273872c0dd56a22386a20ccb82f99.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gd b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gd
new file mode 100644
index 000000000..9162300ec
--- /dev/null
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gd
@@ -0,0 +1,2 @@
+@tool
+extends DialogicLayoutLayer
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gd.uid b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gd.uid
new file mode 100644
index 000000000..8a53a3d7c
--- /dev/null
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gd.uid
@@ -0,0 +1 @@
+uid://pf00w14gv64m
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gdshader b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gdshader
new file mode 100644
index 000000000..74584f028
--- /dev/null
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gdshader
@@ -0,0 +1,35 @@
+shader_type canvas_item;
+
+uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, repeat_disable, filter_nearest;
+
+uniform float amplitude_x : hint_range(0.0, 1.0) = 0.0;
+uniform float frequency_x = 0.0;
+uniform float phase_x = 0.0;
+uniform float time_x = 0.0;
+
+uniform float amplitude_y : hint_range(0.0, 1.0) = 0.0;
+uniform float frequency_y = 0.0;
+uniform float phase_y = 0.0;
+uniform float time_y = 0.0;
+
+uniform vec3 clear_color : source_color = vec3(0.0, 0.0, 0.0);
+
+void fragment() {
+	vec2 uv = SCREEN_UV;
+
+	float angular_frequency_x = 2.0 * PI * frequency_x;
+	float displacement_x = amplitude_x * sin(angular_frequency_x * (time_x + phase_x));
+
+	float angular_frequency_y = 2.0 * PI * frequency_y;
+	float displacement_y = amplitude_y * sin(angular_frequency_y * (time_y + phase_y));
+
+	uv.x += displacement_x / 1.0;
+	uv.y += displacement_y / 1.0;
+
+	if (uv.x > 1.0 || uv.y > 1.0 || uv.x < 0.0 || uv.y < 0.0) {
+		COLOR.rgb = clear_color;
+		COLOR.a = 1.0;
+	} else {
+		COLOR = texture(SCREEN_TEXTURE, uv);
+	}
+}
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gdshader.uid b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gdshader.uid
new file mode 100644
index 000000000..8b7caabe7
--- /dev/null
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gdshader.uid
@@ -0,0 +1 @@
+uid://ckbeep0lno2h1
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.tscn b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.tscn
new file mode 100644
index 000000000..5ad82033f
--- /dev/null
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.tscn
@@ -0,0 +1,40 @@
+[gd_scene load_steps=5 format=3 uid="uid://cdvx2jejg8xln"]
+
+[ext_resource type="Script" uid="uid://pf00w14gv64m" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gd" id="1_l682i"]
+[ext_resource type="Shader" uid="uid://ckbeep0lno2h1" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.gdshader" id="2_7h78g"]
+[ext_resource type="Script" uid="uid://bb1ano1eqhunf" path="res://addons/dialogic/Modules/ScreenShake/node_screen_shaker.gd" id="3_uqmkn"]
+
+[sub_resource type="ShaderMaterial" id="ShaderMaterial_4u8q6"]
+shader = ExtResource("2_7h78g")
+shader_parameter/amplitude_x = 0.0
+shader_parameter/frequency_x = 0.0
+shader_parameter/phase_x = 0.0
+shader_parameter/time_x = 0.0
+shader_parameter/amplitude_y = 0.0
+shader_parameter/frequency_y = 0.0
+shader_parameter/phase_y = 0.0
+shader_parameter/time_y = 0.0
+shader_parameter/clear_color = Color(0, 0, 0, 1)
+
+[node name="ScreenShakeLayer" type="Control"]
+layout_direction = 2
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("1_l682i")
+
+[node name="BackBufferCopy" type="BackBufferCopy" parent="."]
+copy_mode = 2
+
+[node name="DialogicNode_ScreenShaker" type="ColorRect" parent="."]
+material = SubResource("ShaderMaterial_4u8q6")
+layout_mode = 1
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("3_uqmkn")
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Style_SpeakerTextbox/speaker_textbox_style.tres b/addons/dialogic/Modules/DefaultLayoutParts/Style_SpeakerTextbox/speaker_textbox_style.tres
index ce61f628c..45a98d82e 100644
--- a/addons/dialogic/Modules/DefaultLayoutParts/Style_SpeakerTextbox/speaker_textbox_style.tres
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Style_SpeakerTextbox/speaker_textbox_style.tres
@@ -1,4 +1,4 @@
-[gd_resource type="Resource" script_class="DialogicStyle" load_steps=18 format=3 uid="uid://dgkmuyvy5qc35"]
+[gd_resource type="Resource" script_class="DialogicStyle" load_steps=20 format=3 uid="uid://dgkmuyvy5qc35"]
 
 [ext_resource type="PackedScene" uid="uid://c1k5m0w3r40xf" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_FullBackground/full_background_layer.tscn" id="1_sde84"]
 [ext_resource type="Script" uid="uid://bwg6yncmh2cml" path="res://addons/dialogic/Resources/dialogic_style_layer.gd" id="2_i34tx"]
@@ -8,6 +8,7 @@
 [ext_resource type="PackedScene" uid="uid://dhk6j6eb6e3q" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/vn_choice_layer.tscn" id="6_36eid"]
 [ext_resource type="PackedScene" uid="uid://cvgf4c6gg0tsy" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_TextInput/text_input_layer.tscn" id="7_hx5el"]
 [ext_resource type="PackedScene" uid="uid://lx24i8fl6uo" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_History/history_layer.tscn" id="8_00chv"]
+[ext_resource type="PackedScene" uid="uid://cdvx2jejg8xln" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.tscn" id="9_nxqau"]
 [ext_resource type="Script" uid="uid://dv08k6ljua6fm" path="res://addons/dialogic/Resources/dialogic_style.gd" id="9_sdr6x"]
 
 [sub_resource type="Resource" id="Resource_1cyj6"]
@@ -49,10 +50,15 @@ script = ExtResource("2_i34tx")
 scene = ExtResource("8_00chv")
 overrides = {}
 
+[sub_resource type="Resource" id="Resource_sw2nc"]
+script = ExtResource("2_i34tx")
+scene = ExtResource("9_nxqau")
+overrides = {}
+
 [resource]
 script = ExtResource("9_sdr6x")
 name = "Speaker Textbox Style"
-layer_list = Array[String](["10", "11", "12", "13", "14", "15", "16"])
+layer_list = Array[String](["10", "17", "11", "12", "13", "14", "15", "16"])
 layer_info = {
 "": SubResource("Resource_1cyj6"),
 "10": SubResource("Resource_jk75o"),
@@ -61,7 +67,8 @@ layer_info = {
 "13": SubResource("Resource_axty6"),
 "14": SubResource("Resource_xh5pc"),
 "15": SubResource("Resource_ytmk0"),
-"16": SubResource("Resource_yjxtw")
+"16": SubResource("Resource_yjxtw"),
+"17": SubResource("Resource_sw2nc")
 }
 base_overrides = {}
 layers = Array[ExtResource("2_i34tx")]([])
diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Style_VN_Default/default_vn_style.tres b/addons/dialogic/Modules/DefaultLayoutParts/Style_VN_Default/default_vn_style.tres
index 673fb886f..68e96297e 100644
--- a/addons/dialogic/Modules/DefaultLayoutParts/Style_VN_Default/default_vn_style.tres
+++ b/addons/dialogic/Modules/DefaultLayoutParts/Style_VN_Default/default_vn_style.tres
@@ -1,4 +1,4 @@
-[gd_resource type="Resource" script_class="DialogicStyle" load_steps=20 format=3 uid="uid://8t1mr302tmqs"]
+[gd_resource type="Resource" script_class="DialogicStyle" load_steps=22 format=3 uid="uid://8t1mr302tmqs"]
 
 [ext_resource type="Script" uid="uid://dv08k6ljua6fm" path="res://addons/dialogic/Resources/dialogic_style.gd" id="1_mvpc0"]
 [ext_resource type="Script" uid="uid://bwg6yncmh2cml" path="res://addons/dialogic/Resources/dialogic_style_layer.gd" id="2_3b8ue"]
@@ -9,6 +9,7 @@
 [ext_resource type="PackedScene" uid="uid://dsbwnp5hegnu3" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_Glossary/glossary_popup_layer.tscn" id="7_vw5f4"]
 [ext_resource type="PackedScene" uid="uid://dhk6j6eb6e3q" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/vn_choice_layer.tscn" id="8_tc6v1"]
 [ext_resource type="PackedScene" uid="uid://cvgf4c6gg0tsy" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_TextInput/text_input_layer.tscn" id="9_tufw5"]
+[ext_resource type="PackedScene" uid="uid://cdvx2jejg8xln" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_ScreenShake/screen_shake_layer.tscn" id="10_0qncn"]
 [ext_resource type="PackedScene" uid="uid://lx24i8fl6uo" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_History/history_layer.tscn" id="10_8v8jj"]
 
 [sub_resource type="Resource" id="Resource_3dixn"]
@@ -55,10 +56,15 @@ script = ExtResource("2_3b8ue")
 scene = ExtResource("10_8v8jj")
 overrides = {}
 
+[sub_resource type="Resource" id="Resource_tnh7v"]
+script = ExtResource("2_3b8ue")
+scene = ExtResource("10_0qncn")
+overrides = {}
+
 [resource]
 script = ExtResource("1_mvpc0")
 name = "Visual Novel Style"
-layer_list = Array[String](["10", "11", "12", "13", "14", "15", "16", "17"])
+layer_list = Array[String](["10", "11", "18", "12", "13", "14", "15", "16", "17"])
 layer_info = {
 "": SubResource("Resource_3dixn"),
 "10": SubResource("Resource_gen8e"),
@@ -68,8 +74,9 @@ layer_info = {
 "14": SubResource("Resource_pvwog"),
 "15": SubResource("Resource_spe5r"),
 "16": SubResource("Resource_jf1ol"),
-"17": SubResource("Resource_gs5pl")
+"17": SubResource("Resource_gs5pl"),
+"18": SubResource("Resource_tnh7v")
 }
 base_overrides = {}
 layers = Array[ExtResource("2_3b8ue")]([])
-metadata/_latest_layer = "17"
+metadata/_latest_layer = "18"
diff --git a/addons/dialogic/Modules/ScreenShake/event_screen_shake.gd b/addons/dialogic/Modules/ScreenShake/event_screen_shake.gd
new file mode 100644
index 000000000..06ae9ef32
--- /dev/null
+++ b/addons/dialogic/Modules/ScreenShake/event_screen_shake.gd
@@ -0,0 +1,111 @@
+@tool
+extends DialogicEvent
+class_name DialogicScreenShakeEvent
+
+## Event to shake the screen below the screen shake layer.
+
+### Settings
+
+enum Direction {HORIZONTAL, VERTICAL}
+
+## The strength of the screen shake.
+var amplitude: float = 0.1
+## The speed of the screen shake.
+var frequency: float = 1.0
+## The direction of the screen shake.
+var direction := Direction.HORIZONTAL
+## The time the fade animation will take. Leave at 0 for instant change.
+var fade: float = 0.0
+## The time the screen should shake for (excludes fade time).
+## Leave at 0 to keep shaking until the next screen shake event.
+var duration: float = 10.0
+## If true, the event will wait for completion before continuing
+## (only if duration is greater than 0.0 or fade greater than 0.0 and amplitude is 0.0).
+var wait := false
+
+#region EXECUTION
+################################################################################
+
+func _execute() -> void:
+	if direction == Direction.HORIZONTAL:
+		dialogic.ScreenShake.update_shake_x(amplitude, frequency, fade, duration)
+
+		if wait and (duration > 0.0 or (fade > 0.0 and amplitude == 0.0)):
+			await dialogic.ScreenShake.shake_horizontal_finished
+	else:
+		dialogic.ScreenShake.update_shake_y(amplitude, frequency, fade, duration)
+
+		if wait and (duration > 0.0 or (fade > 0.0 and amplitude == 0.0)):
+			await dialogic.ScreenShake.shake_vertical_finished
+
+	finish()
+
+#endregion
+
+#region INITIALIZE
+################################################################################
+
+func _init() -> void:
+	event_name = "Shake Screen"
+	set_default_color('Color8')
+	event_category = "Visuals"
+	event_sorting_index = 2
+
+#endregion
+
+#region SAVING/LOADING
+################################################################################
+
+func get_shortcode() -> String:
+	return "shake_screen"
+
+
+func get_shortcode_parameters() -> Dictionary:
+	return {
+		"amplitude"			: {"property": "amplitude", "default": 0.1},
+		"frequency"			: {"property": "frequency", "default": 1.0},
+		"direction"			: {"property": "direction", "default": Direction.HORIZONTAL},
+		"fade"				: {"property": "fade", "default": 0.0},
+		"duration"			: {"property": "duration", "default": 10.0},
+		"wait"				: {"property": "wait", "default": false},
+	}
+
+#endregion
+
+#region EDITOR REPRESENTATION
+################################################################################
+
+func build_event_editor() -> void:
+	add_header_edit("direction", ValueType.FIXED_OPTIONS, {
+		'left_text': 'Shake screen',
+		'options': [
+			{
+				'label': 'Horizontally',
+				'value': Direction.HORIZONTAL,
+				'icon': ["MirrorX", "EditorIcons"]
+			},
+			{
+				'label': 'Vertically',
+				'value': Direction.VERTICAL,
+				'icon': ["MirrorY", "EditorIcons"]
+			}
+		]})
+
+	add_header_edit("amplitude", ValueType.NUMBER, {
+		'left_text': 'with amplitude:',
+		'min': 0.0,
+		'max': 1.0,
+		'step': 0.01,
+	})
+
+	add_header_edit("frequency", ValueType.NUMBER, {
+		'left_text': 'and frequency:',
+		'min': 0.1,
+		'suffix': 'hz',
+	})
+
+	add_body_edit("fade", ValueType.NUMBER, {'left_text':'Fade time:'})
+	add_body_edit("duration", ValueType.NUMBER, {'left_text': 'Duration:'}, 'amplitude > 0.0')
+	add_body_edit("wait", ValueType.BOOL, {'left_text':'Wait for completion:'}, '(fade > 0.0 and amplitude == 0.0) or duration > 0.0')
+
+#endregion
diff --git a/addons/dialogic/Modules/ScreenShake/event_screen_shake.gd.uid b/addons/dialogic/Modules/ScreenShake/event_screen_shake.gd.uid
new file mode 100644
index 000000000..db350ccba
--- /dev/null
+++ b/addons/dialogic/Modules/ScreenShake/event_screen_shake.gd.uid
@@ -0,0 +1 @@
+uid://chapa5hltp6ac
diff --git a/addons/dialogic/Modules/ScreenShake/icon.png b/addons/dialogic/Modules/ScreenShake/icon.png
new file mode 100644
index 000000000..14b815a9d
Binary files /dev/null and b/addons/dialogic/Modules/ScreenShake/icon.png differ
diff --git a/addons/dialogic/Modules/ScreenShake/icon.png.import b/addons/dialogic/Modules/ScreenShake/icon.png.import
new file mode 100644
index 000000000..eb641d720
--- /dev/null
+++ b/addons/dialogic/Modules/ScreenShake/icon.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://m74xxddbt4sj"
+path="res://.godot/imported/icon.png-27bf81e8d508931fcff8d7fc07586e55.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/dialogic/Modules/ScreenShake/icon.png"
+dest_files=["res://.godot/imported/icon.png-27bf81e8d508931fcff8d7fc07586e55.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/addons/dialogic/Modules/ScreenShake/index.gd b/addons/dialogic/Modules/ScreenShake/index.gd
new file mode 100644
index 000000000..7294e40f3
--- /dev/null
+++ b/addons/dialogic/Modules/ScreenShake/index.gd
@@ -0,0 +1,8 @@
+@tool
+extends DialogicIndexer
+
+func _get_events() -> Array:
+	return [this_folder.path_join('event_screen_shake.gd')]
+
+func _get_subsystems() -> Array:
+	return [{'name':'ScreenShake', 'script':this_folder.path_join('subsystem_screen_shake.gd')}]
diff --git a/addons/dialogic/Modules/ScreenShake/index.gd.uid b/addons/dialogic/Modules/ScreenShake/index.gd.uid
new file mode 100644
index 000000000..be34217ac
--- /dev/null
+++ b/addons/dialogic/Modules/ScreenShake/index.gd.uid
@@ -0,0 +1 @@
+uid://cpq1bgdnqrmth
diff --git a/addons/dialogic/Modules/ScreenShake/node_screen_shaker.gd b/addons/dialogic/Modules/ScreenShake/node_screen_shaker.gd
new file mode 100644
index 000000000..09a77dcaf
--- /dev/null
+++ b/addons/dialogic/Modules/ScreenShake/node_screen_shaker.gd
@@ -0,0 +1,68 @@
+class_name DialogicNode_ScreenShaker
+extends ColorRect
+
+
+const ROLLOVER_TIME = 3600.0
+
+@onready var time_x: float = material.get_shader_parameter('time_x')
+@onready var phase_x: float = material.get_shader_parameter('phase_x')
+@onready var last_frequency_x: float = material.get_shader_parameter('frequency_x')
+
+@onready var time_y: float = material.get_shader_parameter('time_y')
+@onready var phase_y: float = material.get_shader_parameter('phase_y')
+@onready var last_frequency_y: float = material.get_shader_parameter('frequency_y')
+
+
+func _ready() -> void:
+	add_to_group('dialogic_screen_shaker')
+
+	material.set_shader_parameter(
+		'clear_color',
+		ProjectSettings.get_setting('rendering/environment/defaults/default_clear_color', Color.BLACK))
+
+
+func _process(delta: float) -> void:
+	if not DialogicUtil.autoload().paused:
+		time_x += delta
+		time_x = fposmod(time_x, ROLLOVER_TIME)
+		material.set_shader_parameter('time_x', time_x)
+
+		time_y += delta
+		time_y = fposmod(time_y, ROLLOVER_TIME)
+		material.set_shader_parameter('time_y', time_y)
+
+
+func reset_x() -> void:
+	time_x = 0.0
+	phase_x = 0.0
+	last_frequency_x = 0.0
+
+	material.set_shader_parameter('time_x', 0.0)
+	material.set_shader_parameter('phase_x', 0.0)
+
+
+func reset_y() -> void:
+	time_y = 0.0
+	phase_y = 0.0
+	last_frequency_y = 0.0
+
+	material.set_shader_parameter('time_y', 0.0)
+	material.set_shader_parameter('phase_y', 0.0)
+
+
+func update_frequency_x(new_frequency: float) -> void:
+	if new_frequency != 0.0:
+		phase_x = (last_frequency_x * (time_x + phase_x) / new_frequency) - time_x
+	last_frequency_x = new_frequency
+
+	material.set_shader_parameter('frequency_x', new_frequency)
+	material.set_shader_parameter('phase_x', phase_x)
+
+
+func update_frequency_y(new_frequency: float) -> void:
+	if new_frequency != 0.0:
+		phase_y = (last_frequency_y * (time_y + phase_y) / new_frequency) - time_y
+	last_frequency_y = new_frequency
+
+	material.set_shader_parameter('frequency_y', new_frequency)
+	material.set_shader_parameter('phase_y', phase_y)
diff --git a/addons/dialogic/Modules/ScreenShake/node_screen_shaker.gd.uid b/addons/dialogic/Modules/ScreenShake/node_screen_shaker.gd.uid
new file mode 100644
index 000000000..88db6f40c
--- /dev/null
+++ b/addons/dialogic/Modules/ScreenShake/node_screen_shaker.gd.uid
@@ -0,0 +1 @@
+uid://bb1ano1eqhunf
diff --git a/addons/dialogic/Modules/ScreenShake/subsystem_screen_shake.gd b/addons/dialogic/Modules/ScreenShake/subsystem_screen_shake.gd
new file mode 100644
index 000000000..d2cc22185
--- /dev/null
+++ b/addons/dialogic/Modules/ScreenShake/subsystem_screen_shake.gd
@@ -0,0 +1,221 @@
+extends DialogicSubsystem
+
+## Subsystem that manages screen shaking.
+
+
+## Whenever a horizontal screen shake is started, this signal is emitted and
+## contains a dictionary with the following keys: [br]
+## [br]
+## Key          |   Value Type  | Value [br]
+## ------------ | ------------- | ----- [br]
+## `amplitude`  | [type float]  | The strength of the screen shake. [br]
+## `frequency`  | [type float]  | The speed of the screen shake. [br]
+## `fade`       | [type float]  | The time the fade animation will take. Leave at 0 for instant change. [br]
+## `duration`   | [type float]  | The time the screen should shake for (excludes fade time). [br]
+signal shake_horizontal_started(info: Dictionary)
+
+## Whenever a horizontal screen shake has finished, this signal is emitted.
+signal shake_horizontal_finished()
+
+## Whenever a vertical screen shake is started, this signal is emitted and
+## contains a dictionary with the following keys: [br]
+## [br]
+## Key          |   Value Type  | Value [br]
+## ------------ | ------------- | ----- [br]
+## `amplitude`  | [type float]  | The strength of the screen shake. [br]
+## `frequency`  | [type float]  | The speed of the screen shake. [br]
+## `fade`       | [type float]  | The time the fade animation will take. Leave at 0 for instant change. [br]
+## `duration`   | [type float]  | The time the screen should shake for (excludes fade time). [br]
+signal shake_vertical_started(info: Dictionary)
+
+## Whenever a vertical screen shake has finished, this signal is emitted.
+signal shake_vertical_finished()
+
+#region STATE
+####################################################################################################
+
+func clear_game_state(clear_flag:=Dialogic.ClearFlags.FULL_CLEAR) -> void:
+	update_shake_x()
+	update_shake_y()
+
+
+func load_game_state(load_flag:=LoadFlags.FULL_LOAD) -> void:
+	if load_flag == LoadFlags.ONLY_DNODES:
+		return
+	var info: Dictionary = dialogic.current_state_info.get('screen_shake', {'x':{},'y':{}})
+	if info['x'].is_empty() or info['x'].get('amplitude', 0.0) == 0.0:
+		update_shake_x()
+	else:
+		update_shake_x(info['x'].amplitude, info['x'].frequency, info['x'].fade, info['x'].duration)
+	if info['y'].is_empty() or info['y'].get('amplitude', 0.0) == 0.0:
+		update_shake_y()
+	else:
+		update_shake_y(info['y'].amplitude, info['y'].frequency, info['y'].fade, info['y'].duration)
+
+#endregion
+
+#region MAIN METHODS
+####################################################################################################
+
+func update_shake_x(amplitude: float = 0.0, frequency: float = 0.0, fade: float = 0.0, duration: float = 0.0) -> void:
+	var previous_settings = dialogic.current_state_info.get('screen_shake', {'x':{},'y':{}})
+
+	if not dialogic.current_state_info.has('screen_shake'):
+		dialogic.current_state_info['screen_shake'] = {'x':{},'y':{}}
+
+	dialogic.current_state_info['screen_shake']['x'] = {
+		'amplitude': amplitude,
+		'frequency': frequency,
+		'fade': fade,
+		'duration': duration,
+	}
+
+	shake_horizontal_started.emit(dialogic.current_state_info['screen_shake']['x'])
+
+	var screen_shaker := get_screen_shaker()
+
+	if not screen_shaker:
+		return
+
+	if previous_settings['x'].is_empty():
+		screen_shaker.reset_x()
+
+	if fade > 0.0:
+		var current_amplitude := screen_shaker.material.get_shader_parameter('amplitude_x') as float
+		var current_frequency := screen_shaker.material.get_shader_parameter('frequency_x') as float
+		var tween := get_tree().create_tween()
+		tween.tween_method(_tween_amplitude_x, current_amplitude, amplitude, fade)
+		tween.set_parallel()
+		tween.tween_method(_tween_frequency_x, current_frequency, frequency, fade)
+		await tween.finished
+	else:
+		screen_shaker.material.set_shader_parameter('amplitude_x', amplitude)
+		screen_shaker.update_frequency_x(frequency)
+
+	if duration > 0.0 and amplitude > 0.0:
+		await get_tree().create_timer(duration).timeout
+	elif amplitude == 0.0:
+		dialogic.current_state_info['screen_shake']['x'] = {}
+		shake_horizontal_finished.emit()
+		return
+	else:
+		return
+
+	if fade > 0.0:
+		var current_amplitude := screen_shaker.material.get_shader_parameter('amplitude_x') as float
+		var current_frequency := screen_shaker.material.get_shader_parameter('frequency_x') as float
+		var tween := get_tree().create_tween()
+		tween.tween_method(_tween_amplitude_x, current_amplitude, 0.0, fade)
+		tween.set_parallel()
+		tween.tween_method(_tween_frequency_x, current_frequency, 0.0, fade)
+		await tween.finished
+	else:
+		screen_shaker.material.set_shader_parameter('amplitude_x', 0.0)
+		screen_shaker.update_frequency_x(frequency)
+
+	dialogic.current_state_info['screen_shake']['x'] = {}
+	shake_horizontal_finished.emit()
+
+
+func update_shake_y(amplitude: float = 0.0, frequency: float = 0.0, fade: float = 0.0, duration: float = 0.0) -> void:
+	var previous_settings = dialogic.current_state_info.get('screen_shake', {'x':{},'y':{}})
+	dialogic.current_state_info['screen_shake']['y'] = {
+		'amplitude': amplitude,
+		'frequency': frequency,
+		'fade': fade,
+		'duration': duration,
+	}
+
+	shake_vertical_started.emit(dialogic.current_state_info['screen_shake']['y'])
+
+	var screen_shaker := get_screen_shaker()
+
+	if not screen_shaker:
+		return
+
+	if previous_settings['y'].is_empty():
+		screen_shaker.reset_y()
+
+	if fade > 0.0:
+		var current_amplitude := screen_shaker.material.get_shader_parameter('amplitude_y') as float
+		var current_frequency := screen_shaker.material.get_shader_parameter('frequency_y') as float
+		var tween := get_tree().create_tween()
+		tween.tween_method(_tween_amplitude_y, current_amplitude, amplitude, fade)
+		tween.set_parallel()
+		tween.tween_method(_tween_frequency_y, current_frequency, frequency, fade)
+		await tween.finished
+	else:
+		screen_shaker.material.set_shader_parameter('amplitude_y', amplitude)
+		screen_shaker.update_frequency_y(frequency)
+
+	if duration > 0.0 and amplitude > 0.0:
+		await get_tree().create_timer(duration).timeout
+	elif amplitude == 0.0:
+		dialogic.current_state_info['screen_shake']['y'] = {}
+		shake_vertical_finished.emit()
+		return
+	else:
+		return
+
+	if fade > 0.0:
+		var current_amplitude := screen_shaker.material.get_shader_parameter('amplitude_y') as float
+		var current_frequency := screen_shaker.material.get_shader_parameter('frequency_y') as float
+		var tween := get_tree().create_tween()
+		tween.tween_method(_tween_amplitude_y, current_amplitude, 0.0, fade)
+		tween.set_parallel()
+		tween.tween_method(_tween_frequency_y, current_frequency, 0.0, fade)
+		await tween.finished
+	else:
+		screen_shaker.material.set_shader_parameter('amplitude_y', 0.0)
+		screen_shaker.update_frequency_y(frequency)
+
+	dialogic.current_state_info['screen_shake']['y'] = {}
+	shake_vertical_finished.emit()
+
+#endregion
+
+func _tween_amplitude_x(value: float) -> void:
+	var screen_shaker := get_screen_shaker()
+
+	if not screen_shaker:
+		return
+
+	screen_shaker.material.set_shader_parameter('amplitude_x', value)
+
+
+func _tween_amplitude_y(value: float) -> void:
+	var screen_shaker := get_screen_shaker()
+
+	if not screen_shaker:
+		return
+
+	screen_shaker.material.set_shader_parameter('amplitude_y', value)
+
+
+func _tween_frequency_x(value: float) -> void:
+	var screen_shaker := get_screen_shaker()
+
+	if not screen_shaker:
+		return
+
+	screen_shaker.update_frequency_x(value)
+
+
+func _tween_frequency_y(value: float) -> void:
+	var screen_shaker := get_screen_shaker()
+
+	if not screen_shaker:
+		return
+
+	screen_shaker.update_frequency_y(value)
+
+
+func get_screen_shaker() -> DialogicNode_ScreenShaker:
+	var screen_shaker: DialogicNode_ScreenShaker = null
+
+	if dialogic.has_subsystem('Styles'):
+		screen_shaker = dialogic.Styles.get_first_node_in_layout('dialogic_screen_shaker')
+	else:
+		screen_shaker = get_tree().get_first_node_in_group('dialogic_screen_shaker')
+
+	return screen_shaker
diff --git a/addons/dialogic/Modules/ScreenShake/subsystem_screen_shake.gd.uid b/addons/dialogic/Modules/ScreenShake/subsystem_screen_shake.gd.uid
new file mode 100644
index 000000000..f0dddb392
--- /dev/null
+++ b/addons/dialogic/Modules/ScreenShake/subsystem_screen_shake.gd.uid
@@ -0,0 +1 @@
+uid://l5ry6a5mdtxn