diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 72f60aa9f68c6ec57e9bc9605dfcb8e25500f454..6a871057e148d81e56cafd008bf348c6602fdbab 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -1125,7 +1125,7 @@ static inline void CL_DrawConnectionStatus(void)
 	INT32 ccstime = I_GetTime();
 
 	// Draw background fade
-	V_DrawFadeScreen();
+	V_DrawFadeScreen(0xFF00, 16);
 
 	// Draw the bottom box.
 	M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1);
diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c
index 0a09a2b410cc505493639f2c1b1a16b1982c72ab..0e10c6473f666727f7f7c98f14bd977d9f72d222 100644
--- a/src/hardware/hw_draw.c
+++ b/src/hardware/hw_draw.c
@@ -154,9 +154,7 @@ void HWR_DrawFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale,
 	float pdupx = FIXED_TO_FLOAT(vid.fdupx)*2.0f*FIXED_TO_FLOAT(pscale);
 	float pdupy = FIXED_TO_FLOAT(vid.fdupy)*2.0f*FIXED_TO_FLOAT(pscale);
 
-	if (alphalevel == 12)
-		alphalevel = 0;
-	else if (alphalevel >= 10 && alphalevel < 13)
+	if (alphalevel >= 10 && alphalevel < 13)
 		return;
 
 	// make patch ready in hardware cache
@@ -283,9 +281,7 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal
 	float pdupx = FIXED_TO_FLOAT(vid.fdupx)*2.0f*FIXED_TO_FLOAT(pscale);
 	float pdupy = FIXED_TO_FLOAT(vid.fdupy)*2.0f*FIXED_TO_FLOAT(pscale);
 
-	if (alphalevel == 12)
-		alphalevel = 0;
-	else if (alphalevel >= 10 && alphalevel < 13)
+	if (alphalevel >= 10 && alphalevel < 13)
 		return;
 
 	// make patch ready in hardware cache
@@ -494,18 +490,14 @@ void HWR_DrawFlatFill (INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatlumpnum
 //  | /|
 //  |/ |
 //  0--1
-void HWR_FadeScreenMenuBack(UINT32 color, INT32 height)
+void HWR_FadeScreenMenuBack(UINT16 color, UINT8 strength)
 {
 	FOutVector  v[4];
 	FSurfaceInfo Surf;
 
-	// setup some neat-o translucency effect
-	if (!height) //cool hack 0 height is full height
-		height = vid.height;
-
 	v[0].x = v[3].x = -1.0f;
 	v[2].x = v[1].x =  1.0f;
-	v[0].y = v[1].y =  1.0f-((height<<1)/(float)vid.height);
+	v[0].y = v[1].y = -1.0f;
 	v[2].y = v[3].y =  1.0f;
 	v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
 
@@ -514,8 +506,16 @@ void HWR_FadeScreenMenuBack(UINT32 color, INT32 height)
 	v[0].tow = v[1].tow = 1.0f;
 	v[2].tow = v[3].tow = 0.0f;
 
-	Surf.FlatColor.rgba = UINT2RGBA(color);
-	Surf.FlatColor.s.alpha = (UINT8)((0xff/2) * ((float)height / vid.height)); //calum: varies console alpha
+	if (color & 0xFF00) // Do COLORMAP fade.
+	{
+		Surf.FlatColor.rgba = UINT2RGBA(0x01010160);
+		Surf.FlatColor.s.alpha = 0xFF - (strength*8);
+	}
+	else // Do TRANSMAP** fade.
+	{
+		Surf.FlatColor.rgba = pLocalPalette[color].rgba;
+		Surf.FlatColor.s.alpha = (UINT8)((float)(10-strength)*25.5f);
+	}
 	HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
 }
 
diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h
index cb49f817ce1cf1f3e294755eebde0c19e9d08b74..1ae2d8fc39f578b7dc0a9687c18c8c90cde619b5 100644
--- a/src/hardware/hw_main.h
+++ b/src/hardware/hw_main.h
@@ -33,7 +33,7 @@ void HWR_Shutdown(void);
 
 void HWR_clearAutomap(void);
 void HWR_drawAMline(const fline_t *fl, INT32 color);
-void HWR_FadeScreenMenuBack(UINT32 color, INT32 height);
+void HWR_FadeScreenMenuBack(UINT16 color, UINT8 strength);
 void HWR_DrawConsoleBack(UINT32 color, INT32 height);
 void HWR_RenderSkyboxView(INT32 viewnumber, player_t *player);
 void HWR_RenderPlayerView(INT32 viewnumber, player_t *player);
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 97835d845934b95db6bdc1f723ba0fb27c3dcdbc..c592cc2f12fa283b9b5e3eaf2016ab7e16fcb1a4 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -679,6 +679,23 @@ static int libd_getColormap(lua_State *L)
 	return 1;
 }
 
+static int libd_fadeScreen(lua_State *L)
+{
+	UINT16 color = luaL_checkinteger(L, 1);
+	UINT8 strength = luaL_checkinteger(L, 2);
+	const UINT8 maxstrength = ((color & 0xFF00) ? 31 : 9);
+
+	HUDONLY
+	if (!strength)
+		return 0;
+
+	if (strength > maxstrength)
+		return luaL_error(L, "%s fade strength %d out of range (0 - %d)", ((color & 0xFF00) ? "COLORMAP" : "TRANSMAP"), strength, maxstrength);
+
+	V_DrawFadeScreen(color, strength);
+	return 0;
+}
+
 static int libd_width(lua_State *L)
 {
 	HUDONLY
@@ -733,6 +750,7 @@ static luaL_Reg lib_draw[] = {
 	{"drawString", libd_drawString},
 	{"stringWidth", libd_stringWidth},
 	{"getColormap", libd_getColormap},
+	{"fadeScreen", libd_fadeScreen},
 	{"width", libd_width},
 	{"height", libd_height},
 	{"dupx", libd_dupx},
diff --git a/src/m_menu.c b/src/m_menu.c
index 8a8e236542147192a5c9a28ea556e0e84efc4f8d..9f1b7efe7bd40a0c466ed7922cbdd24916289dcf 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -2583,7 +2583,7 @@ void M_Drawer(void)
 	{
 		// now that's more readable with a faded background (yeah like Quake...)
 		if (!WipeInAction)
-			V_DrawFadeScreen();
+			V_DrawFadeScreen(0xFF00, 16);
 
 		if (currentMenu->drawroutine)
 			currentMenu->drawroutine(); // call current menu Draw routine
diff --git a/src/v_video.c b/src/v_video.c
index 10d2d9af5acc2acbd56fe2a3ad4e8fb0549ad521..daf0514d5404102bda028469f552be35d95cc40f 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -1321,25 +1321,43 @@ void V_DrawPatchFill(patch_t *pat)
 //
 // Fade all the screen buffer, so that the menu is more readable,
 // especially now that we use the small hufont in the menus...
+// If color is 0x00 to 0xFF, draw transtable (strength range 0-10).
+// Else, use COLORMAP lump (strength range 0-31).
+// IF YOU ARE NOT CAREFUL, THIS CAN AND WILL CRASH!
+// I have kept the safety checks out of this function;
+// the v.fadeScreen Lua interface handles those.
 //
-void V_DrawFadeScreen(void)
+void V_DrawFadeScreen(UINT16 color, UINT8 strength)
 {
-	const UINT8 *fadetable = (UINT8 *)colormaps + 16*256;
-	const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
-	UINT8 *buf = screens[0];
+	if (!strength)
+		return;
+
+	if (!(color & 0xFF00) && strength == 10)
+	{
+		V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, color);
+		return;
+	}
 
 #ifdef HWRENDER
 	if (rendermode != render_soft && rendermode != render_none)
 	{
-		HWR_FadeScreenMenuBack(0x01010160, 0); // hack, 0 means full height
+		HWR_FadeScreenMenuBack(color, strength);
 		return;
 	}
 #endif
 
-	// heavily simplified -- we don't need to know x or y
-	// position when we're doing a full screen fade
-	for (; buf < deststop; ++buf)
-		*buf = fadetable[*buf];
+	{
+		const UINT8 *fadetable = ((color & 0xFF00) // Color is not palette index?
+		? ((UINT8 *)colormaps + strength*256) // Do COLORMAP fade.
+		: ((UINT8 *)transtables + ((10-strength)<<FF_TRANSSHIFT) + color*256)); // Else, do TRANSMAP** fade.
+		const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
+		UINT8 *buf = screens[0];
+
+		// heavily simplified -- we don't need to know x or y
+		// position when we're doing a full screen fade
+		for (; buf < deststop; ++buf)
+			*buf = fadetable[*buf];
+	}
 }
 
 // Simple translucency with one color, over a set number of lines starting from the top.
diff --git a/src/v_video.h b/src/v_video.h
index c5dfd35a500ec8be1e3fced1e2e4c63faf329490..ee244c6711eac55b9d863bd2c04e96971227a837 100644
--- a/src/v_video.h
+++ b/src/v_video.h
@@ -147,7 +147,7 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c);
 void V_DrawFlatFill(INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatnum);
 
 // fade down the screen buffer before drawing the menu over
-void V_DrawFadeScreen(void);
+void V_DrawFadeScreen(UINT16 color, UINT8 strength);
 
 void V_DrawFadeConsBack(INT32 plines);