diff --git a/src/g_game.c b/src/g_game.c
index bfb58032b4ad63178cdf4069261a1467fcbffa9f..3dc64918320810fb901666768ece50ebc858ed53 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2037,7 +2037,7 @@ void G_Ticker(boolean run)
 
 		if (Playing() == true)
 		{
-			P_InvincGrowMusic();
+			P_InvincGrowMusic_Control();
 
 			K_TickMidVote();
 		}
diff --git a/src/music.cpp b/src/music.cpp
index 44f1edb3cf971386f33994f83bc5fc3d2f1efa01..ed9b2e2b0fc6310d566819986f4c8ef2eacda79a 100644
--- a/src/music.cpp
+++ b/src/music.cpp
@@ -13,6 +13,7 @@
 
 #include "doomtype.h"
 #include "music.h"
+#include "z_zone.h"
 
 using namespace srb2::music;
 
@@ -23,8 +24,31 @@ TuneManager g_tunes;
 
 }; // namespace
 
+skin_t* music_observableSkin = nullptr;
+
+static Tune* Music_Find(const char *id, SINT8 *skintuneIndexOut)
+{
+	if(skintuneIndexOut)
+		*skintuneIndexOut = -1;
+
+	SINT8 skintuneIndex = R_FindSkintuneIndex(music_observableSkin, id);
+
+	if(skintuneIndex < 0)
+		return g_tunes.find(id);
+
+	if(skintuneIndexOut)
+		*skintuneIndexOut = skintuneIndex;
+
+	char* swaptunename = R_TryAllocSkintuneTuneName(skintuneIndex);
+	Tune* ret = g_tunes.find(swaptunename);
+	Z_Free(swaptunename);
+	return ret;
+}
+
 void Music_Init(void)
 {
+	music_observableSkin = nullptr;
+
 	{
 		Tune& tune = g_tunes.insert("level");
 
@@ -217,6 +241,26 @@ void Music_Init(void)
 		tune.priority = 35;
 		tune.loop = false;
 	}
+
+	UINT8 i;
+
+	for(i = 0; i < NUMSKINTUNES; i++)
+	{
+		Tune* src = g_tunes.find(skins_skintunenames[i]);
+
+		if(!src)
+			continue;
+
+		char newtunename[5 + MAXSKINTUNENAMELEN + 1];
+		memset(newtunename, 0, (5 + MAXSKINTUNENAMELEN + 1) * sizeof(char));
+		sprintf(newtunename, "skin_%s", skins_skintunenames[i]);
+
+		Tune& dest = g_tunes.insert(newtunename);
+		dest.priority = src->priority;
+		dest.resume_fade_in = src->resume_fade_in;
+		dest.use_level_volume = src->use_level_volume;
+		// fill out more deep-copy assignments as needed here
+	}
 }
 
 
@@ -249,9 +293,13 @@ void Music_AddTune(const char* id, int priority, int tuneflags)
 
 void Music_Play(const char* id)
 {
-	Tune* tune = g_tunes.find(id);
+	SINT8 skintuneIndex;
+	Tune* tune = Music_Find(id, &skintuneIndex);
 
-	if (tune)
+	if(skintuneIndex >= 0)
+		tune->song = music_observableSkin->skintunes[skintuneIndex];
+
+	if(tune)
 	{
 		tune->play();
 		g_tunes.tick(); // play this immediately
@@ -260,7 +308,7 @@ void Music_Play(const char* id)
 
 void Music_SetFadeOut(const char* id, int fade_out)
 {
-	Tune* tune = g_tunes.find(id);
+	Tune* tune = Music_Find(id, nullptr);
 
 	if (tune)
 	{
@@ -277,7 +325,7 @@ void Music_SetFadeOut(const char* id, int fade_out)
 
 void Music_SetFadeIn(const char* id, int fade_in, boolean resume)
 {
-	Tune* tune = g_tunes.find(id);
+	Tune* tune = Music_Find(id, nullptr);
 
 	if (tune)
 	{
@@ -301,7 +349,7 @@ void Music_SetFadeIn(const char* id, int fade_in, boolean resume)
 
 void Music_DelayEnd(const char* id, tic_t duration)
 {
-	Tune* tune = g_tunes.find(id);
+	Tune* tune = Music_Find(id, nullptr);
 
 	if (tune)
 	{
@@ -318,7 +366,7 @@ void Music_DelayEnd(const char* id, tic_t duration)
 
 void Music_Seek(const char* id, UINT32 set)
 {
-	Tune* tune = g_tunes.find(id);
+	Tune* tune = Music_Find(id, nullptr);
 
 	if (tune)
 	{
@@ -329,7 +377,7 @@ void Music_Seek(const char* id, UINT32 set)
 
 void Music_Stop(const char* id)
 {
-	Tune* tune = g_tunes.find(id);
+	Tune* tune = Music_Find(id, nullptr);
 
 	if (tune)
 	{
@@ -340,7 +388,7 @@ void Music_Stop(const char* id)
 
 void Music_Pause(const char* id)
 {
-	Tune* tune = g_tunes.find(id);
+	Tune* tune = Music_Find(id, nullptr);
 
 	if (tune)
 	{
@@ -351,7 +399,7 @@ void Music_Pause(const char* id)
 
 void Music_UnPause(const char* id)
 {
-	Tune* tune = g_tunes.find(id);
+	Tune* tune = Music_Find(id, nullptr);
 
 	if (tune)
 	{
@@ -362,7 +410,7 @@ void Music_UnPause(const char* id)
 
 void Music_Suspend(const char* id)
 {
-	Tune* tune = g_tunes.find(id);
+	Tune* tune = Music_Find(id, nullptr);
 
 	if (tune)
 	{
@@ -373,7 +421,7 @@ void Music_Suspend(const char* id)
 
 void Music_UnSuspend(const char* id)
 {
-	Tune* tune = g_tunes.find(id);
+	Tune* tune = Music_Find(id, nullptr);
 
 	if (tune)
 	{
@@ -402,7 +450,7 @@ void Music_StopAll(void)
 
 void Music_Remap(const char* id, const char* song)
 {
-	Tune* tune = g_tunes.find(id);
+	Tune* tune = Music_Find(id, nullptr);
 
 	if (tune)
 	{
@@ -412,7 +460,7 @@ void Music_Remap(const char* id, const char* song)
 
 boolean Music_TuneExists(const char* id)
 {
-	const Tune* tune = g_tunes.find(id);
+	const Tune* tune = Music_Find(id, nullptr);
 
 	if (tune)
 	{
@@ -423,49 +471,49 @@ boolean Music_TuneExists(const char* id)
 
 boolean Music_Playing(const char* id)
 {
-	const Tune* tune = g_tunes.find(id);
+	const Tune* tune = Music_Find(id, nullptr);
 	return tune && tune->playing();
 }
 
 boolean Music_Paused(const char* id)
 {
-	const Tune* tune = g_tunes.find(id);
+	const Tune* tune = Music_Find(id, nullptr);
 	return tune && tune->paused();
 }
 
 boolean Music_Suspended(const char* id)
 {
-	const Tune* tune = g_tunes.find(id);
+	const Tune* tune = Music_Find(id, nullptr);
 	return tune && tune->suspend;
 }
 
 tic_t Music_Elapsed(const char* id)
 {
-	const Tune* tune = g_tunes.find(id);
+	const Tune* tune = Music_Find(id, nullptr);
 	return tune ? tune->elapsed() : 0u;
 }
 
 tic_t Music_DurationLeft(const char* id)
 {
-	const Tune* tune = g_tunes.find(id);
+	const Tune* tune = Music_Find(id, nullptr);
 	return tune ? tune->time_remaining() : 0u;
 }
 
 tic_t Music_TotalDuration(const char* id)
 {
-	const Tune* tune = g_tunes.find(id);
+	const Tune* tune = Music_Find(id, nullptr);
 	return tune ? tune->duration() : 0u;
 }
 
 unsigned int Music_FadeOutDuration(const char* id)
 {
-	const Tune* tune = g_tunes.find(id);
+	const Tune* tune = Music_Find(id, nullptr);
 	return tune ? tune->fade_out : 0;
 }
 
 void Music_Loop(const char* id, boolean loop)
 {
-	Tune* tune = g_tunes.find(id);
+	Tune* tune = Music_Find(id, nullptr);
 
 	if (tune)
 	{
@@ -477,19 +525,19 @@ void Music_Loop(const char* id, boolean loop)
 
 boolean Music_CanLoop(const char* id)
 {
-	const Tune* tune = g_tunes.find(id);
+	const Tune* tune = Music_Find(id, nullptr);
 	return tune && tune->loop;
 }
 
 boolean Music_CanEnd(const char* id)
 {
-	const Tune* tune = g_tunes.find(id);
+	const Tune* tune = Music_Find(id, nullptr);
 	return tune && tune->can_end();
 }
 
 const char* Music_Song(const char* id)
 {
-	const Tune* tune = g_tunes.find(id);
+	const Tune* tune = Music_Find(id, nullptr);
 	return tune ? tune->song.c_str() : "";
 }
 
@@ -505,7 +553,7 @@ const char* Music_CurrentId(void)
 
 void Music_BatchExempt(const char* id)
 {
-	Tune* tune = g_tunes.find(id);
+	Tune* tune = Music_Find(id, nullptr);
 
 	if (tune)
 	{
diff --git a/src/music.h b/src/music.h
index 630c7a98ce72698d1426ad2ed8ef2d1ef740e2bf..3b64f17a09be44a241fb02933cfb1f7d5ba23aec 100644
--- a/src/music.h
+++ b/src/music.h
@@ -30,6 +30,7 @@
 #define MUSIC_H
 
 #include "doomtype.h"
+#include "r_skins.h" // skin_t, skins_skintunenames, etc.
 
 #ifdef __cplusplus
 extern "C" {
@@ -63,6 +64,7 @@ extern "C" {
 // Get the currently playing tune.
 //
 
+extern skin_t* music_observableSkin;
 
 // Returns the song name for the currently playing tune.
 // Returns empty string if no tune is playing.
diff --git a/src/p_local.h b/src/p_local.h
index 19e311911487982182307ba9bd8f0d84379a1160..7b33e359859cd84d3d95595ee42c6beca9505944 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -185,7 +185,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean fromAir, angle_t oldPitch, an
 void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative);
 void P_StartPositionMusic(boolean exact);
 void P_EndingMusic(void);
-void P_InvincGrowMusic(void);
+void P_InvincGrowMusic_Control(void);
 mobj_t *P_SpawnGhostMobj(mobj_t *mobj);
 mobj_t *P_SpawnFakeShadow(mobj_t *mobj, UINT8 offset);
 INT32 P_GivePlayerRings(player_t *player, INT32 num_rings);
diff --git a/src/p_user.c b/src/p_user.c
index ecff343eeec5469e345b20e4a36c06b086ed2709..64e195a905c00f520e841f28397f980546f71ad5 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -738,10 +738,22 @@ skippingposition:
 	Music_Play("finish");
 }
 
-void P_InvincGrowMusic(void)
+static void P_InvincGrowMusic_Play(UINT8 skintuneIndex, INT32 timer)
 {
-	INT32 invinc = 0;
-	INT32 grow = 0;
+	char* tunename = skins_skintunenames[skintuneIndex];
+
+	if (timer && !Music_Playing(tunename))
+		Music_Play(tunename);
+
+	Music_DelayEnd(tunename, timer);
+}
+
+void P_InvincGrowMusic_Control(void)
+{
+	INT32 timerSlots[NUMSKINTUNES];
+	memset(timerSlots, 0, NUMSKINTUNES * sizeof(INT32));
+
+	skin_t *prevObservable = music_observableSkin;
 
 	UINT8 i;
 
@@ -759,29 +771,23 @@ void P_InvincGrowMusic(void)
 		// Find the longest running timer among splitscreen
 		// players and use that.
 
-		if (player->invincibilitytimer > invinc)
+		if (player->invincibilitytimer > timerSlots[0])
 		{
-			invinc = player->invincibilitytimer;
+			timerSlots[0] = player->invincibilitytimer;
 		}
 
-		if (player->growshrinktimer > grow)
+		if (player->growshrinktimer > timerSlots[1])
 		{
-			grow = player->growshrinktimer;
+			timerSlots[1] = player->growshrinktimer;
 		}
-	}
 
-	if (invinc && !Music_Playing("invinc"))
-	{
-		Music_Play("invinc");
+		music_observableSkin = (timerSlots[0] || timerSlots[1]) ? &skins[player->skin] : NULL;
 	}
 
-	if (grow && !Music_Playing("grow"))
-	{
-		Music_Play("grow");
-	}
+	for(i = 0; i < NUMSKINTUNES; i++)
+		P_InvincGrowMusic_Play(i, timerSlots[i]);
 
-	Music_DelayEnd("invinc", invinc);
-	Music_DelayEnd("grow", grow);
+	music_observableSkin = prevObservable;
 }
 
 //
diff --git a/src/r_skins.c b/src/r_skins.c
index 994ce64fbbd372b01b49846a8ffb0f1993d5be9c..9811c65a6e3869bad92c49e0e98487ee18e69888 100644
--- a/src/r_skins.c
+++ b/src/r_skins.c
@@ -54,6 +54,11 @@ CV_PossibleValue_t skin_cons_t[MAXSKINS+1];
 
 CV_PossibleValue_t Forceskin_cons_t[MAXSKINS+2];
 
+char skins_skintunenames[NUMSKINTUNES][MAXSKINTUNENAMELEN + 1] = {
+	"invinc",
+	"grow"
+};
+
 //
 // P_GetSkinSprite2
 // For non-super players, tries each sprite2's immediate predecessor until it finds one with a number of frames or ends up at standing.
@@ -103,6 +108,39 @@ UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player)
 	return spr2;
 }
 
+SINT8 R_FindSkintuneIndex(skin_t *skin, const char *tunename)
+{
+	if(!skin || !tunename)
+		return -1;
+
+	UINT8 i;
+
+	for(i = 0; i < NUMSKINTUNES; i++)
+	{
+		if(stricmp(skins_skintunenames[i], tunename))
+			continue;
+
+		if(!skin->definedSkintunes[i])
+			continue;
+
+		return i;
+	}
+	
+	return -1;
+}
+
+char* R_TryAllocSkintuneTuneName(UINT8 skintuneIndex)
+{
+	if(skintuneIndex >= NUMSKINTUNES)
+		return NULL;
+
+	int allocAmt = (MAXSKINTUNENAMELEN + 5 + 1) * sizeof(char); // i'm aware that a char is 1 byte big, doing the mult for posterity
+	char *ret = (char*)Z_Malloc(allocAmt, PU_STATIC, NULL);
+	memset(ret, 0, allocAmt);
+	sprintf(ret, "skin_%s", skins_skintunenames[skintuneIndex]);
+	return ret;
+}
+
 static void Sk_SetDefaultValue(skin_t *skin)
 {
 	INT32 i;
@@ -892,40 +930,66 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value)
 	GETFLAG(BADNIK)
 #undef GETFLAG
 
-	else // let's check if it's a sound, otherwise error out
+	else // let's check if it's a sound or skintune, otherwise error out
 	{
-		boolean found = false;
-		sfxenum_t i;
-		size_t stokenadjust;
-
-		// Remove the prefix. (We need to affect an adjusting variable so that we can print error messages if it's not actually a sound.)
-		if ((stoken[0] == 'D' || stoken[0] == 'd') && (stoken[1] == 'S' || stoken[1] == 's')) // DS*
-			stokenadjust = 2;
-		else // sfx_*
-			stokenadjust = 4;
-
-		// Remove the prefix. (We can affect this directly since we're not going to use it again.)
-		if ((value[0] == 'D' || value[0] == 'd') && (value[1] == 'S' || value[1] == 's')) // DS*
-			value += 2;
-		else // sfx_*
-			value += 4;
-
-		// copy name of sounds that are remapped
-		// for this skin
-		for (i = 0; i < sfx_skinsoundslot0; i++)
+		SINT8 skindex = -1;
+		UINT8 skini;
+
+		for(skini = 0; skini < NUMSKINTUNES; skini++)
 		{
-			if (!S_sfx[i].name)
-				continue;
-			if (S_sfx[i].skinsound != -1
-				&& !stricmp(S_sfx[i].name,
-					stoken + stokenadjust))
+			char tokenConditionBuffer[3 + MAXSKINTUNENAMELEN + 1]; // +3 for 'MUS', +1 for null terminator
+			memset(tokenConditionBuffer, 0, (3 + MAXSKINTUNENAMELEN + 1) * sizeof(char));
+			sprintf(tokenConditionBuffer, "MUS%s", skins_skintunenames[skini]);
+
+			if(!stricmp(tokenConditionBuffer, stoken))
+			{
+				skindex=skini;
+				break;
+			}
+		}
+
+		if(skindex >= 0)
+			skin->definedSkintunes[skindex] = true;
+
+		if(skin->definedSkintunes[skindex]) // IS skintune...
+		{
+			strncpy(skin->skintunes[skindex], value, min(8, strlen(value))); // assumes before now that all skintunes[] buffers are full of null terminators from the skin init process
+		}
+		else
+		{
+			boolean found = false;
+			sfxenum_t i;
+			size_t stokenadjust;
+	
+			// Remove the prefix. (We need to affect an adjusting variable so that we can print error messages if it's not actually a sound.)
+			if ((stoken[0] == 'D' || stoken[0] == 'd') && (stoken[1] == 'S' || stoken[1] == 's')) // DS*
+				stokenadjust = 2;
+			else // sfx_*
+				stokenadjust = 4;
+	
+			// Remove the prefix. (We can affect this directly since we're not going to use it again.)
+			if ((value[0] == 'D' || value[0] == 'd') && (value[1] == 'S' || value[1] == 's')) // DS*
+				value += 2;
+			else // sfx_*
+				value += 4;
+	
+			// copy name of sounds that are remapped
+			// for this skin
+			for (i = 0; i < sfx_skinsoundslot0; i++)
 			{
-				skin->soundsid[S_sfx[i].skinsound] =
-					S_AddSoundFx(value, S_sfx[i].singularity, S_sfx[i].pitch, true);
-				found = true;
+				if (!S_sfx[i].name)
+					continue;
+				if (S_sfx[i].skinsound != -1
+					&& !stricmp(S_sfx[i].name,
+						stoken + stokenadjust))
+				{
+					skin->soundsid[S_sfx[i].skinsound] =
+						S_AddSoundFx(value, S_sfx[i].singularity, S_sfx[i].pitch, true);
+					found = true;
+				}
 			}
+			return found;
 		}
-		return found;
 	}
 	return true;
 }
diff --git a/src/r_skins.h b/src/r_skins.h
index 9a05129f998e509c61f85a6fc5138b60b08acf96..56d74d1965c97195548a634c29fa7bc093ebc827 100644
--- a/src/r_skins.h
+++ b/src/r_skins.h
@@ -35,6 +35,11 @@ extern "C" {
 #define DEFAULTSKIN3 "sonic" // third player
 #define DEFAULTSKIN4 "knuckles" // fourth player
 
+#define NUMSKINTUNES 2
+#define MAXSKINTUNENAMELEN 6
+
+extern char skins_skintunenames[NUMSKINTUNES][MAXSKINTUNENAMELEN + 1];
+
 /// The skin_t struct
 struct skin_t
 {
@@ -70,6 +75,9 @@ struct skin_t
 	// contains super versions too
 	spritedef_t sprites[NUMPLAYERSPRITES*2];
 	spriteinfo_t sprinfo[NUMPLAYERSPRITES*2];
+
+	char skintunes[NUMSKINTUNES][9]; // sound lump names that can be remapped when one of the tunes from skins_skintunenames plays
+	boolean definedSkintunes[NUMSKINTUNES]; // whether or not this skin had definitions for each skintune during R_ProcessPatchableFields(), not to be confused on whether or not this skin had lumps present for those definitions which is what would be better to check for instead but I don't know how :S.
 };
 
 enum facepatches {
@@ -137,6 +145,10 @@ UINT32 R_GetLocalRandomSkin(void);
 // Sprite2
 UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player);
 
+SINT8 R_FindSkintuneIndex(skin_t *skin, const char *tunename);
+
+char* R_TryAllocSkintuneTuneName(UINT8 skintuneIndex);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif