diff --git a/Osu.Patcher.Hook/Patches/PatchBeatmapThumbnailAlpha.cs b/Osu.Patcher.Hook/Patches/PatchBeatmapThumbnailAlpha.cs
new file mode 100644
index 0000000..0cea4a0
--- /dev/null
+++ b/Osu.Patcher.Hook/Patches/PatchBeatmapThumbnailAlpha.cs
@@ -0,0 +1,72 @@
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+using HarmonyLib;
+using JetBrains.Annotations;
+using Osu.Stubs;
+using static System.Reflection.Emit.OpCodes;
+
+namespace Osu.Patcher.Hook.Patches;
+
+///
+/// Changes the default alpha on beatmap thumbnails in song select.
+///
+[HarmonyPatch]
+[UsedImplicitly]
+public class PatchBeatmapThumbnailAlpha : BasePatch
+{
+ // TODO: make this user configurable through settings
+ private const int NewDeselectedAlpha = 185; // 0-255
+
+ [UsedImplicitly]
+ [HarmonyTargetMethods]
+ private static IEnumerable Target() =>
+ [
+ BeatmapTreeItem.PopulateSprites.Reference,
+ BeatmapTreeItem.UpdateSprites.Reference,
+ ];
+
+ ///
+ /// Changes the target color used as an alpha overlay over the thumbnail.
+ /// Replaces all 50s as in new Color(50, 50, 50, ...) with a higher value.
+ /// Applied to both UpdateSprites and PopulateSprites.
+ ///
+ [UsedImplicitly]
+ [HarmonyTranspiler]
+ private static IEnumerable ChangeDeselectFade(IEnumerable instructions) =>
+ instructions.Manipulator(
+ inst => inst.Is(Ldc_I4_S, 50),
+ inst => inst.operand = NewDeselectedAlpha
+ );
+
+
+ ///
+ /// Avoid re-fading in from zero when alpha already high from previous patches.
+ /// Replaces the following code with Nop: thumbnail.FadeInFromZero(instant ? 0 : 1000);
+ /// This is only applicable to UpdateSprites().
+ ///
+ [UsedImplicitly]
+ [HarmonyTranspiler]
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ private static IEnumerable DisableFadeInFromZero(
+ IEnumerable instructions,
+ MethodBase __originalMethod)
+ {
+ if (__originalMethod != BeatmapTreeItem.UpdateSprites.Reference)
+ return instructions;
+
+ return NoopSignature(instructions, new[]
+ {
+ Ldarg_0,
+ Ldfld,
+ Ldloc_0,
+ Ldfld,
+ Brtrue_S,
+ Ldc_I4,
+ Br_S,
+ Ldc_I4_0,
+ Callvirt,
+ Pop,
+ });
+ }
+}
\ No newline at end of file
diff --git a/Osu.Stubs/BeatmapTreeItem.cs b/Osu.Stubs/BeatmapTreeItem.cs
new file mode 100644
index 0000000..692e724
--- /dev/null
+++ b/Osu.Stubs/BeatmapTreeItem.cs
@@ -0,0 +1,62 @@
+using JetBrains.Annotations;
+using Osu.Stubs.Opcode;
+using static System.Reflection.Emit.OpCodes;
+
+namespace Osu.Stubs;
+
+///
+/// Original: osu.GameplayElements.Beatmaps.BeatmapTreeItem
+/// b20240102.2: #=z5dQ2d9vSoRzE9rWp7h5_dJ$Z1Sw13QcKf_J$7ZjIN21zOue2gQ==
+///
+[UsedImplicitly]
+public static class BeatmapTreeItem
+{
+ ///
+ /// Original: PopulateSprites(TreeItemState lastState, bool instant)
+ /// b20240102.2: #=ztf5JjnV1ubq2KLwXBg==
+ ///
+ [UsedImplicitly]
+ public static readonly LazyMethod UpdateSprites = new(
+ "BeatmapTreeItem#UpdateSprites(...)",
+ new[]
+ {
+ Pop,
+ Ldsfld,
+ Ldftn,
+ Newobj,
+ Dup,
+ Stsfld,
+ Callvirt,
+ Ret,
+ Ldarg_0,
+ Ldfld,
+ Ldloc_0,
+ Ldftn,
+ Newobj,
+ }
+ );
+
+ ///
+ /// Original: PopulateSprites()
+ /// b20240102.2: #=zGdedQLY8W$wSqdNzBA==
+ ///
+ [UsedImplicitly]
+ public static readonly LazyMethod PopulateSprites = new(
+ "BeatmapTreeItem#PopulateSprites()",
+ new[]
+ {
+ Brfalse,
+ Ldsfld,
+ Ldfld,
+ Ldc_I4,
+ Clt,
+ Ldc_I4_0,
+ Ceq,
+ Stloc_S,
+ Ldarg_0,
+ Ldc_I4_5,
+ Newarr,
+ Dup,
+ }
+ );
+}
\ No newline at end of file
diff --git a/Osu.Stubs/BeatmapTreeManager.cs b/Osu.Stubs/BeatmapTreeManager.cs
index a7bcd2d..32423a3 100644
--- a/Osu.Stubs/BeatmapTreeManager.cs
+++ b/Osu.Stubs/BeatmapTreeManager.cs
@@ -4,7 +4,7 @@
namespace Osu.Stubs;
///
-/// Original: osu.GameplayElements.Beatmaps:BeatmapTreeManager
+/// Original: osu.GameplayElements.Beatmaps.BeatmapTreeManager
/// b20240102.2: #=zV51QPv13Z_vZJRxEY28cvGye77gU6YZQsv_F5muuWuN62V5sIQ==
///
[UsedImplicitly]