Load new passage images instantly

Twine Version: 2.3.12
Story Format: SugarCube 2.34.1
I’m making a dating simulator, and there are parts where I want the image from the next passage to load instantly, without a flash of a solid background. (For example, when you select a correct response, the passage changes to one where the character’s image changes to one where they are in the same pose and position, but they are now smiling; when the background flashes, this transition looks choppy.)
I searched for some way to cache or preload images and found a couple of examples that worked for other people, but unfortunately not for me. If needed, I can provide a link to my game which may help visualize what I’m aiming to do.
(Apologies if I’ve gotten the format or something wrong, I’ve never posted on this forum before.)

It would help if you supplied a code example of how you are currently displaying your images, both the initial displaying of the images and also how you are changing the image being shown.

Is the image being displayed within the area of the page reserved for Passage content?
Have you used JavaScript & HTML to created a custom area of the page (above the standard Passage area) to display the image in?

I’m very new to coding so sorry if I answer your question incorrectly. First of all, here is the portion of my Stylesheet in question:

body[data-tags~="enter"] { background-image: url("https://gaypeoplechat.weebly.com/uploads/1/3/5/4/135471765/journal1.jpg_medium"); background-size: width="1040" height="760"; background-color: pink; background-position: center top 60px;}
body[data-tags~="enter"] .passage { padding: 0px; transition: none;}

body[data-tags~="name"] { background-image: url("https://gaypeoplechat.weebly.com/uploads/1/3/5/4/135471765/journal2.jpg_medium"); background-size: width="1040" height="760"; background-color: pink; background-position: center top 60px;}
body[data-tags~="name"] .passage { padding: 0px; transition: none; }

body[data-tags~="pronoun"] { background-image: url("https://gaypeoplechat.weebly.com/uploads/1/3/5/4/135471765/journay3.jpg_medium"); background-size: width="1040" height="760"; background-color: pink; background-position: center top 60px;}
body[data-tags~="pronoun"] .passage { padding: 0px; transition: none;}

body[data-tags~="division"] { background-image: url("https://gaypeoplechat.weebly.com/uploads/1/3/5/4/135471765/journal4.jpg_medium"); background-size: width="1040" height="760"; background-color: pink; background-position: center top 60px;}
body[data-tags~="clean"] .passage { padding: 0px; transition: none; }

(Edit: please ignore those image url links, the website name is an inside joke made by my a friend)
Here is an example of one of the passages, this one is tagged ‘division’.

<div class="vertical">
What is my magic division?
[[Arts|clean][$division]]
[[Elemental|clean][$division]]
[[Manipulation|clean][$division]]
</div>
<<audio "pageflip" play>>

The passages themselves are tagged with the corresponding data tags, that is, they don’t have img src in them; the background-image is importing it to the passage. Is that what is causing the delay? If so, is there a way I could get around that? Thank you for the quick response!

FYI - When you’re doing this kind of thing:

[[Arts|clean][$division]]

The [$division] part isn’t doing anything. The above line is equivalent to this:

[[Arts|clean]]

Did you perhaps mean to write something more like this?:

[[Arts|clean][$division = "Arts"]]

Since that would set $division to “Arts”.

Anyways, to answer your original question, since the image to be displayed is based upon passage tags, that actually makes writing some code to automatically cache the images which might be shown in the next passage much simpler.

The first thing you’d want to create is a lookup table in your JavaScript section which would let you create lists of images to cache for each passage tag. So, it might look something like this:

setup.images = {};
setup.images.division = ["https://website.com/uploads/division.jpg"];
setup.images.union = ["https://website.com/uploads/union.jpg"];
setup.images.multiple = ["https://website.com/uploads/multiple1.jpg", "https://website.com/uploads/multiple2.jpg"];

The last example shows how you could set up multiple images to be cached using a single passage tag, by putting multiple elements into that array.

Then, during each :passagerender event you could search any links for “data-passage” attributes to determine what passage they link to. You could then use the tags() function to determine what tags are in those passages, and then you could use that lookup table to determine what images need caching.

I’m about to go to work, so I don’t have time to provide any code, but that should help point you in the correct directions.

Hope that helps! :slight_smile:

Wow, thank you for pointing that out! I’m not sure how I missed that, but it certainly would have taken me a long time to catch that later down the road.

Okay, I’m very new to JavaScript, so I could be missing something critically obvious. Here is what I put in my JavaScript text:

//passagerender implement test
/* Execute the handler function each time the event triggers. */
$(document).on(':passagerender', function (ev) {
	setup.images = {};
setup.images.division = ["https://gaypeoplechat.weebly.com/uploads/1/3/5/4/135471765/journal4.jpg_medium"];
setup.images.pronoun = ["https://gaypeoplechat.weebly.com/uploads/1/3/5/4/135471765/journay3.jpg_medium"];
setup.images.name = ["https://gaypeoplechat.weebly.com/uploads/1/3/5/4/135471765/journal2.jpg_medium"];
setup.images.enter = ["https://gaypeoplechat.weebly.com/uploads/1/3/5/4/135471765/journal1.jpg_medium"];
	//javascript code
});

(again, sorry for those website titles)
This gave me no errors, but I compared it to the game without this code, and there was no difference in the delay times between the images. I also tried to implement what you said about the tags() function, but my understanding of coding is pretty awful, so sorry if this looks like a product from the seventh ring of hell.

//passagerender implement test
/* Execute the handler function each time the event triggers. */
$(document).on(':passagerender', function (ev) {
	if (tags().includes("enter")) {
setup.images.name = ["https://gaypeoplechat.weebly.com/uploads/1/3/5/4/135471765/journal2.jpg_medium"];	
	}
	else if (tags().includes("name")) {
setup.images.pronoun = ["https://gaypeoplechat.weebly.com/uploads/1/3/5/4/135471765/journay3.jpg_medium"];		
	}
	else if (tags().includes("pronoun")){
setup.images.division = ["https://gaypeoplechat.weebly.com/uploads/1/3/5/4/135471765/journal4.jpg_medium"];
	//javascript code
			}
});

I know I’m doing something wrong, I’m just having difficulty understanding the documentation without any prior JavaScript knowledge, so I don’t really know what I’m doing wrong.
Thank you so much for your time and help!!

Sorry, I hoped I was clear enough, but I was just about to start work. Here’s some code that does what you want:

setup.images = {};
setup.images.division = ["https://gaypeoplechat.weebly.com/uploads/1/3/5/4/135471765/journal4.jpg_medium"];
setup.images.pronoun = ["https://gaypeoplechat.weebly.com/uploads/1/3/5/4/135471765/journay3.jpg_medium"];
setup.images.name = ["https://gaypeoplechat.weebly.com/uploads/1/3/5/4/135471765/journal2.jpg_medium"];
setup.images.enter = ["https://gaypeoplechat.weebly.com/uploads/1/3/5/4/135471765/journal1.jpg_medium"];

$(document).on(':passagerender', function (ev) {
	var ptags = [], curtags, i;
	$(ev.content).find("a[data-passage]").each(function() {
		curtags = tags($(this).attr("data-passage"));
		for (i = 0; i < curtags.length; i++) {
			ptags.pushUnique(curtags[i]);
		}
	});
	for (i = 0; i < ptags.length; i++) {
		if (UInv.isProperty(setup.images, ptags[i])) {
			UInv.cacheImages("", setup.images[ptags[i]]);
		}
	}
});

You’ll need to add a full list of tag to URL conversions in the code at the top of that JavaScript in your JavaScript section, and you’ll need to add all of the code from UniversalInventorySystem.js to the bottom. (That code is from the “Universal Inventory System” (UInv), which I wrote to help making inventory systems in Twine/SugarCube games a bit easier, and it includes image caching code.)

For details on methods used in the above code see:

  1. The jQuery .find() method.
  2. The jQuery .each() method.
  3. The jQuery .attr() method.
  4. The SugarCube .pushUnique() method.
  5. The UInv .isProperty() method.
  6. The UInv .cacheImages() method.

Anyways, once you have that code in your JavaScript section, it will automatically look at any links in the current passage, find out what passages those links go to, get the tags on those passages, and then it will cache any images you’ve set up with a matching property name on the setup.images object.

If the path starts the same for all of your images (as in the example above), then you could shorten your code a bit like this instead:

setup.images = {};
setup.images.division = ["journal4.jpg_medium"];
setup.images.pronoun = ["journay3.jpg_medium"];
setup.images.name = ["journal2.jpg_medium"];
setup.images.enter = ["journal1.jpg_medium"];

$(document).on(':passagerender', function (ev) {
	var ptags = [], curtags, i;
	$(ev.content).find("a[data-passage]").each(function() {
		curtags = tags($(this).attr("data-passage"));
		for (i = 0; i < curtags.length; i++) {
			ptags.pushUnique(curtags[i]);
		}
	});
	for (i = 0; i < ptags.length; i++) {
		if (UInv.isProperty(setup.images, ptags[i])) {
			UInv.cacheImages("https://gaypeoplechat.weebly.com/uploads/1/3/5/4/135471765/", setup.images[ptags[i]]);
		}
	}
});

That just moves the repeating part of the URLs from the top part down to the call of the .cacheImages() method below.

Enjoy! :slight_smile:

P.S. I’d recommend against naming your files like “journay3.jpg_medium”, and instead you should use something like “journay3_medium.jpg” which keeps the “.jpg” extension at the end. A few browsers, like Internet Explorer, use that extension to determine how to decode the images, so an image with an extension of “.jpg_medium” would get displayed as a broken image in those browsers, even if it’s a valid image file.

That’s totally on me! You were perfectly clear, I’m just very new to this, and have difficulty understanding even the most intuitive of concepts.
Anyways–thank you so much, the code worked first try! And that doesn’t usually happen for me, haha. I must admit, I was super intimidated seeing that huge page of code, but now I’m honestly just inspired to try and understand it all given the resources you’ve provided to me. Seriously, you’re one skilled individual.

Interesting, I had no idea! The .jpg_medium is the default from the website I was downloading those images from, but I’ll start converting them now that I know that. Thanks for catching another one of my careless errors, haha.
Thanks so much for all this, I was really worrying the delay would totally ruin my game, now I don’t have to worry about it at all. Have a great day/night! :smile: