diff --git a/src/d_main.c b/src/d_main.c
index a5e1a025424dd43ade761e52627e21ea382a84aa..ee946be4816891aecda133236ee58b7c4c3f681e 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -283,7 +283,7 @@ static void D_Display(void)
 		if (rendermode != render_none)
 		{
 			// Fade to black first
-			if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction)) // fades to black on its own timing, always
+			if ((wipegamestate != -2) // fades to black on its own timing, always
 			 && wipedefs[wipedefindex] != UINT8_MAX)
 			{
 				F_WipeStartScreen();
@@ -361,88 +361,95 @@ static void D_Display(void)
 			break;
 	}
 
-	// clean up border stuff
-	// see if the border needs to be initially drawn
-	if (gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))
+	// STUPID race condition...
+	if (wipegamestate == GS_INTRO && gamestate == GS_TITLESCREEN)
+		wipegamestate = -2;
+	else
 	{
-		// draw the view directly
+		wipegamestate = gamestate;
 
-		if (!automapactive && !dedicated && cv_renderview.value)
+		// clean up border stuff
+		// see if the border needs to be initially drawn
+		if (gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))
 		{
-			if (players[displayplayer].mo || players[displayplayer].playerstate == PST_DEAD)
-			{
-				topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
-				objectsdrawn = 0;
-#ifdef HWRENDER
-				if (rendermode != render_soft)
-					HWR_RenderPlayerView(0, &players[displayplayer]);
-				else
-#endif
-				if (rendermode != render_none)
-					R_RenderPlayerView(&players[displayplayer]);
-			}
+			// draw the view directly
 
-			// render the second screen
-			if (splitscreen && players[secondarydisplayplayer].mo)
+			if (!automapactive && !dedicated && cv_renderview.value)
 			{
-#ifdef HWRENDER
-				if (rendermode != render_soft)
-					HWR_RenderPlayerView(1, &players[secondarydisplayplayer]);
-				else
-#endif
-				if (rendermode != render_none)
+				if (players[displayplayer].mo || players[displayplayer].playerstate == PST_DEAD)
 				{
-					viewwindowy = vid.height / 2;
-					M_Memcpy(ylookup, ylookup2, viewheight*sizeof (ylookup[0]));
-
 					topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
+					objectsdrawn = 0;
+	#ifdef HWRENDER
+					if (rendermode != render_soft)
+						HWR_RenderPlayerView(0, &players[displayplayer]);
+					else
+	#endif
+					if (rendermode != render_none)
+						R_RenderPlayerView(&players[displayplayer]);
+				}
 
-					R_RenderPlayerView(&players[secondarydisplayplayer]);
+				// render the second screen
+				if (splitscreen && players[secondarydisplayplayer].mo)
+				{
+	#ifdef HWRENDER
+					if (rendermode != render_soft)
+						HWR_RenderPlayerView(1, &players[secondarydisplayplayer]);
+					else
+	#endif
+					if (rendermode != render_none)
+					{
+						viewwindowy = vid.height / 2;
+						M_Memcpy(ylookup, ylookup2, viewheight*sizeof (ylookup[0]));
+
+						topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
+
+						R_RenderPlayerView(&players[secondarydisplayplayer]);
+
+						viewwindowy = 0;
+						M_Memcpy(ylookup, ylookup1, viewheight*sizeof (ylookup[0]));
+					}
+				}
 
-					viewwindowy = 0;
-					M_Memcpy(ylookup, ylookup1, viewheight*sizeof (ylookup[0]));
+				// Image postprocessing effect
+				if (rendermode == render_soft)
+				{
+					if (postimgtype)
+						V_DoPostProcessor(0, postimgtype, postimgparam);
+					if (postimgtype2)
+						V_DoPostProcessor(1, postimgtype2, postimgparam2);
 				}
 			}
 
-			// Image postprocessing effect
-			if (rendermode == render_soft)
+			if (lastdraw)
 			{
-				if (postimgtype)
-					V_DoPostProcessor(0, postimgtype, postimgparam);
-				if (postimgtype2)
-					V_DoPostProcessor(1, postimgtype2, postimgparam2);
+				if (rendermode == render_soft)
+				{
+					VID_BlitLinearScreen(screens[0], screens[1], vid.width*vid.bpp, vid.height, vid.width*vid.bpp, vid.rowbytes);
+					usebuffer = true;
+				}
+				lastdraw = false;
 			}
-		}
 
-		if (lastdraw)
-		{
-			if (rendermode == render_soft)
+			if (gamestate == GS_LEVEL)
 			{
-				VID_BlitLinearScreen(screens[0], screens[1], vid.width*vid.bpp, vid.height, vid.width*vid.bpp, vid.rowbytes);
-				usebuffer = true;
+				ST_Drawer();
+				HU_Drawer();
 			}
-			lastdraw = false;
-		}
-
-		if (gamestate == GS_LEVEL)
-		{
-			ST_Drawer();
-			HU_Drawer();
+			else
+				F_TitleScreenDrawer();
 		}
-		else
-			F_TitleScreenDrawer();
 	}
 
 	// change gamma if needed
 	// (GS_LEVEL handles this already due to level-specific palettes)
-	if (forcerefresh && gamestate != GS_LEVEL)
+	if (forcerefresh && !(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction)))
 		V_SetPalette(0);
 
-	wipegamestate = gamestate;
-
 	// draw pause pic
 	if (paused && cv_showhud.value && (!menuactive || netgame))
 	{
+#if 0
 		INT32 py;
 		patch_t *patch;
 		if (automapactive)
@@ -451,6 +458,11 @@ static void D_Display(void)
 			py = viewwindowy + 4;
 		patch = W_CachePatchName("M_PAUSE", PU_CACHE);
 		V_DrawScaledPatch(viewwindowx + (BASEVIDWIDTH - SHORT(patch->width))/2, py, 0, patch);
+#else
+		INT32 y = ((automapactive) ? (32) : (BASEVIDHEIGHT/2));
+		M_DrawTextBox((BASEVIDWIDTH/2) - (60), y - (16), 13, 2);
+		V_DrawCenteredString(BASEVIDWIDTH/2, y - (4), V_YELLOWMAP, "Game Paused");
+#endif
 	}
 
 	// vid size change is now finished if it was on...
diff --git a/src/f_finale.c b/src/f_finale.c
index db62ddf0924d2fcd389f356a3290b6aa667a51c3..2b0c385429df0716fd2a08a8ce0b6a0b5ae7ae50 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -29,6 +29,7 @@
 #include "g_input.h"
 #include "console.h"
 #include "m_random.h"
+#include "m_misc.h" // moviemode functionality
 #include "y_inter.h"
 #include "m_cond.h"
 #include "p_local.h"
@@ -816,18 +817,27 @@ void F_IntroDrawer(void)
 
 			// Stay on black for a bit. =)
 			{
-				tic_t quittime;
-				quittime = I_GetTime() + NEWTICRATE*2; // Shortened the quit time, used to be 2 seconds
-				while (quittime > I_GetTime())
+				tic_t nowtime, quittime, lasttime;
+				nowtime = lasttime = I_GetTime();
+				quittime = nowtime + NEWTICRATE*2; // Shortened the quit time, used to be 2 seconds
+				while (quittime > nowtime)
 				{
+					while (!((nowtime = I_GetTime()) - lasttime))
+						I_Sleep();
+					lasttime = nowtime;
+
 					I_OsPolling();
 					I_UpdateNoBlit();
 					M_Drawer(); // menu is drawn even on top of wipes
 					I_FinishUpdate(); // Update the screen with the image Tails 06-19-2001
+
+					if (moviemode) // make sure we save frames for the white hold too
+						M_SaveFrame();
 				}
 			}
 
 			D_StartTitle();
+			wipegamestate = GS_INTRO;
 			return;
 		}
 		F_NewCutscene(introtext[++intro_scenenum]);
@@ -1532,7 +1542,7 @@ void F_TitleScreenDrawer(void)
 	if (!titlemapinaction)
 		F_SkyScroll(titlescrollspeed);
 
-	// Don't draw outside of the title screewn, or if the patch isn't there.
+	// Don't draw outside of the title screen, or if the patch isn't there.
 	if (!ttwing || (gamestate != GS_TITLESCREEN && gamestate != GS_WAITINGPLAYERS))
 		return;
 
@@ -1877,6 +1887,9 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset
 
 	G_SetGamestate(GS_CUTSCENE);
 
+	if (wipegamestate == GS_CUTSCENE)
+		wipegamestate = -1;
+
 	gameaction = ga_nothing;
 	paused = false;
 	CON_ToggleOff();
diff --git a/src/p_setup.c b/src/p_setup.c
index 11e6b4ef72a1fbc87d248c08a37d95bd20cf009f..a1ef08c5d180713dcf3fcf6f11c6d840d56f48db 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -56,8 +56,11 @@
 
 #include "filesrch.h" // refreshdirmenu
 
-// wipes
-#include "f_finale.h"
+#ifdef HAVE_BLUA
+#include "lua_hud.h" // level title
+#endif
+
+#include "f_finale.h" // wipes
 
 #include "md5.h" // map MD5
 
@@ -2673,7 +2676,6 @@ boolean P_SetupLevel(boolean skipprecip)
 	CON_Drawer(); // let the user know what we are going to do
 	I_FinishUpdate(); // page flip or blit buffer
 
-
 	// Reset the palette
 	if (rendermode != render_none)
 		V_SetPaletteLump("PLAYPAL");
@@ -2726,6 +2728,9 @@ boolean P_SetupLevel(boolean skipprecip)
 	// will be set by player think.
 	players[consoleplayer].viewz = 1;
 
+	// Cancel all d_main.c fadeouts (keep fade in though).
+	wipegamestate = -2;
+
 	// Special stage fade to white
 	// This is handled BEFORE sounds are stopped.
 	if (modeattacking && pausedelay == INT32_MIN)
@@ -2763,41 +2768,42 @@ boolean P_SetupLevel(boolean skipprecip)
 	S_StopSounds();
 	S_ClearSfx();
 
-	// As oddly named as this is, this handles music only.
-	// We should be fine starting it here.
-	/// ... as long as this isn't a titlemap transition, that is
 	if (!titlemapinaction)
+	{
+		// As oddly named as this is, this handles music only.
+		// We should be fine starting it here.
 		S_Start();
 
-	// Let's fade to black here
-	// But only if we didn't do the special stage wipe
-	if (rendermode != render_none && !ranspecialwipe)
-	{
-		F_WipeStartScreen();
-		V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
+		// Let's fade to black here
+		// But only if we didn't do the special stage wipe
+		if (rendermode != render_none && !ranspecialwipe)
+		{
+			F_WipeStartScreen();
+			V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
 
-		F_WipeEndScreen();
-		F_RunWipe(wipedefs[wipe_level_toblack], false);
-	}
+			F_WipeEndScreen();
+			F_RunWipe(wipedefs[wipe_level_toblack], false);
+		}
 
-	if (ranspecialwipe == 2)
-	{
-		pausedelay = -NEWTICRATE;
-		S_StartSound(NULL, sfx_s3k73);
-	}
+		if (ranspecialwipe == 2)
+		{
+			pausedelay = -NEWTICRATE;
+			S_StartSound(NULL, sfx_s3k73);
+		}
 
-	// Print "SPEEDING OFF TO [ZONE] [ACT 1]..."
-	if (!titlemapinaction && rendermode != render_none)
-	{
-		// Don't include these in the fade!
-		char tx[64];
-		V_DrawSmallString(1, 191, V_ALLOWLOWERCASE, M_GetText("Speeding off to..."));
-		snprintf(tx, 63, "%s%s%s",
-			mapheaderinfo[gamemap-1]->lvlttl,
-			(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " ZONE",
-			(mapheaderinfo[gamemap-1]->actnum > 0) ? va(", Act %d",mapheaderinfo[gamemap-1]->actnum) : "");
-		V_DrawSmallString(1, 195, V_ALLOWLOWERCASE, tx);
-		I_UpdateNoVsync();
+		// Print "SPEEDING OFF TO [ZONE] [ACT 1]..."
+		if (rendermode != render_none)
+		{
+			// Don't include these in the fade!
+			char tx[64];
+			V_DrawSmallString(1, 191, V_ALLOWLOWERCASE, M_GetText("Speeding off to..."));
+			snprintf(tx, 63, "%s%s%s",
+				mapheaderinfo[gamemap-1]->lvlttl,
+				(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " ZONE",
+				(mapheaderinfo[gamemap-1]->actnum > 0) ? va(", Act %d",mapheaderinfo[gamemap-1]->actnum) : "");
+			V_DrawSmallString(1, 195, V_ALLOWLOWERCASE, tx);
+			I_UpdateNoVsync();
+		}
 	}
 
 #ifdef HAVE_BLUA
@@ -3176,7 +3182,7 @@ boolean P_SetupLevel(boolean skipprecip)
 	P_MapEnd();
 
 	// Remove the loading shit from the screen
-	if (rendermode != render_none)
+	if (rendermode != render_none && !titlemapinaction)
 		V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (ranspecialwipe) ? 0 : 31);
 
 	if (precache || dedicated)
@@ -3227,6 +3233,45 @@ boolean P_SetupLevel(boolean skipprecip)
 #endif
 	}
 
+	// Stage title!
+	if (rendermode != render_none
+		&& (!titlemapinaction)
+		&& ranspecialwipe != 2
+		&& *mapheaderinfo[gamemap-1]->lvlttl != '\0'
+#ifdef HAVE_BLUA
+		&& LUA_HudEnabled(hud_stagetitle)
+#endif
+	)
+	{
+		tic_t starttime = I_GetTime();
+		tic_t endtime = starttime + (10*NEWTICRATERATIO);
+		tic_t nowtime = starttime;
+		tic_t lasttime = starttime;
+		while (nowtime < endtime)
+		{
+			// draw loop
+			while (!((nowtime = I_GetTime()) - lasttime))
+				I_Sleep();
+			lasttime = nowtime;
+
+			V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (ranspecialwipe) ? 0 : 31);
+			stplyr = &players[consoleplayer];
+			ST_drawLevelTitle(nowtime - starttime);
+			if (splitscreen)
+			{
+				stplyr = &players[secondarydisplayplayer];
+				ST_drawLevelTitle(nowtime - starttime);
+			}
+
+			I_OsPolling();
+			I_UpdateNoBlit();
+			I_FinishUpdate(); // page flip or blit buffer
+
+			if (moviemode) // make sure we save frames for the white hold too
+				M_SaveFrame();
+		}
+	}
+
 	return true;
 }
 
diff --git a/src/p_user.c b/src/p_user.c
index 2bcb3bc6c8f102bd1b5023a6e667972567979547..40e5f7a87aba4165ab4c7dc017e06d3ef29864cf 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -766,8 +766,8 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 
 		// Don't show before title card
 		// Not consistency safe, but this only affects drawing
-		if (timeinmap + 40 < 110)
-			player->texttimer = (UINT8)(110 - timeinmap);
+		if (timeinmap + 40 < (110 - 70))
+			player->texttimer = (UINT8)((110 - 70) - timeinmap);
 	}
 
 	player->powers[pw_carry] = CR_NIGHTSMODE;
diff --git a/src/st_stuff.c b/src/st_stuff.c
index 6984115331aa9500d679a78672f2699f4973e33e..6f9b0c182253bf3d8423945cc1962ea9b5c76f0c 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -1113,7 +1113,7 @@ static void ST_drawInput(void)
 		V_DrawThinString(x, y, hudinfo[HUD_LIVES].f|((leveltime & 4) ? V_YELLOWMAP : V_REDMAP), "BAD DEMO!!");
 }
 
-static void ST_drawLevelTitle(void)
+void ST_drawLevelTitle(tic_t titletime)
 {
 	char *lvlttl = mapheaderinfo[gamemap-1]->lvlttl;
 	char *subttl = mapheaderinfo[gamemap-1]->subttl;
@@ -1121,7 +1121,7 @@ static void ST_drawLevelTitle(void)
 	INT32 lvlttly, zoney, lvlttlxpos, ttlnumxpos, zonexpos;
 	INT32 subttlxpos = BASEVIDWIDTH/2;
 
-	if (!(timeinmap > 2 && timeinmap-3 < 110))
+	if (!(titletime > 2 && titletime-3 < 110))
 		return;
 
 	if (actnum > 0)
@@ -1144,22 +1144,22 @@ static void ST_drawLevelTitle(void)
 #define MIDZONEY 105
 #define MIDDIFF 4
 
-	if (timeinmap < 10)
+	if (titletime < 10)
 	{
-		fixed_t z = ((timeinmap - 3)<<FRACBITS)/7;
+		fixed_t z = ((titletime - 3)<<FRACBITS)/7;
 		INT32 ttlh = V_LevelNameHeight(lvlttl);
 		zoney = (200<<FRACBITS) - ((200 - (MIDZONEY + MIDDIFF))*z);
 		lvlttly = ((MIDTTLY + ttlh - MIDDIFF)*z) - (ttlh<<FRACBITS);
 	}
-	else if (timeinmap < 105)
+	else if (titletime < 105)
 	{
-		fixed_t z = (((timeinmap - 10)*MIDDIFF)<<(FRACBITS+1))/95;
+		fixed_t z = (((titletime - 10)*MIDDIFF)<<(FRACBITS+1))/95;
 		zoney = ((MIDZONEY + MIDDIFF)<<FRACBITS) - z;
 		lvlttly = ((MIDTTLY - MIDDIFF)<<FRACBITS) + z;
 	}
 	else
 	{
-		fixed_t z = ((timeinmap - 105)<<FRACBITS)/7;
+		fixed_t z = ((titletime - 105)<<FRACBITS)/7;
 		INT32 zoneh = V_LevelNameHeight(M_GetText("ZONE"));
 		zoney = (MIDZONEY + zoneh - MIDDIFF)*(FRACUNIT - z) - (zoneh<<FRACBITS);
 		lvlttly = ((MIDTTLY + MIDDIFF)<<FRACBITS) + ((200 - (MIDTTLY + MIDDIFF))*z);
@@ -1170,8 +1170,8 @@ static void ST_drawLevelTitle(void)
 #undef MIDDIFF
 #else
 	// There's no consistent algorithm that can accurately define the old positions
-	// so I just ended up resorting to a single switct statement to define them
-	switch (timeinmap-3)
+	// so I just ended up resorting to a single switch statement to define them
+	switch (titletime-3)
 	{
 		case 0:   zoney = 200; lvlttly =   0; break;
 		case 1:   zoney = 188; lvlttly =  12; break;
@@ -2386,7 +2386,7 @@ static void ST_overlayDrawer(void)
 	&& LUA_HudEnabled(hud_stagetitle)
 #endif
 	)
-		ST_drawLevelTitle();
+		ST_drawLevelTitle(timeinmap+70);
 
 	if (!hu_showscores && (netgame || multiplayer)
 #ifdef HAVE_BLUA
diff --git a/src/st_stuff.h b/src/st_stuff.h
index df0adace3f4a451c2d2120f8410e4552edfb8b0c..8f73eb2e6330acaa6d3097b5150a283f64dc6e93 100644
--- a/src/st_stuff.h
+++ b/src/st_stuff.h
@@ -47,6 +47,9 @@ void ST_ReloadSkinFaceGraphics(void);
 
 void ST_doPaletteStuff(void);
 
+// level title draw
+void ST_drawLevelTitle(tic_t titletime);
+
 // return if player a is in the same team as player b
 boolean ST_SameTeam(player_t *a, player_t *b);