diff --git a/source/games/duke/src/actors.cpp b/source/games/duke/src/actors.cpp
index a16ffbf78..0a0eef257 100644
--- a/source/games/duke/src/actors.cpp
+++ b/source/games/duke/src/actors.cpp
@@ -303,7 +303,7 @@ void movesector(DDukeActor* const actor, int msindex, DAngle rotation)
 
 void movecyclers(void)
 {
-	for (int q = numcyclers - 1; q >= 0; q--)
+	for (int q = cyclers.Size() - 1; q >= 0; q--)
 	{
 		Cycler* c = &cyclers[q];
 		auto sect = c->sector;
@@ -336,15 +336,13 @@ void movecyclers(void)
 
 void addcycler(sectortype* sector, int lotag, int shade, int shade2, int hitag, int state)
 {
-	if (numcyclers >= MAXCYCLERS)
-		I_Error("Too many cycling sectors.");
-	cyclers[numcyclers].sector = sector;
-	cyclers[numcyclers].lotag = lotag;
-	cyclers[numcyclers].shade1 = shade;
-	cyclers[numcyclers].shade2 = shade2;
-	cyclers[numcyclers].hitag = hitag;
-	cyclers[numcyclers].state = state;
-	numcyclers++;
+	cyclers.Reserve(1);
+	cyclers.Last().sector = sector;
+	cyclers.Last().lotag = lotag;
+	cyclers.Last().shade1 = shade;
+	cyclers.Last().shade2 = shade2;
+	cyclers.Last().hitag = hitag;
+	cyclers.Last().state = state;
 }
 
 //---------------------------------------------------------------------------
diff --git a/source/games/duke/src/actors_lava.cpp b/source/games/duke/src/actors_lava.cpp
index 9b4332576..89b1b6df9 100644
--- a/source/games/duke/src/actors_lava.cpp
+++ b/source/games/duke/src/actors_lava.cpp
@@ -528,14 +528,11 @@ void thunder(void)
 
 int addambient(int hitag, int lotag)
 {
-	if (ambientfx >= 64)
-		I_Error("Too many ambient effects");
-	else
-	{
-		ambienthitag[ambientfx] = hitag;
-		ambientlotag[ambientfx] = lotag;
-		return ambientfx++;
-	}
+
+	ambienttags.Reserve(1);
+	ambienttags.Last().lo = lotag;
+	ambienttags.Last().hi = hitag;
+	return ambienttags.Size() - 1;
 }
 
 END_DUKE_NS
diff --git a/source/games/duke/src/constants.h b/source/games/duke/src/constants.h
index aeccb2a3e..c25baa9ab 100644
--- a/source/games/duke/src/constants.h
+++ b/source/games/duke/src/constants.h
@@ -281,7 +281,6 @@ enum
 
 enum
 {
-	MAXCYCLERS      = 1024,
 	MAXANIMATES     = 1024,
 	MAXANIMWALLS    = 512,
 	MAXANIMPOINTS   = 2048,
diff --git a/source/games/duke/src/gameexec.cpp b/source/games/duke/src/gameexec.cpp
index 957a55f89..559a53d8f 100644
--- a/source/games/duke/src/gameexec.cpp
+++ b/source/games/duke/src/gameexec.cpp
@@ -1760,23 +1760,23 @@ int ParseState::parse(void)
 		break;
 	case concmd_ifsoundid:
 		insptr++;
-		parseifelse((short)*insptr == ambientlotag[g_ac->spr.detail]);
+		parseifelse((short)*insptr == ambienttags[g_ac->spr.detail].lo);
 		break;
 	case concmd_ifsounddist:
 		insptr++;
 		if (*insptr == 0)
-			parseifelse(ambienthitag[g_ac->spr.detail] > g_x);
+			parseifelse(ambienttags[g_ac->spr.detail].hi > g_x);
 		else if (*insptr == 1)
-			parseifelse(ambienthitag[g_ac->spr.detail] < g_x);
+			parseifelse(ambienttags[g_ac->spr.detail].hi < g_x);
 		break;
 	case concmd_soundtag:
 		insptr++;
-		S_PlayActorSound(ambientlotag[g_ac->spr.detail], g_ac);
+		S_PlayActorSound(ambienttags[g_ac->spr.detail].lo, g_ac);
 		break;
 	case concmd_soundtagonce:
 		insptr++;
-		if (!S_CheckActorSoundPlaying(g_ac, ambientlotag[g_ac->spr.detail]))
-			S_PlayActorSound(ambientlotag[g_ac->spr.detail], g_ac);
+		if (!S_CheckActorSoundPlaying(g_ac, ambienttags[g_ac->spr.detail].lo))
+			S_PlayActorSound(ambienttags[g_ac->spr.detail].lo, g_ac);
 		break;
 	case concmd_soundonce:
 		insptr++;
diff --git a/source/games/duke/src/global.cpp b/source/games/duke/src/global.cpp
index 998f2e5eb..c445f2697 100644
--- a/source/games/duke/src/global.cpp
+++ b/source/games/duke/src/global.cpp
@@ -99,16 +99,13 @@ sectortype* clouds[256];
 float cloudx;
 float cloudy;
 int cloudclock;
-int numcyclers;								// sector lighting effects
-Cycler cyclers[MAXCYCLERS];
+TArray<Cycler> cyclers;
+TArray<AmbientTags> ambienttags;
 int mirrorcnt;
 sectortype* mirrorsector[64];					// mirrors
 walltype* mirrorwall[64];
 int numplayersprites;						// player management for some SEs.
 player_orig po[MAXPLAYERS];
-unsigned ambientfx;							// used by soundtag and soundtagonce script commands. If exported, export the commands, not the data!
-short ambientlotag[64];
-short ambienthitag[64];
 uint32_t everyothertime;					// Global animation ticker helper.
 
 // Redneck Rampage
diff --git a/source/games/duke/src/global.h b/source/games/duke/src/global.h
index 2790e03c8..79b57a3ec 100644
--- a/source/games/duke/src/global.h
+++ b/source/games/duke/src/global.h
@@ -71,7 +71,6 @@ extern int lastvisinc;
 extern animwalltype animwall[MAXANIMWALLS];
 extern int numanimwalls;
 extern int numclouds;
-extern int numcyclers;
 extern int earthquaketime;
 extern int global_random;
 extern int mirrorcnt;
@@ -105,7 +104,8 @@ extern float cloudx;
 extern float cloudy;
 extern int cloudclock;
 
-extern Cycler cyclers[MAXCYCLERS];
+extern TArray<Cycler> cyclers;
+extern TArray<AmbientTags> ambienttags;
 extern sectortype* mirrorsector[64];
 extern walltype* mirrorwall[64];
 
@@ -126,9 +126,6 @@ extern double geox2[MAXGEOSECTORS];
 extern double geoy2[MAXGEOSECTORS];
 extern int geocnt;
 
-extern short ambientlotag[64];
-extern short ambienthitag[64];
-extern unsigned ambientfx;
 extern TArray<DVector2> mspos;
 extern int WindTime;
 extern DAngle WindDir;
diff --git a/source/games/duke/src/premap.cpp b/source/games/duke/src/premap.cpp
index a0e0f5353..65e764f56 100644
--- a/source/games/duke/src/premap.cpp
+++ b/source/games/duke/src/premap.cpp
@@ -420,7 +420,7 @@ void resetprestat(int snum,int g)
 
 	screenpeek              = myconnectindex;
 	numanimwalls            = 0;
-	numcyclers              = 0;
+	cyclers.Clear();
 	animatecnt              = 0;
 	randomseed              = 17L;
 	paused             = 0;
@@ -657,7 +657,7 @@ void prelevel_common(int g)
 
 	lava_cleararrays();
 	geocnt = 0;
-	ambientfx = 0;
+	ambienttags.Clear();
 	thunderon = 0;
 	chickenplant = 0;
 	WindTime = 0;
@@ -676,8 +676,6 @@ void prelevel_common(int g)
 
 	memset(geosectorwarp, -1, sizeof(geosectorwarp));
 	memset(geosectorwarp2, -1, sizeof(geosectorwarp2));
-	memset(ambienthitag, -1, sizeof(ambienthitag));
-	memset(ambientlotag, -1, sizeof(ambientlotag));
 
 	for(auto&sec: sector)
 	{
diff --git a/source/games/duke/src/savegame.cpp b/source/games/duke/src/savegame.cpp
index 5e84d50b9..1b56ca12c 100644
--- a/source/games/duke/src/savegame.cpp
+++ b/source/games/duke/src/savegame.cpp
@@ -310,6 +310,19 @@ FSerializer& Serialize(FSerializer& arc, const char* keyname, Cycler& w, Cycler*
 	return arc;
 }
 
+FSerializer& Serialize(FSerializer& arc, const char* keyname, AmbientTags& w, AmbientTags* def)
+{
+	static AmbientTags nul;
+	if (!def) def = &nul;
+	if (arc.BeginObject(keyname))
+	{
+		arc("lotag", w.lo, def->lo)
+			("hitag", w.hi, def->hi)
+			.EndObject();
+	}
+	return arc;
+}
+
 FSerializer& Serialize(FSerializer& arc, const char* keyname, animate& w, animate* def)
 {
 	static animate nul;
@@ -332,8 +345,6 @@ void GameInterface::SerializeGameState(FSerializer& arc)
 	{
 		memset(geosectorwarp, -1, sizeof(geosectorwarp));
 		memset(geosectorwarp2, -1, sizeof(geosectorwarp2));
-		memset(ambienthitag, -1, sizeof(ambienthitag));
-		memset(ambientlotag, -1, sizeof(ambientlotag));
 	}
 	if (arc.BeginObject("duke.gamestate"))
 	{
@@ -380,8 +391,7 @@ void GameInterface::SerializeGameState(FSerializer& arc)
 			.Array("clouds", clouds, numclouds)
 
 			.Array("spriteq", spriteq, 1024)
-			("numcyclers", numcyclers)
-			.Array("cycler", cyclers, numcyclers)
+			("cycler", cyclers)
 			("mirrorcnt", mirrorcnt)
 			.Array("mirrorsector", mirrorsector, mirrorcnt)
 			.Array("mirrorwall", mirrorwall, mirrorcnt)
@@ -400,9 +410,7 @@ void GameInterface::SerializeGameState(FSerializer& arc)
 			.Array("geoy", geoy, geocnt)
 			.Array("geox2", geox2, geocnt)
 			.Array("geoy2", geoy2, geocnt)
-			("ambientfx", ambientfx)
-			.Array("ambientlotag", ambientlotag, ambientfx)
-			.Array("ambienthitag", ambienthitag, ambientfx)
+			("ambienttags", ambienttags)
 			("mspos", mspos)
 			("windtime", WindTime)
 			("winddir", WindDir)
diff --git a/source/games/duke/src/sectors.cpp b/source/games/duke/src/sectors.cpp
index 4ad06268a..abf1f2930 100644
--- a/source/games/duke/src/sectors.cpp
+++ b/source/games/duke/src/sectors.cpp
@@ -1105,7 +1105,7 @@ void operateactivators(int low, player_struct* plr)
 	Cycler * p;
 	walltype* wal;
 
-	for (i = numcyclers - 1; i >= 0; i--)
+	for (i = cyclers.Size() - 1; i >= 0; i--)
 	{
 		p = &cyclers[i];
 
diff --git a/source/games/duke/src/types.h b/source/games/duke/src/types.h
index 2c5e459ce..ae6e9815a 100644
--- a/source/games/duke/src/types.h
+++ b/source/games/duke/src/types.h
@@ -368,6 +368,10 @@ struct Cycler
 	bool state;
 };
 
+struct AmbientTags
+{
+	int lo, hi;
+};
 
 struct DukeLevel
 {