diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c
index 9170d79d753c9f920a48da1f3cf59828b95fbc9e..75a77e9f0e6b975b02f4906310a92cc35f87233c 100644
--- a/src/hardware/hw_cache.c
+++ b/src/hardware/hw_cache.c
@@ -1497,6 +1497,32 @@ UINT32 HWR_CreateLightTable(UINT8 *lighttable)
 	return id;
 }
 
+// get hwr lighttable id for colormap, create it if it doesn't already exist
+UINT32 HWR_GetLightTableID(extracolormap_t *colormap)
+{
+	boolean default_colormap = false;
+	if (!colormap)
+	{
+		colormap = R_GetDefaultColormap(); // a place to store the hw lighttable id
+		// alternatively could just store the id in a global variable if there are issues
+		default_colormap = true;
+	}
+
+	// create hw lighttable if there isn't one
+	if (!colormap->gl_lighttable_id)
+	{
+		UINT8 *colormap_pointer;
+
+		if (default_colormap)
+			colormap_pointer = colormaps; // don't actually use the data from the "default colormap"
+		else
+			colormap_pointer = colormap->colormap;
+		colormap->gl_lighttable_id = HWR_CreateLightTable(colormap_pointer);
+	}
+
+	return colormap->gl_lighttable_id;
+}
+
 // Note: all hardware lighttable ids assigned before this
 // call become invalid and must not be used.
 void HWR_ClearLightTables(void)
diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h
index b175c21e0b84315a2cab4479cdb9f75c13beb470..bf1bed9ee06cf75980dbc481a4732b7339f74d4c 100644
--- a/src/hardware/hw_defs.h
+++ b/src/hardware/hw_defs.h
@@ -153,8 +153,9 @@ enum
 	SHADER_FOG,
 	SHADER_SKY,
 	SHADER_PALETTE_POSTPROCESS,
+	SHADER_UI_COLORMAP_FADE,
 
-	NUMSHADERTARGETS,
+	NUMSHADERTARGETS
 };
 
 // Maximum amount of shader programs
@@ -335,7 +336,7 @@ enum hwdscreentexture
 	HWD_SCREENTEXTURE_WIPE_START, // source image for the wipe/fade effect
 	HWD_SCREENTEXTURE_WIPE_END,   // destination image for the wipe/fade effect
 	HWD_SCREENTEXTURE_GENERIC1,   // underwater/heat effect, intermission background
-	HWD_SCREENTEXTURE_GENERIC2,   // screen before palette rendering's postprocessing
+	HWD_SCREENTEXTURE_GENERIC2,   // palette-based colormap fade, screen before palette rendering's postprocessing
 	HWD_SCREENTEXTURE_GENERIC3,   // screen after palette rendering's postprocessing
 	NUMSCREENTEXTURES,            // (generic3 is unused if palette rendering is disabled)
 };
diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c
index 90b9d0372e2c0d4f961588f12ca9c5728a7fe705..e8365ee2f54324ab0dc51bb803fd09c7f290e18c 100644
--- a/src/hardware/hw_draw.c
+++ b/src/hardware/hw_draw.c
@@ -797,6 +797,19 @@ void HWR_FadeScreenMenuBack(UINT16 color, UINT8 strength)
 
 	if (color & 0xFF00) // Do COLORMAP fade.
 	{
+		if (HWR_ShouldUsePaletteRendering())
+		{
+			const hwdscreentexture_t scr_tex = HWD_SCREENTEXTURE_GENERIC2;
+
+			Surf.LightTableId = HWR_GetLightTableID(NULL);
+			Surf.LightInfo.light_level = strength;
+			HWD.pfnMakeScreenTexture(scr_tex);
+			HWD.pfnSetShader(HWR_GetShaderFromTarget(SHADER_UI_COLORMAP_FADE));
+			HWD.pfnDrawScreenTexture(scr_tex, &Surf, PF_ColorMapped|PF_NoDepthTest);
+			HWD.pfnUnSetShader();
+
+			return;
+		}
 		Surf.PolyColor.rgba = UINT2RGBA(0x01010160);
 		Surf.PolyColor.s.alpha = (strength*8);
 	}
diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h
index 50354786566bc7f296910979f802d069ccd0ebe6..d4fe88d474cc71ac202c368ab320ca690aeeaeb6 100644
--- a/src/hardware/hw_drv.h
+++ b/src/hardware/hw_drv.h
@@ -56,7 +56,7 @@ EXPORT INT32 HWRAPI(GetTextureUsed) (void);
 
 EXPORT void HWRAPI(FlushScreenTextures) (void);
 EXPORT void HWRAPI(DoScreenWipe) (int wipeStart, int wipeEnd);
-EXPORT void HWRAPI(DrawScreenTexture) (int tex);
+EXPORT void HWRAPI(DrawScreenTexture) (int tex, FSurfaceInfo *surf, FBITFIELD polyflags);
 EXPORT void HWRAPI(MakeScreenTexture) (int tex);
 EXPORT void HWRAPI(DrawScreenFinalTexture) (int tex, int width, int height);
 
diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h
index 2c582d6d8a7901e1b7f55732be5e825f6a054d5d..70218de0d401d59f8d6018b08754d39b1080ed8a 100644
--- a/src/hardware/hw_glob.h
+++ b/src/hardware/hw_glob.h
@@ -132,6 +132,7 @@ void HWR_UnlockCachedPatch(GLPatch_t *gpatch);
 void HWR_SetPalette(RGBA_t *palette);
 void HWR_SetMapPalette(void);
 UINT32 HWR_CreateLightTable(UINT8 *lighttable);
+UINT32 HWR_GetLightTableID(extracolormap_t *colormap);
 void HWR_ClearLightTables(void);
 
 
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index f7752dfa90c25edb254fc0d328ec879dea0644a4..7dfe1bff9e2c93dbd5ba3c8fc35fe1353e097e8c 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -239,31 +239,9 @@ void HWR_Lighting(FSurfaceInfo *Surface, INT32 light_level, extracolormap_t *col
 	Surface->LightInfo.fade_end = (colormap != NULL) ? colormap->fadeend : 31;
 
 	if (HWR_ShouldUsePaletteRendering())
-	{
-		boolean default_colormap = false;
-		if (!colormap)
-		{
-			colormap = R_GetDefaultColormap(); // a place to store the hw lighttable id
-			// alternatively could just store the id in a global variable if there are issues
-			default_colormap = true;
-		}
-		// create hw lighttable if there isn't one
-		if (!colormap->gl_lighttable_id)
-		{
-			UINT8 *colormap_pointer;
-
-			if (default_colormap)
-				colormap_pointer = colormaps; // don't actually use the data from the "default colormap"
-			else
-				colormap_pointer = colormap->colormap;
-			colormap->gl_lighttable_id = HWR_CreateLightTable(colormap_pointer);
-		}
-		Surface->LightTableId = colormap->gl_lighttable_id;
-	}
+		Surface->LightTableId = HWR_GetLightTableID(colormap);
 	else
-	{
 		Surface->LightTableId = 0;
-	}
 }
 
 UINT8 HWR_FogBlockAlpha(INT32 light, extracolormap_t *colormap) // Let's see if this can work
@@ -6835,7 +6813,7 @@ void HWR_EndScreenWipe(void)
 
 void HWR_DrawIntermissionBG(void)
 {
-	HWD.pfnDrawScreenTexture(HWD_SCREENTEXTURE_GENERIC1);
+	HWD.pfnDrawScreenTexture(HWD_SCREENTEXTURE_GENERIC1, NULL, 0);
 }
 
 //
diff --git a/src/hardware/hw_shaders.c b/src/hardware/hw_shaders.c
index d4d7f21bce7974fc6105cd28923f8d4b36609612..6f81a55ef2b2f6887066344d996f32eb29a642a5 100644
--- a/src/hardware/hw_shaders.c
+++ b/src/hardware/hw_shaders.c
@@ -315,6 +315,18 @@
 		"gl_FragColor = final_color;\n" \
 	"}\0"
 
+#define GLSL_UI_COLORMAP_FADE_SHADER \
+	"uniform sampler2D tex;\n" \
+	"uniform float lighting;\n" \
+	"uniform sampler3D palette_lookup_tex;\n" \
+	"uniform sampler2D lighttable_tex;\n" \
+	"void main(void) {\n" \
+		"vec4 texel = texture2D(tex, gl_TexCoord[0].st);\n" \
+		"float tex_pal_idx = texture3D(palette_lookup_tex, vec3((texel * 63.0 + 0.5) / 64.0))[0] * 255.0;\n" \
+		"vec2 lighttable_coord = vec2((tex_pal_idx + 0.5) / 256.0, (lighting + 0.5) / 32.0);\n" \
+		"gl_FragColor = texture2D(lighttable_tex, lighttable_coord);\n" \
+	"}\0" \
+
 // ================
 //  Shader sources
 // ================
@@ -347,6 +359,9 @@ static struct {
 	// Palette postprocess shader
 	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_PALETTE_POSTPROCESS_SHADER},
 
+	// UI colormap fade shader
+	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_UI_COLORMAP_FADE_SHADER},
+
 	{NULL, NULL},
 };
 
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index efeb8289d5c8fe3f4a386dd588122a8ae8b1f450..6152b2c433871a7031378a71ecb2f2c003f22193 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -1181,11 +1181,11 @@ EXPORT void HWRAPI(ReadScreenTexture) (int tex, UINT8 *dst_data)
 	// and draw generic2 back after reading the framebuffer.
 	// this hack is for some reason **much** faster than the simple solution of using glGetTexImage.
 	if (tex != HWD_SCREENTEXTURE_GENERIC2)
-		DrawScreenTexture(tex);
+		DrawScreenTexture(tex, NULL, 0);
 	pglPixelStorei(GL_PACK_ALIGNMENT, 1);
 	pglReadPixels(0, 0, screen_width, screen_height, GL_RGB, GL_UNSIGNED_BYTE, dst_data);
 	if (tex != HWD_SCREENTEXTURE_GENERIC2)
-		DrawScreenTexture(HWD_SCREENTEXTURE_GENERIC2);
+		DrawScreenTexture(HWD_SCREENTEXTURE_GENERIC2, NULL, 0);
 	// Flip image upside down.
 	// In other words, convert OpenGL's "bottom->top" row order into "top->bottom".
 	for(i = 0; i < screen_height/2; i++)
@@ -1965,6 +1965,7 @@ static void Shader_CompileError(const char *message, GLuint program, INT32 shade
 }
 
 // code that is common between DrawPolygon and DrawIndexedTriangles
+// DrawScreenTexture also can use this function for fancier screen texture drawing
 // the corona thing is there too, i have no idea if that stuff works with DrawIndexedTriangles and batching
 static void PreparePolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, FBITFIELD PolyFlags)
 {
@@ -2978,7 +2979,7 @@ EXPORT void HWRAPI(FlushScreenTextures) (void)
 		screenTextures[i] = 0;
 }
 
-EXPORT void HWRAPI(DrawScreenTexture)(int tex)
+EXPORT void HWRAPI(DrawScreenTexture)(int tex, FSurfaceInfo *surf, FBITFIELD polyflags)
 {
 	float xfix, yfix;
 	INT32 texsize = 512;
@@ -3015,7 +3016,10 @@ EXPORT void HWRAPI(DrawScreenTexture)(int tex)
 	pglClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
 
 	pglBindTexture(GL_TEXTURE_2D, screenTextures[tex]);
-	Shader_SetUniforms(NULL, NULL, NULL, NULL); // prepare shader, if it is enabled
+	if (surf)
+		PreparePolygon(surf, NULL, polyflags);
+	else
+		Shader_SetUniforms(NULL, NULL, NULL, NULL); // prepare shader, if it is enabled
 	pglColor4ubv(white);
 
 	pglTexCoordPointer(2, GL_FLOAT, 0, fix);
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index ad539e76bbed75ed50719ba1b022dfcad4d23a90..da357354c3b5d569edf1bbfbbb610f1992a7fa14 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -1260,7 +1260,7 @@ void I_FinishUpdate(void)
 			HWD.pfnMakeScreenTexture(HWD_SCREENTEXTURE_GENERIC2);
 			HWD.pfnSetSpecialState(HWD_SET_SHADERS, 1);
 			HWD.pfnSetShader(HWR_GetShaderFromTarget(SHADER_PALETTE_POSTPROCESS));
-			HWD.pfnDrawScreenTexture(HWD_SCREENTEXTURE_GENERIC2);
+			HWD.pfnDrawScreenTexture(HWD_SCREENTEXTURE_GENERIC2, NULL, 0);
 			HWD.pfnUnSetShader();
 			HWD.pfnSetSpecialState(HWD_SET_SHADERS, 0);
 		}
diff --git a/src/sdl/ogl_sdl.c b/src/sdl/ogl_sdl.c
index 91df683ecf7e4e349e30e37c7042bf349811bd88..cd09dc07127db11919d8094b185bdaf84bba7f64 100644
--- a/src/sdl/ogl_sdl.c
+++ b/src/sdl/ogl_sdl.c
@@ -234,7 +234,7 @@ void OglSdlFinishUpdate(boolean waitvbl)
 	//			effects that want to take the old screen can do so after this
 	// Generic2 has the screen image without palette rendering brightness adjustments.
 	// Using that here will prevent brightness adjustments being applied twice.
-	DrawScreenTexture(HWD_SCREENTEXTURE_GENERIC2);
+	DrawScreenTexture(HWD_SCREENTEXTURE_GENERIC2, NULL, 0);
 }
 
 EXPORT void HWRAPI(OglSdlSetPalette) (RGBA_t *palette)