diff --git a/src/f_wipe.c b/src/f_wipe.c
index d048037d8d914eca193fd5581d4d0e9cdc244975..9adbcbc02f927d5fae5d61b9f22b54880410057f 100644
--- a/src/f_wipe.c
+++ b/src/f_wipe.c
@@ -375,11 +375,7 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu)
 
 		// draw level title
 		if ((WipeStageTitle && st_overlay)
-		&& *mapheaderinfo[gamemap-1]->lvlttl != '\0'
-#ifdef HAVE_BLUA
-		&& LUA_HudEnabled(hud_stagetitle)
-#endif
-		)
+		&& *mapheaderinfo[gamemap-1]->lvlttl != '\0')
 		{
 			ST_runTitleCard();
 			ST_drawWipeTitleCard();
diff --git a/src/g_game.c b/src/g_game.c
index 3b38c367d3b7c14aa1507c1acedb2d82e002ef17..a4dc16b954d6ba6b1103854cb066b6ffe2fd99fe 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -47,6 +47,10 @@
 #include "m_cond.h" // condition sets
 #include "md5.h" // demo checksums
 
+#ifdef HAVE_BLUA
+#include "lua_hud.h"
+#endif
+
 gameaction_t gameaction;
 gamestate_t gamestate = GS_NULL;
 UINT8 ultimatemode = false;
diff --git a/src/lua_hud.h b/src/lua_hud.h
index 7f928f7c4a1c82de246a417173166bfd4e81cb58..d1adef7dc09abb760151aa3c0322cf68078e8931 100644
--- a/src/lua_hud.h
+++ b/src/lua_hud.h
@@ -43,3 +43,4 @@ boolean LUA_HudEnabled(enum hud option);
 void LUAh_GameHUD(player_t *stplyr);
 void LUAh_ScoresHUD(void);
 void LUAh_TitleHUD(void);
+void LUAh_TitleCardHUD(player_t *stplyr);
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index f12bb9ec56994b56ae9592aad3b857ef9abe7df8..9b12dd3c034f824f2b551d3f349b58e9716874f0 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -92,12 +92,14 @@ static const char *const patch_opt[] = {
 enum hudhook {
 	hudhook_game = 0,
 	hudhook_scores,
-	hudhook_title
+	hudhook_title,
+	hudhook_titlecard
 };
 static const char *const hudhook_opt[] = {
 	"game",
 	"scores",
 	"title",
+	"titlecard",
 	NULL};
 
 // alignment types for v.drawString
@@ -1052,6 +1054,9 @@ int LUA_HudLib(lua_State *L)
 
 		lua_newtable(L);
 		lua_rawseti(L, -2, 4); // HUD[3] = title rendering functions array
+
+		lua_newtable(L);
+		lua_rawseti(L, -2, 5); // HUD[4] = title card rendering functions array
 	lua_setfield(L, LUA_REGISTRYINDEX, "HUD");
 
 	luaL_newmetatable(L, META_HUDINFO);
@@ -1189,4 +1194,38 @@ void LUAh_TitleHUD(void)
 	hud_running = false;
 }
 
+void LUAh_TitleCardHUD(player_t *stplayr)
+{
+	if (!gL || !(hudAvailable & (1<<hudhook_titlecard)))
+		return;
+
+	hud_running = true;
+	lua_pop(gL, -1);
+
+	lua_getfield(gL, LUA_REGISTRYINDEX, "HUD");
+	I_Assert(lua_istable(gL, -1));
+	lua_rawgeti(gL, -1, 5); // HUD[5] = rendering funcs
+	I_Assert(lua_istable(gL, -1));
+
+	lua_rawgeti(gL, -2, 1); // HUD[1] = lib_draw
+	I_Assert(lua_istable(gL, -1));
+	lua_remove(gL, -3); // pop HUD
+
+	LUA_PushUserdata(gL, stplayr, META_PLAYER);
+	lua_pushinteger(gL, lt_ticker);
+	lua_pushinteger(gL, (lt_endtime + TICRATE));
+	lua_pushnil(gL);
+
+	while (lua_next(gL, -6) != 0) {
+		lua_pushvalue(gL, -6); // graphics library (HUD[1])
+		lua_pushvalue(gL, -6); // stplayr
+		lua_pushvalue(gL, -6); // lt_ticker
+		lua_pushvalue(gL, -6); // lt_endtime
+		LUA_Call(gL, 4);
+	}
+
+	lua_pop(gL, -1);
+	hud_running = false;
+}
+
 #endif
diff --git a/src/p_setup.c b/src/p_setup.c
index f143193c975117aa26812f9d645dc472246b858f..fc88a5e5e4108355d7d79ff9a8f4369884258701 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -3234,9 +3234,6 @@ boolean P_SetupLevel(boolean skipprecip)
 		&& WipeStageTitle
 		&& ranspecialwipe != 2
 		&& *mapheaderinfo[gamemap-1]->lvlttl != '\0'
-#ifdef HAVE_BLUA
-		&& LUA_HudEnabled(hud_stagetitle)
-#endif
 	)
 		G_PreLevelTitleCard(lt_ticker, true);
 
diff --git a/src/st_stuff.c b/src/st_stuff.c
index c555c8b665f4285c3ee459d6e1a0b375f8154c5b..00371a6e839bc963e2eb6a97ad9c3fc02ba4a1f6 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -1210,7 +1210,7 @@ void ST_startTitleCard(void)
 }
 
 //
-// What happens before drawing the title card.
+// What happens before drawing the status bar.
 // Which is just setting the HUD translucency.
 //
 void ST_preDrawTitleCard(void)
@@ -1283,8 +1283,17 @@ void ST_drawTitleCard(void)
 	INT32 zzticker;
 	patch_t *actpat, *zigzag, *zztext;
 
+#ifdef HAVE_BLUA
+	if (!LUA_HudEnabled(hud_stagetitle))
+		goto luahook;
+#endif
+
 	if (lt_ticker >= (lt_endtime + TICRATE))
+#ifdef HAVE_BLUA
+		goto luahook;
+#else
 		return;
+#endif
 
 	if ((lt_ticker-lt_lasttic) > 1)
 		lt_ticker = lt_lasttic+1;
@@ -1324,6 +1333,11 @@ void ST_drawTitleCard(void)
 	V_DrawCenteredString(subttlxpos - ttlnumxpos, 128, V_PERPLAYER|V_ALLOWLOWERCASE, subttl);
 
 	lt_lasttic = lt_ticker;
+
+#ifdef HAVE_BLUA
+luahook:
+	LUAh_TitleCardHUD(stplyr);
+#endif
 }
 
 //
@@ -2525,11 +2539,7 @@ static void ST_overlayDrawer(void)
 	// Check for a valid level title
 	// If the HUD is enabled
 	// And, if Lua is running, if the HUD library has the stage title enabled
-	if (*mapheaderinfo[gamemap-1]->lvlttl != '\0' && !(hu_showscores && (netgame || multiplayer))
-#ifdef HAVE_BLUA
-	&& LUA_HudEnabled(hud_stagetitle)
-#endif
-	)
+	if (*mapheaderinfo[gamemap-1]->lvlttl != '\0' && !(hu_showscores && (netgame || multiplayer)))
 	{
 		stagetitle = true;
 		ST_preDrawTitleCard();
diff --git a/src/st_stuff.h b/src/st_stuff.h
index 7130855c61af42445c1843234fb79ebabc336e16..e6e8bec12f00f21c3cd298767810148cbbfbbccc 100644
--- a/src/st_stuff.h
+++ b/src/st_stuff.h
@@ -49,7 +49,6 @@ void ST_doPaletteStuff(void);
 
 // title card
 void ST_startTitleCard(void);
-void ST_preDrawTitleCard(void);
 void ST_runTitleCard(void);
 void ST_drawTitleCard(void);
 void ST_preLevelTitleCardDrawer(tic_t ticker, boolean update);