diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c
index f282ca89184b9d47fe000f47e126880c0467b602..b71bf7007e03ab2cdb7d229d84aa5a57deb8cc8c 100644
--- a/src/hardware/hw_cache.c
+++ b/src/hardware/hw_cache.c
@@ -1258,20 +1258,29 @@ void HWR_SetMapPalette(void)
 
 // Creates a hardware lighttable from the supplied lighttable.
 // Returns the id of the hw lighttable, usable in FSurfaceInfo.
-UINT32 HWR_CreateLightTable(UINT8 *lighttable)
+UINT32 HWR_CreateLightTable(UINT8 *lighttable, RGBA_t *hw_lighttable)
 {
-	UINT32 i, id;
+	UINT32 i;
 	RGBA_t *palette = HWR_GetTexturePalette();
-	RGBA_t *hw_lighttable = Z_Malloc(256 * 32 * sizeof(RGBA_t), PU_STATIC, NULL);
 
 	// To make the palette index -> RGBA mapping easier for the shader,
 	// the hardware lighttable is composed of RGBA colors instead of palette indices.
 	for (i = 0; i < 256 * 32; i++)
 		hw_lighttable[i] = palette[lighttable[i]];
 
-	id = HWD.pfnCreateLightTable(hw_lighttable);
-	Z_Free(hw_lighttable);
-	return id;
+	return HWD.pfnCreateLightTable(hw_lighttable);
+}
+
+// Updates a hardware lighttable of a given id from the supplied lighttable.
+void HWR_UpdateLightTable(UINT32 id, UINT8 *lighttable, RGBA_t *hw_lighttable)
+{
+	UINT32 i;
+	RGBA_t *palette = HWR_GetTexturePalette();
+
+	for (i = 0; i < 256 * 32; i++)
+		hw_lighttable[i] = palette[lighttable[i]];
+
+	HWD.pfnUpdateLightTable(id, hw_lighttable);
 }
 
 // get hwr lighttable id for colormap, create it if it doesn't already exist
@@ -1285,25 +1294,41 @@ UINT32 HWR_GetLightTableID(extracolormap_t *colormap)
 		default_colormap = true;
 	}
 
+	UINT8 *colormap_pointer;
+
+	if (default_colormap)
+		colormap_pointer = colormaps; // don't actually use the data from the "default colormap"
+	else
+		colormap_pointer = colormap->colormap;
+
 	// create hw lighttable if there isn't one
-	if (!colormap->gl_lighttable_id)
+	if (colormap->gl_lighttable.data == NULL)
 	{
-		UINT8 *colormap_pointer;
+		Z_Malloc(256 * 32 * sizeof(RGBA_t), PU_HWRLIGHTTABLEDATA, &colormap->gl_lighttable.data);
+	}
 
-		if (default_colormap)
-			colormap_pointer = colormaps; // don't actually use the data from the "default colormap"
-		else
-			colormap_pointer = colormap->colormap;
-		colormap->gl_lighttable_id = HWR_CreateLightTable(colormap_pointer);
+	// Generate the texture for this light table
+	if (!colormap->gl_lighttable.id)
+	{
+		colormap->gl_lighttable.id = HWR_CreateLightTable(colormap_pointer, colormap->gl_lighttable.data);
+	}
+	// Update the texture if it was directly changed by a script
+	else if (colormap->gl_lighttable.needs_update)
+	{
+		HWR_UpdateLightTable(colormap->gl_lighttable.id, colormap_pointer, colormap->gl_lighttable.data);
 	}
 
-	return colormap->gl_lighttable_id;
+	colormap->gl_lighttable.needs_update = false;
+
+	return colormap->gl_lighttable.id;
 }
 
 // Note: all hardware lighttable ids assigned before this
 // call become invalid and must not be used.
 void HWR_ClearLightTables(void)
 {
+	Z_FreeTag(PU_HWRLIGHTTABLEDATA);
+
 	if (vid.glstate == VID_GL_LIBRARY_LOADED)
 		HWD.pfnClearLightTables();
 }
diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h
index 694cc1b8c0d5a16e1d40ae9d14575c562d868058..a5fdc00a47c17e6b5cbeba5d074554a66672cde7 100644
--- a/src/hardware/hw_drv.h
+++ b/src/hardware/hw_drv.h
@@ -71,6 +71,7 @@ EXPORT void HWRAPI(SetShaderInfo) (hwdshaderinfo_t info, INT32 value);
 
 EXPORT void HWRAPI(SetPaletteLookup)(UINT8 *lut);
 EXPORT UINT32 HWRAPI(CreateLightTable)(RGBA_t *hw_lighttable);
+EXPORT void HWRAPI(UpdateLightTable)(UINT32 id, RGBA_t *hw_lighttable);
 EXPORT void HWRAPI(ClearLightTables)(void);
 EXPORT void HWRAPI(SetScreenPalette)(RGBA_t *palette);
 
@@ -125,6 +126,7 @@ struct hwdriver_s
 
 	SetPaletteLookup    pfnSetPaletteLookup;
 	CreateLightTable    pfnCreateLightTable;
+	UpdateLightTable    pfnUpdateLightTable;
 	ClearLightTables    pfnClearLightTables;
 	SetScreenPalette    pfnSetScreenPalette;
 };
diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h
index 62fe8d0bda5b521f650a91f00f97e111d99e6eed..bf9f7da3b4c126154fed2faf81761f348fbeed63 100644
--- a/src/hardware/hw_glob.h
+++ b/src/hardware/hw_glob.h
@@ -133,7 +133,8 @@ void HWR_UnlockCachedPatch(GLPatch_t *gpatch);
 
 void HWR_SetPalette(RGBA_t *palette);
 void HWR_SetMapPalette(void);
-UINT32 HWR_CreateLightTable(UINT8 *lighttable);
+UINT32 HWR_CreateLightTable(UINT8 *lighttable, RGBA_t *hw_lighttable);
+void HWR_UpdateLightTable(UINT32 id, UINT8 *lighttable, RGBA_t *hw_lighttable);
 UINT32 HWR_GetLightTableID(extracolormap_t *colormap);
 void HWR_ClearLightTables(void);
 
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index 75a92c2fb33ad542b0931786a9b4688a9f9dccbc..85a50edb0691bb11bd141e1a9c7262ef6233e564 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -3257,6 +3257,24 @@ EXPORT UINT32 HWRAPI(CreateLightTable)(RGBA_t *hw_lighttable)
 	return item->id;
 }
 
+EXPORT void HWRAPI(UpdateLightTable)(UINT32 id, RGBA_t *hw_lighttable)
+{
+	LTListItem *item = LightTablesHead;
+	while (item && item->id != id)
+		item = item->next;
+
+	if (item)
+	{
+		pglBindTexture(GL_TEXTURE_2D, item->id);
+
+		// Just update it
+		pglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 32, GL_RGBA, GL_UNSIGNED_BYTE, hw_lighttable);
+
+		// restore previously bound texture
+		pglBindTexture(GL_TEXTURE_2D, tex_downloaded);
+	}
+}
+
 // Delete light table textures, ids given before become invalid and must not be used.
 EXPORT void HWRAPI(ClearLightTables)(void)
 {
diff --git a/src/lua_colorlib.c b/src/lua_colorlib.c
index 9e679eb402f8e6e99eb4957c93aee8dbe9915bdb..2743635edfefe88348a93dc45aa26f77d4702067 100644
--- a/src/lua_colorlib.c
+++ b/src/lua_colorlib.c
@@ -593,7 +593,7 @@ static int extracolormap_set(lua_State *L)
 		|| exc->fadergba != old_fade_rgba
 		|| exc->fadestart != old_fade_start
 		|| exc->fadeend != old_fade_end)
-	R_GenerateLightTable(exc, true);
+	R_UpdateLightTable(exc, true);
 
 	return 0;
 }
diff --git a/src/r_data.c b/src/r_data.c
index e7200ecbd600c85cc6b3ca89c5c84d511b8aface..244690ebe5e13626a856ff976e3db480cfc7254a 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -849,6 +849,15 @@ void R_GenerateLightTable(extracolormap_t *extra_colormap, boolean uselookup)
 	}
 }
 
+void R_UpdateLightTable(extracolormap_t *extra_colormap, boolean uselookup)
+{
+	R_GenerateLightTable(extra_colormap, uselookup);
+
+#ifdef HWRENDER
+	extra_colormap->gl_lighttable.needs_update = true;
+#endif
+}
+
 extracolormap_t *R_CreateColormapFromLinedef(char *p1, char *p2, char *p3)
 {
 	// default values
diff --git a/src/r_data.h b/src/r_data.h
index 7c6ee19d50c4b8b9b081691541b4d35fff5388c4..64abf6d83637ed8f54c7449f621ac52eb6aa9113 100644
--- a/src/r_data.h
+++ b/src/r_data.h
@@ -90,6 +90,7 @@ typedef enum
 } textmapcolormapflags_t;
 
 void R_GenerateLightTable(extracolormap_t *extra_colormap, boolean uselookup);
+void R_UpdateLightTable(extracolormap_t *extra_colormap, boolean uselookup);
 lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap);
 extracolormap_t * R_CreateColormapFromLinedef(char *p1, char *p2, char *p3);
 extracolormap_t* R_CreateColormap(INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 flags);
diff --git a/src/r_defs.h b/src/r_defs.h
index 51fac21fae4ed2e1c83929d6a121d09286e2a271..7269aa9d5b10526435ede8909796af25579cf3e1 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -76,8 +76,11 @@ typedef struct extracolormap_s
 	lighttable_t *colormap;
 
 #ifdef HWRENDER
-	// The id of the hardware lighttable. Zero means it does not exist yet.
-	UINT32 gl_lighttable_id;
+	struct {
+		UINT32 id; // The id of the hardware lighttable. Zero means it does not exist yet.
+		RGBA_t *data; // The texture data of the hardware lighttable.
+		boolean needs_update; // If the colormap changed recently or not.
+	} gl_lighttable;
 #endif
 
 #ifdef EXTRACOLORMAPLUMPS
diff --git a/src/sdl/hwsym_sdl.c b/src/sdl/hwsym_sdl.c
index ca87fcc7951758e9a5de7ca1a405f88b1f202204..f561266017dd9fcf20b467b3c5e9323be7f5dbe2 100644
--- a/src/sdl/hwsym_sdl.c
+++ b/src/sdl/hwsym_sdl.c
@@ -112,6 +112,7 @@ void *hwSym(const char *funcName,void *handle)
 
 	GETFUNC(SetPaletteLookup);
 	GETFUNC(CreateLightTable);
+	GETFUNC(UpdateLightTable);
 	GETFUNC(ClearLightTables);
 	GETFUNC(SetScreenPalette);
 
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index 10c866a1e32ff6dd3c8ac248fb50df2a4e3a30f6..36bfd380f8164194dbc2715cf9d7452fe476475c 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -1901,6 +1901,7 @@ void VID_StartupOpenGL(void)
 
 		HWD.pfnSetPaletteLookup = hwSym("SetPaletteLookup",NULL);
 		HWD.pfnCreateLightTable = hwSym("CreateLightTable",NULL);
+		HWD.pfnUpdateLightTable = hwSym("UpdateLightTable",NULL);
 		HWD.pfnClearLightTables = hwSym("ClearLightTables",NULL);
 		HWD.pfnSetScreenPalette = hwSym("SetScreenPalette",NULL);
 
diff --git a/src/z_zone.c b/src/z_zone.c
index 5750f8ae016becd9c9548b690b0e7fd5b32ec7cb..0852fabaeac03f3751d33063cb14f63105678537 100644
--- a/src/z_zone.c
+++ b/src/z_zone.c
@@ -671,6 +671,7 @@ static void Command_Memfree_f(void)
 		CONS_Printf(M_GetText("Cached textures        : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRCACHE)>>10));
 		CONS_Printf(M_GetText("Texture colormaps      : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRPATCHCOLMIPMAP)>>10));
 		CONS_Printf(M_GetText("Model textures         : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRMODELTEXTURE)>>10));
+		CONS_Printf(M_GetText("Light table textures   : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRLIGHTTABLEDATA)>>10));
 		CONS_Printf(M_GetText("Plane polygons         : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRPLANE)>>10));
 		CONS_Printf(M_GetText("All GPU textures       : %7d KB\n"), HWR_GetTextureUsed()>>10);
 	}
diff --git a/src/z_zone.h b/src/z_zone.h
index ce7af4a159555e3a6c2be83e8d0eadcf8a7a69eb..56c540c3bb31a4a84e5f2403519455f3830583ee 100644
--- a/src/z_zone.h
+++ b/src/z_zone.h
@@ -55,6 +55,7 @@ enum
 	PU_HWRPATCHINFO          = 21, // Hardware GLPatch_t struct for OpenGL texture cache
 	PU_HWRPATCHCOLMIPMAP     = 22, // Hardware GLMipmap_t struct colormap variation of patch
 	PU_HWRMODELTEXTURE       = 23, // Hardware model texture
+	PU_HWRLIGHTTABLEDATA     = 24, // Hardware light table data
 
 	PU_HWRCACHE              = 48, // static until unlocked
 	PU_CACHE                 = 49, // static until unlocked