diff --git a/src/f_finale.c b/src/f_finale.c
index ffbf5ee7ef0a445cb6327f23527a985598bafd0d..66957c92c0d0f8e2ed4656f297a8f9504315c6a7 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -45,7 +45,7 @@
 
 // Stage of animation:
 // 0 = text, 1 = art screen
-static INT32 finalecount;
+INT32 finalecount;
 INT32 titlescrollxspeed = 20;
 INT32 titlescrollyspeed = 0;
 UINT8 titlemapinaction = TITLEMAP_OFF;
@@ -2573,6 +2573,8 @@ void F_TitleScreenDrawer(void)
 {
 	boolean hidepics;
 	fixed_t sc = FRACUNIT / max(1, curttscale);
+	INT32 whitefade = 0;
+	UINT8 *whitecol[2] = {NULL, NULL};
 
 	if (modeattacking)
 		return; // We likely came here from retrying. Don't do a damn thing.
@@ -2658,16 +2660,40 @@ void F_TitleScreenDrawer(void)
 			//
 			if (finalecount <= 29)
 				V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
+			// Flash at tic 30, timed to O__TITLE percussion. Hold the flash until tic 34.
+			// After tic 34, fade the flash until tic 44.
+			else
+			{
+				if (finalecount > 29 && finalecount < 35)
+					V_DrawFadeScreen(0, (whitefade = 9));
+				else if (finalecount > 34 && 44-finalecount > 0 && 44-finalecount < 10)
+					V_DrawFadeScreen(0, 44-finalecount);
+				if (39-finalecount > 0)
+				{
+					whitefade = (9 - (39-finalecount))<<V_ALPHASHIFT;
+					whitecol[0] = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_SUPERGOLD3, GTC_CACHE);
+					whitecol[1] = R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE);
+				}
+			}
 
 			// Draw emblem
 			V_DrawSciencePatch(40<<FRACBITS, 20<<FRACBITS, 0, TTEMBL[0], sc);
 
+			if (whitecol[0])
+			{
+				V_DrawFixedPatch(40<<FRACBITS, 20<<FRACBITS, sc, whitefade, TTEMBL[0], whitecol[0]);
+				V_DrawFixedPatch(40<<FRACBITS, 20<<FRACBITS, sc, V_TRANSLUCENT + ((whitefade/2) & V_ALPHAMASK), TTEMBL[0], whitecol[1]);
+			}
+
 			// Animate SONIC ROBO BLAST 2 before the white flash at tic 30.
 			if (finalecount <= 29)
 			{
 				// Ribbon unfurls, revealing SONIC text, from tic 0 to tic 24. SONIC text is pre-baked into this ribbon graphic.
 				V_DrawSciencePatch(39<<FRACBITS, 88<<FRACBITS, 0, TTRIBB[min(max(0, finalecount), 24)], sc);
 
+				// Darken non-text things.
+				V_DrawFadeScreen(0xFF00, 12);
+
 				// Animate SONIC text while the ribbon unfurls, from tic 0 to tic 28.
 				if(finalecount >= 0)
 					V_DrawSciencePatch(89<<FRACBITS, 92<<FRACBITS, 0, TTSONT[min(finalecount, 28)], sc);
@@ -2692,6 +2718,7 @@ void F_TitleScreenDrawer(void)
 							case 8: case 7: fadeval = V_30TRANS; break;
 							case 6: case 5: fadeval = V_20TRANS; break;
 							case 4: case 3: fadeval = V_10TRANS; break;
+							default: break;
 						}
 					}
 					V_DrawSciencePatch(79<<FRACBITS, 132<<FRACBITS, fadeval, TTROBO[0], sc);
@@ -3112,9 +3139,15 @@ void F_TitleScreenDrawer(void)
 			// After tic 34, starting when the flash fades,
 			// draw the combined ribbon and SONIC ROBO BLAST 2 logo. Note the different Y value, because this
 			// graphic is cropped differently from the unfurling ribbon.
-			if (finalecount > 34)
+			if (finalecount > 29)
 				V_DrawSciencePatch(39<<FRACBITS, 93<<FRACBITS, 0, TTRBTX[0], sc);
 
+			if (whitecol[0])
+			{
+				V_DrawFixedPatch(39<<FRACBITS, 93<<FRACBITS, sc, whitefade, TTRBTX[0], whitecol[0]);
+				V_DrawFixedPatch(39<<FRACBITS, 93<<FRACBITS, sc, V_TRANSLUCENT + ((whitefade/2) & V_ALPHAMASK), TTRBTX[0], whitecol[1]);
+			}
+
 			//
 			// FRONT LAYER CHARACTERS
 			//
@@ -3253,13 +3286,6 @@ void F_TitleScreenDrawer(void)
 				}
 			}
 
-			// Flash at tic 30, timed to O__TITLE percussion. Hold the flash until tic 34.
-			// After tic 34, fade the flash until tic 44.
-			if (finalecount > 29 && finalecount < 35)
-				V_DrawFadeScreen(0, 9);
-			else if (finalecount > 34 && 44-finalecount > 0 && 44-finalecount < 10)
-				V_DrawFadeScreen(0, 44-finalecount);
-
 #undef CHARSTART
 #undef SONICSTART
 #undef SONICIDLE
diff --git a/src/f_finale.h b/src/f_finale.h
index f75f93c7745057f6b845054070af3a792bae6a3b..3fa7106a98e867909b077ccfd591879d1e346131 100644
--- a/src/f_finale.h
+++ b/src/f_finale.h
@@ -74,6 +74,7 @@ void F_StartContinue(void);
 void F_ContinueTicker(void);
 void F_ContinueDrawer(void);
 
+extern INT32 finalecount;
 extern INT32 titlescrollxspeed;
 extern INT32 titlescrollyspeed;
 
diff --git a/src/g_game.c b/src/g_game.c
index db80614bf5003bc7cff90aa72874c6bc3cfc35c2..63caaa15cb0879a6f6c1c495784eac116b3f33bb 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -1717,7 +1717,7 @@ boolean G_Responder(event_t *ev)
 	if (gameaction == ga_nothing && !singledemo &&
 		((demoplayback && !modeattacking && !titledemo) || gamestate == GS_TITLESCREEN))
 	{
-		if (ev->type == ev_keydown && ev->data1 != 301)
+		if (ev->type == ev_keydown && ev->data1 != 301 && !(gamestate == GS_TITLESCREEN && finalecount < TICRATE))
 		{
 			M_StartControlPanel();
 			return true;
diff --git a/src/m_menu.c b/src/m_menu.c
index 6a0a31bc1ae947e7ab8ed4828c8c173a94cf9b95..e772c94218498e6268e691795d5eb488659f8458 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -2985,6 +2985,9 @@ boolean M_Responder(event_t *ev)
 	|| gamestate == GS_CREDITS || gamestate == GS_EVALUATION || gamestate == GS_GAMEEND)
 		return false;
 
+	if (gamestate == GS_TITLESCREEN && finalecount < TICRATE)
+		return false;
+
 	if (noFurtherInput)
 	{
 		// Ignore input after enter/escape/other buttons