diff --git a/devTools/types/FC/util.d.ts b/devTools/types/FC/util.d.ts
index 3ed3029b8ef312d7d6465dbab41f8009f3ada656..8123985964b9bac8c4856555f616c230430f0e20 100644
--- a/devTools/types/FC/util.d.ts
+++ b/devTools/types/FC/util.d.ts
@@ -18,4 +18,8 @@ declare namespace FC {
 		min: number;
 		max: number;
 	}
+
+	type PromiseWithProgress<T> = Promise<T> & {
+		onProgress: (fn: (progress: number) => void) => PromiseWithProgress<T>
+	}
 }
diff --git a/src/art/artJS.js b/src/art/artJS.js
index c633ffb87a2c0c3157ff2320b543e055f9f7fbb6..b792dc42340a49fda3a8e20f77eb817f27fae0d0 100644
--- a/src/art/artJS.js
+++ b/src/art/artJS.js
@@ -621,13 +621,15 @@ App.Art.aiArtElement = function(slave, imageSize, isEventImage = null) {
 		updateAndRefresh: (index = null) => {
 			container.classList.add("refreshing");
 
-			App.Art.GenAI.staticCache.updateSlave(slave, index, isEventImage).then(() => {
-				staticSpecific.refresh();
-			}).catch(error => {
-				console.log(error.message || error);
-			}).finally(() => {
-				container.classList.remove("refreshing");
-			});
+			App.Art.GenAI.staticCache.updateSlave(slave, index, isEventImage)
+				.onProgress((progressNum) => progress.value = progressNum)
+				.then(() => {
+					staticSpecific.refresh();
+				}).catch(error => {
+					console.log(error.message || error);
+				}).finally(() => {
+					container.classList.remove("refreshing");
+				});
 		},
 		refresh: () => {
 			renderAIArt(slave, imageSize, slave.custom.aiDisplayImageIdx)
diff --git a/src/art/genAI/reactiveImageDB.js b/src/art/genAI/reactiveImageDB.js
index a5127ab07319e32084eafd163e98e6ec0cf515ac..cccd4a894d290268318d71795747399a3ca5e82a 100644
--- a/src/art/genAI/reactiveImageDB.js
+++ b/src/art/genAI/reactiveImageDB.js
@@ -320,18 +320,13 @@ App.Art.GenAI.reactiveImageDB = (function() {
 		return fuzzyResults;
 	}
 
-	/**
-	 * @typedef {Promise<T> & { onProgress: (fn: (progress: number) => void) => PromiseWithProgress<T> }} PromiseWithProgress<T>
-	 * @template {any} T
-	 */
-
 	/**
 	 * Get an image from the IndexedDB
 	 * @param {App.Entity.SlaveState[]} slaves The ID of the image to retrieve
 	 * @param {Partial<App.Art.GenAI.GetImageOptions>} [options] Misc options.
 	 * Defaults: action='overview', size=App.Art.ArtSizes.SMALL, forceRegenerate: false, isEventImage: false
 	 *
-	 * @returns {PromiseWithProgress<App.Art.GenAI.EventStore.Entry>} Promise object that resolves with the retrieved image data
+	 * @returns {FC.PromiseWithProgress<App.Art.GenAI.EventStore.Entry>} Promise object that resolves with the retrieved image data
 	 */
 	function getImage(slaves, options = {}) {
 		const progressFns = [];
@@ -408,7 +403,7 @@ App.Art.GenAI.reactiveImageDB = (function() {
 				/**
 				 * Do something when there's progress on generating an image
 				 * @param {(progress: number) => void} fn A function to call when there's progress
-				 * @returns {PromiseWithProgress<App.Art.GenAI.EventStore.Entry>}
+				 * @returns {FC.PromiseWithProgress<App.Art.GenAI.EventStore.Entry>}
 				 */
 				onProgress(fn) {
 					progressFns.push(fn);
diff --git a/src/art/genAI/stableDiffusion.js b/src/art/genAI/stableDiffusion.js
index be45c82c7789ee5134a7292029607136c610d972..777a6e194c9602965b710ce2ed6c9a5646ad9062 100644
--- a/src/art/genAI/stableDiffusion.js
+++ b/src/art/genAI/stableDiffusion.js
@@ -641,34 +641,70 @@ App.Art.GenAI.StaticCaching = class {
 	 * @param {FC.SlaveState} slave - The slave to update
 	 * @param {number | null} replacementImageIndex - If provided, replace the image at this index
 	 * @param {boolean | null} isEventImage - Whether request is canceled on passage change and which step setting to use. true => V.aiSamplingStepsEvent, false => V.aiSamplingSteps, null => chosen based on passage tags
+	 * @returns {FC.PromiseWithProgress<void>}
 	 */
-	async updateSlave(slave, replacementImageIndex = null, isEventImage = null) {
-		const base64Image = await this.fetchImageForSlave(slave, isEventImage);
-		const imageData = getImageData(base64Image);
-		const imagePreexisting = await compareExistingImages(slave, imageData);
-		if (!isEventImage) {
-			let vSlave = globalThis.getSlave(slave.ID);
-			// if `slave` is owned but the variable has become detached from V.slaves, save the image changes to V.slaves instead
-			// but don't do it for temporary images because they might be intentionally using a copy of a slave for temporary changes
-			if (vSlave && slave !== vSlave) {
-				slave = vSlave;
-			}
-		}
-		// If new image, add or replace it in
-		if (imagePreexisting === -1) {
-			const imageId = await App.Art.GenAI.staticImageDB.putImage({data: imageData});
-			if (replacementImageIndex !== null) {
-				await App.Art.GenAI.staticImageDB.removeImage(slave.custom.aiImageIds[replacementImageIndex]);
-				slave.custom.aiImageIds[replacementImageIndex] = imageId;
-			} else {
-				slave.custom.aiImageIds.push(imageId);
-				slave.custom.aiDisplayImageIdx = slave.custom.aiImageIds.indexOf(imageId);
+	updateSlave(slave, replacementImageIndex = null, isEventImage = null) {
+		const progressFns = [];
+		const result = Object.assign(
+			new Promise((resolve, reject) => {
+				(async () => {
+					const base64Image = await this.fetchImageForSlave(slave, isEventImage);
+					const imageData = getImageData(base64Image);
+					const imagePreexisting = await compareExistingImages(slave, imageData);
+					if (!isEventImage) {
+						let vSlave = globalThis.getSlave(slave.ID);
+						// if `slave` is owned but the variable has become detached from V.slaves, save the image changes to V.slaves instead
+						// but don't do it for temporary images because they might be intentionally using a copy of a slave for temporary changes
+						if (vSlave && slave !== vSlave) {
+							slave = vSlave;
+						}
+					}
+					// If new image, add or replace it in
+					if (imagePreexisting === -1) {
+						const imageId = await App.Art.GenAI.staticImageDB.putImage({data: imageData});
+						if (replacementImageIndex !== null) {
+							await App.Art.GenAI.staticImageDB.removeImage(slave.custom.aiImageIds[replacementImageIndex]);
+							slave.custom.aiImageIds[replacementImageIndex] = imageId;
+						} else {
+							slave.custom.aiImageIds.push(imageId);
+							slave.custom.aiDisplayImageIdx = slave.custom.aiImageIds.indexOf(imageId);
+						}
+						// If image already exists, just update the display idx to it
+					} else {
+						console.log('Generated redundant image, no image stored');
+						slave.custom.aiDisplayImageIdx = imagePreexisting;
+					}
+				})().then(resolve).catch(reject);
+			}), {
+				/**
+				 * Do something when there's progress on generating an image
+				 * @param {(progress: number) => void} fn A function to call when there's progress
+				 * @returns {FC.PromiseWithProgress<void>}
+				 */
+				onProgress(fn) {
+					progressFns.push(fn);
+					return result;
+				}
 			}
-			// If image already exists, just update the display idx to it
-		} else {
-			console.log('Generated redundant image, no image stored');
-			slave.custom.aiDisplayImageIdx = imagePreexisting;
-		}
+		);
+
+		// TODO: Only do this if it's the current image being generated
+		const interval = setInterval(async () => {
+			const response = await fetch('https://home.local:9728/sdapi/v1/progress?skip_current_image=true', {
+				method: 'GET',
+				headers: [
+					['accept', 'application/json'],
+				],
+			});
+			const progress = (await response.json()).progress;
+			progressFns.forEach((fn) => fn(progress));
+		}, 1000);
+		result.finally(() => {
+			clearInterval(interval);
+			progressFns.forEach((fn) => fn(1));
+		});
+
+		return result;
 	}
 };