diff --git a/addons/material_maker/types/gradient.gd b/addons/material_maker/types/gradient.gd
index 37871ba9f..80db1a8ed 100644
--- a/addons/material_maker/types/gradient.gd
+++ b/addons/material_maker/types/gradient.gd
@@ -13,11 +13,13 @@ class CustomSorter:
 		return a.v < b.v
 
 var points = [ Point.new(0.0, Color(0.0, 0.0, 0.0, 0.0)), { v=1.0, c=Color(1.0, 1.0, 1.0, 1.0) } ]
-var interpolation = 1
-var sorted = true
+
+enum {CONSTANT=0, LINEAR=1, SMOOTHSTEP=2, CUBIC=3}
+var interpolation := LINEAR
+var sorted := true
 
 func to_string() -> String:
-	var rv = PackedStringArray()
+	var rv := PackedStringArray()
 	for p in points:
 		rv.append("("+str(p.v)+","+str(p.c)+")")
 	return ",".join(rv)
@@ -30,51 +32,60 @@ func duplicate() -> Object:
 	copy.interpolation = interpolation
 	return copy
 
+
 func clear() -> void:
 	points.clear()
 	sorted = true
 
+
 func add_point(v : float, c : Color) -> void:
 	points.append(Point.new(v, c))
 	sorted = false
 
+
 func get_point_count() -> int:
 	return points.size()
 
+
 func get_point_position(i : int) -> float:
 	return points[i].v
 
+
 func set_point_position(i : int, v : float) -> void:
 	points[i].v = v
 	sorted = false
 
+
 func sort() -> void:
-	if ! sorted:
+	if not sorted:
 		points.sort_custom(Callable(CustomSorter, "compare"))
 		for i in range(points.size()-1):
 			if points[i].v+0.0000005 >= points[i+1].v:
 				points[i+1].v = points[i].v+0.000001
 		sorted = true
 
-func get_color(x) -> Color:
+
+func get_color(x:float) -> Color:
 	sort()
 	if points.size() > 0:
+		# x is before the first point
 		if x < points[0].v:
 			return points[0].c
-		var s = points.size()-1
+
+		# x is after the last point
+		var s := points.size()-1
+		if x > points[s].v:
+			return points[s].c
+
 		for i in range(s):
 			if x < points[i+1].v:
-				var p0 = points[i].v
-				var c0 = points[i].c
-				var p1 = points[i+1].v
-				var c1 = points[i+1].c
-				return c0 + (c1-c0) * (x-p0) / (p1-p0)
-		return points[s].c
-	else:
-		return Color(0.0, 0.0, 0.0, 1.0)
+				return get_color_between_points(i, i+1, x)
+
+	return Color(0.0, 0.0, 0.0, 1.0)
+
 
 func get_shader_params(parameter_name : String, attribute : String = "uniform") -> String:
-	var rv = ""
+	var rv := ""
 	for p : MMGenBase.ShaderUniform in get_parameters(parameter_name):
 		rv += p.to_str(attribute)
 	return rv
@@ -112,10 +123,10 @@ func pc(parameter_name : String, i : int) -> String:
 
 func get_shader(parameter_name : String) -> String:
 	sort()
-	var shader
+	var shader: String
 	shader  = "vec4 "+parameter_name+"_gradient_fct(float x) {\n"
 	match interpolation:
-		0:
+		CONSTANT:
 			if points.size() > 0:
 				shader += "  if (x < %s) {\n" % pv(parameter_name, 1)
 				shader += "    return "+pc(parameter_name, 0)+";\n"
@@ -127,20 +138,20 @@ func get_shader(parameter_name : String) -> String:
 				shader += "  return "+pc(parameter_name, s)+";\n"
 			else:
 				shader += "  return vec4(0.0, 0.0, 0.0, 1.0);\n"
-		1, 2:
+		LINEAR, SMOOTHSTEP:
 			if points.size() > 0:
 				shader += "  if (x < %s) {\n" % pv(parameter_name, 0)
 				shader += "    return "+pc(parameter_name, 0)+";\n"
 				var s = points.size()-1
 				for i in range(s):
 					shader += "  } else if (x < %s) {\n" % pv(parameter_name, i+1)
-					var function = "(" if interpolation == 1 else "0.5-0.5*cos(3.14159265359*"
+					var function = "(" if interpolation == LINEAR else "0.5-0.5*cos(3.14159265359*"
 					shader += "    return mix(%s, %s, %s(x-%s)/(%s-%s)));\n" % [ pc(parameter_name, i), pc(parameter_name, i+1), function, pv(parameter_name, i), pv(parameter_name, i+1), pv(parameter_name, i) ]
 				shader += "  }\n"
 				shader += "  return "+pc(parameter_name, s)+";\n"
 			else:
 				shader += "  return vec4(0.0, 0.0, 0.0, 1.0);\n"
-		3:
+		CUBIC:
 			if points.size() > 0:
 				shader += "  if (x < %s) {\n" % pv(parameter_name, 0)
 				shader += "    return "+pc(parameter_name, 0)+";\n"
@@ -174,7 +185,7 @@ func get_shader(parameter_name : String) -> String:
 func serialize() -> Dictionary:
 	sort()
 	var rv = []
-	if interpolation == 0:
+	if interpolation == CONSTANT:
 		var p : Point = points[0]
 		rv.append({ pos=0, r=p.c.r, g=p.c.g, b=p.c.b, a=p.c.a })
 		for i in range(1, points.size()):
@@ -201,7 +212,7 @@ func deserialize(v) -> void:
 			add_point(i.pos, Color(i.r, i.g, i.b, i.a))
 		if v.has("interpolation"):
 			interpolation = int(v.interpolation)
-			if interpolation == 0:
+			if interpolation == CONSTANT:
 				for i in range(points.size()-1, 0, -1):
 					if points[i].c == points[i-1].c:
 						points.remove_at(i)
@@ -209,7 +220,7 @@ func deserialize(v) -> void:
 						points[i].v = 0.5*(points[i-1].v+points[i].v)
 				points[0].v = 0
 		else:
-			interpolation = 1
+			interpolation = LINEAR
 	elif typeof(v) == TYPE_OBJECT and v.get_script() == get_script():
 		clear()
 		for p in v.points:
@@ -217,3 +228,95 @@ func deserialize(v) -> void:
 		interpolation = v.interpolation
 	else:
 		print("Cannot deserialize gradient "+str(v))
+
+
+func effect_reverse() -> void:
+	for i in range(get_point_count()):
+		set_point_position(i, 1-get_point_position(i))
+
+
+func effect_evenly_distribute() -> void:
+	for i in range(get_point_count()):
+		set_point_position(i, 1.0/(get_point_count()-1)*i)
+
+
+func effect_simplify(threshold := 0.05) -> void:
+	sort()
+	while true:
+		var most_useless_point := -1
+		var most_useless_strength := 10.0
+
+		for point in range(get_point_count()):
+			var uselessness := 10.0
+			var point_color: Color = points[point].c
+			if point == 0:
+				uselessness = get_color_difference(point_color, points[point+1].c)
+			elif point == get_point_count()-1:
+				uselessness = get_color_difference(point_color, points[point-1].c)
+
+			else:
+				uselessness = get_color_difference(point_color,
+					get_color_between_points(point-1, point+1, points[point].v))
+
+			if uselessness < most_useless_strength:
+				most_useless_point = point
+				most_useless_strength = uselessness
+
+		if most_useless_strength > threshold or get_point_count()<3:
+			break
+
+		points.remove_at(most_useless_point)
+
+
+func get_color_difference(color1: Color, color2: Color) -> float:
+	return abs((color1.r-color2.r)+(color1.g-color2.g) + (color1.b-color2.b) + (color1.a-color2.a))
+
+
+func get_color_between_points(point1:int, point2:int, global_offset:float) -> Color:
+	# point left to the offset
+	var pl: Point = points[point1]
+	# point right to the offset
+	var pr: Point = points[point2]
+
+	# offset relative between the two points
+	var offset := local_offset(point1, point2, global_offset)#(global_offset-pl.v)/(pr.v-pl.v)
+
+	if interpolation == CONSTANT:
+		return pl.c
+	# LINEAR
+	elif interpolation == LINEAR:
+		return pl.c.lerp(pr.c, offset)
+	# SMOOTHSTEP
+	elif interpolation == SMOOTHSTEP:
+		return pl.c.lerp(pr.c, 0.5-0.5*cos(3.14159265359*offset))
+	# CUBIC
+	elif interpolation == CUBIC:
+		if point1 == 0:
+			var factor := 1.0 - 0.5 * offset
+			var next_section_lerp := pr.c.lerp(points[point2+1].c, local_offset(point2, point2+1, global_offset))
+			var this_section_lerp := pl.c.lerp(pr.c, offset)
+
+			return next_section_lerp.lerp(this_section_lerp, factor)
+
+		elif point2 == len(points)-1:
+			var factor := 0.5 + 0.5 * offset
+			var this_section_lerp := pl.c.lerp(pr.c, offset)
+			var prev_section_lerp: Color = points[point1-1].c.lerp(pl.c, local_offset(point1-1, point1, global_offset))
+
+			return prev_section_lerp.lerp(this_section_lerp, factor)
+
+		else:
+			var this_section_lerp := pl.c.lerp(pr.c, offset)
+
+			var prev_section_lerp: Color = points[point1-1].c.lerp(pl.c, local_offset(point1-1, point1, global_offset))
+			var next_section_lerp: Color = pr.c.lerp(points[point2+1].c, local_offset(point2, point2+1, global_offset))
+
+			var final := 0.5 * (this_section_lerp + prev_section_lerp.lerp(next_section_lerp, 0.5-0.5*cos(3.14159265359 * offset)))
+
+			return final
+
+	return pl.c
+
+
+func local_offset(point1:int, point2:int, global_offset:float) -> float:
+	return (global_offset-points[point1].v)/(points[point2].v-points[point1].v)
diff --git a/material_maker/panels/common/menu_bar_button_with_panel.gd b/material_maker/panels/common/menu_bar_button_with_panel.gd
index f97d9c9a6..49fb54727 100644
--- a/material_maker/panels/common/menu_bar_button_with_panel.gd
+++ b/material_maker/panels/common/menu_bar_button_with_panel.gd
@@ -28,6 +28,7 @@ func _draw() -> void:
 
 func _on_toggled(pressed:bool) -> void:
 	panel.visible = pressed
+	panel.size = Vector2()
 
 	if panel.visible:
 		position_panel()
diff --git a/material_maker/widgets/gradient_editor/gradient_edit.gd b/material_maker/widgets/gradient_editor/gradient_edit.gd
index 27c5f2e74..0027bc6e7 100644
--- a/material_maker/widgets/gradient_editor/gradient_edit.gd
+++ b/material_maker/widgets/gradient_editor/gradient_edit.gd
@@ -322,7 +322,7 @@ func remove_popup_button() -> void:
 
 
 func update_visuals() -> void:
-	if %PopupButton:
+	if has_node("%PopupButton"):
 		%PopupButton.icon = get_theme_icon("dropdown", "MM_Icons")
 	var is_hovered := Rect2(Vector2(), size).has_point(get_local_mouse_position())
 	if is_hovered != hovered:
diff --git a/material_maker/widgets/gradient_editor/gradient_effects_menu.gd b/material_maker/widgets/gradient_editor/gradient_effects_menu.gd
new file mode 100644
index 000000000..6c1d16229
--- /dev/null
+++ b/material_maker/widgets/gradient_editor/gradient_effects_menu.gd
@@ -0,0 +1,9 @@
+extends PanelContainer
+
+
+func add_effects_button(text:String, callable: Callable) -> Button:
+	var button := Button.new()
+	button.text = text
+	button.pressed.connect(callable)
+	$VBox.add_child(button)
+	return button
diff --git a/material_maker/widgets/gradient_editor/gradient_effects_menu.gd.uid b/material_maker/widgets/gradient_editor/gradient_effects_menu.gd.uid
new file mode 100644
index 000000000..2d505afe4
--- /dev/null
+++ b/material_maker/widgets/gradient_editor/gradient_effects_menu.gd.uid
@@ -0,0 +1 @@
+uid://d1a1bkn1evvxv
diff --git a/material_maker/widgets/gradient_editor/gradient_popup.gd b/material_maker/widgets/gradient_editor/gradient_popup.gd
index a2b5401b3..14f863b09 100644
--- a/material_maker/widgets/gradient_editor/gradient_popup.gd
+++ b/material_maker/widgets/gradient_editor/gradient_popup.gd
@@ -11,6 +11,11 @@ func _ready() -> void:
 	%Previous.icon = get_theme_icon("arrow_left", "MM_Icons")
 	%Next.icon = get_theme_icon("arrow_right", "MM_Icons")
 
+	%EffectsMenuPanel.add_effects_button("Reverse", gradient_effect.bind("effect_reverse"))
+	%EffectsMenuPanel.add_effects_button("Evenly Distribute", gradient_effect.bind("effect_evenly_distribute"))
+	%EffectsMenuPanel.add_effects_button("Simplify", gradient_effect.bind("effect_simplify"))
+	%EffectsMenuPanel.add_effects_button("Strong Simplify", gradient_effect.bind("effect_simplify", [0.2]))
+
 
 func set_gradient(value:MMGradient, cursor_index := 0) -> void:
 	%GradientEdit.value = value
@@ -25,7 +30,7 @@ func set_gradient(value:MMGradient, cursor_index := 0) -> void:
 # Handle closing the popup
 func _input(event:InputEvent) -> void:
 	if event is InputEventMouseButton and event.pressed:
-		if not get_global_rect().has_point(get_global_mouse_position()):
+		if not get_global_rect().has_point(get_global_mouse_position()) and not %EffectsMenuPanel.visible:
 			if not %Pin.button_pressed:
 				close()
 				accept_event()
@@ -71,3 +76,12 @@ func _on_previous_pressed() -> void:
 
 func _on_next_pressed() -> void:
 	%GradientEdit.active_cursor += 1
+
+
+func gradient_effect(effect_method:String, args := []) -> void:
+	if not %GradientEdit.value.has_method(effect_method):
+		return
+	%GradientEdit.value.callv(effect_method, args)
+	%GradientEdit.set_value(%GradientEdit.value)
+	_on_gradient_edit_updated(%GradientEdit.value, false)
+	$Panel/VBox/HBox/EffectsMenu.button_pressed = false
diff --git a/material_maker/widgets/gradient_editor/gradient_popup.tscn b/material_maker/widgets/gradient_editor/gradient_popup.tscn
index de7edab75..70b74465f 100644
--- a/material_maker/widgets/gradient_editor/gradient_popup.tscn
+++ b/material_maker/widgets/gradient_editor/gradient_popup.tscn
@@ -1,9 +1,11 @@
-[gd_scene load_steps=5 format=3 uid="uid://in4lqr3eetvc"]
+[gd_scene load_steps=7 format=3 uid="uid://in4lqr3eetvc"]
 
-[ext_resource type="Script" path="res://material_maker/widgets/gradient_editor/gradient_popup.gd" id="1"]
+[ext_resource type="Script" uid="uid://h85iv7e2rlbv" path="res://material_maker/widgets/gradient_editor/gradient_popup.gd" id="1"]
 [ext_resource type="PackedScene" uid="uid://rflulhsuy3ax" path="res://material_maker/widgets/float_edit/float_edit.tscn" id="2_fotv7"]
 [ext_resource type="PackedScene" uid="uid://cp6ft7qbucfam" path="res://material_maker/widgets/gradient_editor/gradient_edit.tscn" id="2_uait3"]
 [ext_resource type="PackedScene" uid="uid://dj5q8sxvd3gci" path="res://material_maker/widgets/option_edit/option_edit.tscn" id="3_2wy2b"]
+[ext_resource type="Script" uid="uid://c37lcka7r53wk" path="res://material_maker/panels/common/menu_bar_button_with_panel.gd" id="4_6ujyx"]
+[ext_resource type="Script" uid="uid://d1a1bkn1evvxv" path="res://material_maker/widgets/gradient_editor/gradient_effects_menu.gd" id="5_sd4bi"]
 
 [node name="GradientPopup" type="MarginContainer"]
 top_level = true
@@ -60,6 +62,30 @@ popup/item_2/id = 2
 popup/item_3/text = "Cubic"
 popup/item_3/id = 3
 
+[node name="EffectsMenu" type="Button" parent="Panel/VBox/HBox"]
+custom_minimum_size = Vector2(40, 25)
+layout_mode = 2
+tooltip_text = "Tools"
+theme_type_variation = &"MM_PanelMenuButton"
+toggle_mode = true
+button_mask = 3
+script = ExtResource("4_6ujyx")
+icon_name = "settings"
+
+[node name="EffectsMenuPanel" type="PanelContainer" parent="Panel/VBox/HBox/EffectsMenu"]
+unique_name_in_owner = true
+top_level = true
+layout_mode = 0
+offset_left = 493.0
+offset_top = 9.0
+offset_right = 817.0
+offset_bottom = 75.0
+theme_type_variation = &"MM_PanelMenuSubPanel"
+script = ExtResource("5_sd4bi")
+
+[node name="VBox" type="VBoxContainer" parent="Panel/VBox/HBox/EffectsMenu/EffectsMenuPanel"]
+layout_mode = 2
+
 [node name="Pin" type="Button" parent="Panel/VBox/HBox"]
 unique_name_in_owner = true
 layout_mode = 2