diff --git a/src/Autoload/Global.gd b/src/Autoload/Global.gd
index 49a0caa16..7977d45d4 100644
--- a/src/Autoload/Global.gd
+++ b/src/Autoload/Global.gd
@@ -954,7 +954,7 @@ func general_redo(project := current_project) -> void:
 ## Performs actions done after an undo or redo is done. this takes [member general_undo] and
 ## [member general_redo] a step further. Does further work if the current action requires it
 ## like refreshing textures, redraw UI elements etc...[br]
-## [param frame_index] and [param layer_index] are there for optimizzation. if the undo or redo
+## [param frame_index] and [param layer_index] are there for optimization. if the undo or redo
 ## happens only in one cel then the cel's frame and layer should be passed to [param frame_index]
 ## and [param layer_index] respectively, otherwise the entire timeline will be refreshed.
 func undo_or_redo(
@@ -981,10 +981,14 @@ func undo_or_redo(
 	):
 		if layer_index > -1 and frame_index > -1:
 			canvas.update_texture(layer_index, frame_index, project)
+			var cel := project.frames[frame_index].cels[layer_index]
+			cel.on_undo_redo(undo)
 		else:
 			for i in project.frames.size():
 				for j in project.layers.size():
 					canvas.update_texture(j, i, project)
+					var cel := project.frames[i].cels[j]
+					cel.on_undo_redo(undo)
 
 		canvas.selection.queue_redraw()
 		if action_name == "Scale":
diff --git a/src/Classes/Cels/BaseCel.gd b/src/Classes/Cels/BaseCel.gd
index 313544956..f5ccc7b73 100644
--- a/src/Classes/Cels/BaseCel.gd
+++ b/src/Classes/Cels/BaseCel.gd
@@ -77,7 +77,7 @@ func update_texture() -> void:
 				cel.texture_changed.emit()
 
 
-func tool_finished_drawing() -> void:
+func on_undo_redo(undo: bool) -> void:
 	pass
 
 
diff --git a/src/Classes/Cels/CelTileMap.gd b/src/Classes/Cels/CelTileMap.gd
index e9610901e..3f0fa576d 100644
--- a/src/Classes/Cels/CelTileMap.gd
+++ b/src/Classes/Cels/CelTileMap.gd
@@ -36,11 +36,11 @@ func update_texture() -> void:
 	super.update_texture()
 
 
-func tool_finished_drawing() -> void:
-	update_tileset()
+func on_undo_redo(undo: bool) -> void:
+	update_tileset(undo)
 
 
-func update_tileset() -> void:
+func update_tileset(undo: bool) -> void:
 	for i in indices.size():
 		var coords := get_tile_coords(i)
 		var rect := Rect2i(coords, tileset.tile_size)
@@ -74,6 +74,10 @@ func update_tileset() -> void:
 			if not found_tile:
 				tileset.add_tile(image_portion, TileSetPanel.tile_editing_mode)
 				indices[i] = tileset.tiles.size() - 1
+	if undo:
+		var tile_removed := tileset.remove_unused_tiles()
+		if tile_removed:
+			re_index_tiles()
 
 
 ## Cases:[br]
@@ -209,7 +213,6 @@ func get_tile_position(coords: Vector2i) -> int:
 	return x + y
 
 
-## Unused, should delete.
 func re_index_tiles() -> void:
 	for i in indices.size():
 		var x_coord := float(tileset.tile_size.x) * (i % indices_x)
diff --git a/src/Classes/TileSetCustom.gd b/src/Classes/TileSetCustom.gd
index 8d5867a30..8fb9b3849 100644
--- a/src/Classes/TileSetCustom.gd
+++ b/src/Classes/TileSetCustom.gd
@@ -3,6 +3,7 @@ extends RefCounted
 
 signal updated
 
+var project: Project
 var name := ""
 var tile_size: Vector2i
 var tiles: Array[Tile] = []
@@ -12,40 +13,44 @@ class Tile:
 	var image: Image
 	var mode_added: TileSetPanel.TileEditingMode
 	var times_used := 1
+	var undo_step_added := 0
 
-	func _init(_image: Image, _mode_added: TileSetPanel.TileEditingMode) -> void:
+	func _init(
+		_image: Image, _mode_added: TileSetPanel.TileEditingMode, _undo_step_added := 0
+	) -> void:
 		image = _image
 		mode_added = _mode_added
+		undo_step_added = _undo_step_added
 
-	func can_be_removed() -> bool:
+	func can_be_removed(project: Project) -> bool:
+		if project.undos < undo_step_added:
+			return true
 		return mode_added != TileSetPanel.TileEditingMode.STACK and times_used <= 0
 
 
-func _init(_tile_size: Vector2i, _name := "") -> void:
+func _init(_tile_size: Vector2i, _project: Project, _name := "") -> void:
 	tile_size = _tile_size
+	project = _project
 	name = _name
-	#var indices_x := ceili(float(_project_size.x) / tile_size.x)
-	#var indices_y := ceili(float(_project_size.y) / tile_size.y)
-	#tiles.resize(indices_x * indices_y + 1)
 	var empty_image := Image.create_empty(tile_size.x, tile_size.y, false, Image.FORMAT_RGBA8)
 	tiles.append(Tile.new(empty_image, TileSetPanel.tile_editing_mode))
 
 
 func add_tile(image: Image, edit_mode: TileSetPanel.TileEditingMode) -> void:
-	var tile := Tile.new(image, edit_mode)
+	var tile := Tile.new(image, edit_mode, project.undos)
 	tiles.append(tile)
 	updated.emit()
 
 
 func insert_tile(image: Image, position: int, edit_mode: TileSetPanel.TileEditingMode) -> void:
-	var tile := Tile.new(image, edit_mode)
+	var tile := Tile.new(image, edit_mode, project.undos)
 	tiles.insert(position, tile)
 	updated.emit()
 
 
 func unuse_tile_at_index(index: int) -> bool:
 	tiles[index].times_used -= 1
-	if tiles[index].can_be_removed():
+	if tiles[index].can_be_removed(project):
 		remove_tile_at_index(index)
 		return true
 	return false
@@ -69,12 +74,11 @@ func find_tile(image: Image) -> int:
 	return -1
 
 
-## Unused, should delete.
 func remove_unused_tiles() -> bool:
 	var tile_removed := false
 	for i in range(tiles.size() - 1, 0, -1):
 		var tile := tiles[i]
-		if tile.can_be_removed():
-			tile_removed = true
+		if tile.can_be_removed(project):
 			remove_tile_at_index(i)
+			tile_removed = true
 	return tile_removed
diff --git a/src/Tools/BaseTool.gd b/src/Tools/BaseTool.gd
index 0d60398ff..4e173e7fc 100644
--- a/src/Tools/BaseTool.gd
+++ b/src/Tools/BaseTool.gd
@@ -76,16 +76,9 @@ func draw_end(_pos: Vector2i) -> void:
 	is_moving = false
 	_draw_cache = []
 	var project := Global.current_project
-	update_cels(project)
 	project.can_undo = true
 
 
-func update_cels(project := Global.current_project) -> void:
-	for cel_index in project.selected_cels:
-		var cel := project.frames[cel_index[0]].cels[cel_index[1]] as BaseCel
-		cel.tool_finished_drawing()
-
-
 func is_placing_tiles() -> bool:
 	return Global.current_project.get_current_cel() is CelTileMap and TileSetPanel.placing_tiles
 
diff --git a/src/Tools/DesignTools/Pencil.gd b/src/Tools/DesignTools/Pencil.gd
index f373d7dda..3a47716a1 100644
--- a/src/Tools/DesignTools/Pencil.gd
+++ b/src/Tools/DesignTools/Pencil.gd
@@ -96,9 +96,6 @@ func draw_start(pos: Vector2i) -> void:
 	_old_spacing_mode = _spacing_mode
 	pos = snap_position(pos)
 	super.draw_start(pos)
-	if is_placing_tiles():
-		draw_tile(pos, TileSetPanel.selected_tile_index)
-		return
 	if Input.is_action_pressed(&"draw_color_picker", true):
 		_picking_color = true
 		_pick_color(pos)
@@ -106,6 +103,10 @@ func draw_start(pos: Vector2i) -> void:
 	_picking_color = false
 
 	Global.canvas.selection.transform_content_confirm()
+	prepare_undo("Draw")
+	if is_placing_tiles():
+		draw_tile(pos, TileSetPanel.selected_tile_index)
+		return
 	var can_skip_mask := true
 	if tool_slot.color.a < 1 and !_overwrite:
 		can_skip_mask = false
@@ -115,7 +116,6 @@ func draw_start(pos: Vector2i) -> void:
 	_drawer.color_op.overwrite = _overwrite
 	_draw_points = []
 
-	prepare_undo("Draw")
 	_drawer.reset()
 
 	_draw_line = Input.is_action_pressed("draw_create_line")
@@ -141,13 +141,13 @@ func draw_move(pos_i: Vector2i) -> void:
 	var pos := _get_stabilized_position(pos_i)
 	pos = snap_position(pos)
 	super.draw_move(pos)
-	if is_placing_tiles():
-		draw_tile(pos, TileSetPanel.selected_tile_index)
-		return
 	if _picking_color:  # Still return even if we released Alt
 		if Input.is_action_pressed(&"draw_color_picker", true):
 			_pick_color(pos)
 		return
+	if is_placing_tiles():
+		draw_tile(pos, TileSetPanel.selected_tile_index)
+		return
 
 	if _draw_line:
 		_spacing_mode = false  # spacing mode is disabled during line mode
@@ -170,12 +170,13 @@ func draw_move(pos_i: Vector2i) -> void:
 
 func draw_end(pos: Vector2i) -> void:
 	pos = snap_position(pos)
+	if _picking_color:
+		super.draw_end(pos)
+		return
 	if is_placing_tiles():
 		super.draw_end(pos)
 		draw_tile(pos, TileSetPanel.selected_tile_index)
-		return
-	if _picking_color:
-		super.draw_end(pos)
+		commit_undo()
 		return
 
 	if _draw_line:
diff --git a/src/Tools/UtilityTools/Text.gd b/src/Tools/UtilityTools/Text.gd
index 94fcffe5e..7c84f77bb 100644
--- a/src/Tools/UtilityTools/Text.gd
+++ b/src/Tools/UtilityTools/Text.gd
@@ -159,7 +159,6 @@ func text_to_pixels() -> void:
 		if image is ImageExtended:
 			image.convert_rgb_to_indexed()
 		commit_undo("Draw", undo_data)
-	update_cels(project)
 
 
 func commit_undo(action: String, undo_data: Dictionary) -> void:
diff --git a/src/UI/Canvas/Selection.gd b/src/UI/Canvas/Selection.gd
index 9f12b69b2..f28fd7cd7 100644
--- a/src/UI/Canvas/Selection.gd
+++ b/src/UI/Canvas/Selection.gd
@@ -517,7 +517,6 @@ func transform_content_confirm() -> void:
 			big_bounding_rectangle.position
 		)
 		cel_image.convert_rgb_to_indexed()
-		cel.tool_finished_drawing()
 	project.selection_map.move_bitmap_values(project)
 	commit_undo("Move Selection", undo_data)
 
@@ -553,7 +552,6 @@ func transform_content_cancel() -> void:
 				big_bounding_rectangle.position
 			)
 			cel.transformed_content = null
-		cel.tool_finished_drawing()
 	for cel_index in project.selected_cels:
 		canvas.update_texture(cel_index[1])
 	original_preview_image = Image.new()
diff --git a/src/UI/Timeline/AnimationTimeline.gd b/src/UI/Timeline/AnimationTimeline.gd
index dea3d6c52..a6255ffa6 100644
--- a/src/UI/Timeline/AnimationTimeline.gd
+++ b/src/UI/Timeline/AnimationTimeline.gd
@@ -847,7 +847,7 @@ func add_layer(type := 0) -> void:
 			l = Layer3D.new(project)
 			SteamManager.set_achievement("ACH_3D_LAYER")
 		Global.LayerTypes.TILEMAP:
-			l = LayerTileMap.new(project, TileSetCustom.new(Vector2i(16, 16)))
+			l = LayerTileMap.new(project, TileSetCustom.new(Vector2i(16, 16), project))
 
 	var cels := []
 	for f in project.frames: