Introducing iffinity, a new minimal IF engine with iffinite possibilities!

First off, glad to have you back, Sotiris! :slight_smile:

Thanks for the in depth reply. Lots of good stuff to digest!

iffinity is a programming framework. Itā€™s not an IF authoring application. Things can get complicated beyond expectations in a browser environment (as you illustrated). As Iā€™ll explain in a bit, iffinity doesnā€™t necessarily enforce any particular way of presenting a game so itā€™s totally understandable restoring a game is outside the scope of iffinity.


Iā€™ve only really used Chapbook and Harlowe (both do this ā€œauto-restore on refreshā€ thing). Chapbook would be my suggestion to look at. I found that itā€™s easier to look under the hood in Chapbookā€™s GitHub repository. Itā€™s built by Chris Klimas @klembot who also built Twine (so he would definitely know all about this refresh scenario). Chris also built Snowman.


Kind of unrelatedā€¦

In addition to saving/loading the game state JSON (which I havenā€™t done yet either), I may implement storing the innerHTML of the entire page because if game logic alters any HTML content after the snippet is drawn, that information would be lost/unknown to the game state as well. (You could rebuild everything manually upon restoringā€¦ but HTML is just a text string that describes everything anyway. It seems like the easiest thing to do as long as the string length isnā€™t ridiculous for a browser to handle.) To my knowledge, no Twine story format does this innerHTML save/restore thing, though I am not familiar with SugarCube (the most popular Twine story format for intermediate/advanced authors).

I do like to make things difficult apparently. :wink:

3 Likes

Very interesting remarks.

Iā€™ll definately check both Chapbook and Harlowe to see how they implement this (Iā€™ve only used Snowman to be honest).

Storing the whole innerHTML is a nice idea, albeit a bit ā€œhackyā€, in the sense that you must make absolutely sure that restoring the HTML leads you to a stable state. In my previous game which had a ridiculous amount of state management and logic, I generally tried my best to abide to the following 2 rules, which I list here in case anyone else finds them interesting for their own projects/design processes:

  1. Make sure that the game state at the beginning of each snippet is stable and reproducible i.e., given the specific state you can ā€œloadā€ the game from this snippet without any problems and be able to continue from there (crucial if you want to implement a full-fledged save/load mechanic for game).
  2. Try to make state changes snippet-changing or, if that can not be done in your particular case, try to figure out whether these changes really need to be reflected on your ā€œglobalā€ state or whether you can keep a smaller, ā€œlocalā€ state for your snippet and then, when the ā€œbigā€ change comes, change the snippet (and the actual, global state).

These are just rough guidelines that I try to adhere to because I feel they make my life a bit easier, by turning (the beginning of) snippets into well-defined checkpoints.

@HAL9000, Iā€™d be really interested to see whether you make something interesting out of this innerHTML-caching idea. Also, keep in mind that you can always use some kind of compression - there are numerous JS libraries that implement these and also in Snowman, when you save your state (similarly to iffinity), it doesnā€™t return a JSON but an encoded string. I also thought about doing the same in iffinity but, again, that would contrast its main idea: it should offer the basic functionality (ā€œhereā€™s your state as isā€) and then you can do whatever you want with it (compress it? cache it? etc).

4 Likes

So, I was thinking about whether iffinity should incorporate some kind of mechanism to ease the handling of multimedia (sounds, music, images, GIFs, etc), and I would love to hear comments/feedback on this from this thread.

Iā€™d like to especially address @Cerfeuil because I took a peak at their game Messages From the Universe Graveyard and I have to say it looks quite intriguing and well-polished (I hope the server-side has been working smoothly so far!). I hope to find some time in the following days to actually play it. For now, I took a very brief look and I also browsed part of the codebase, mainly to see how you handle your many multimedia assets.

So, would you feel that there could be a way for iffinity to maybe streamline the inclusion of multimedia (while following its main ā€œphilosophyā€ regarding simplicity and, as @HAL9000 nicely put it, more-of-a-framework-than-an-end-product approach)? While working on your game, would you feel that the engine, as you experienced it, could help you in managing your assets?

Iā€™m specifically referring to @Cerfeuil because of their game, but obviously the request for feedback is open to all!

3 Likes

Given that weā€™re working with multi-media content I took the view, when creating Rez, that handling of media assets needed to be built in.

So there is an @asset element to specify e.g. an image, sound, or movie file and asset_tag & asset_path filters for working with them in templates. E.g.

  @asset foo_image {
    file_name: "foo.png"
  }

  @card with_image {
    bindings: {img: #foo_image}
    content: ```
    ${img | asset_tag}
    located at: ${img | asset_path}
    ```
  }

Would generate the appropriate <img /> tag with width & height (we get this data when we compile the game) and display the path to the asset in the compiled game. The compiler also manages finding the image in the project and putting it in the right place in the dist folder.

The two drawbacks of this method are (1) you have to specify your assets in advance. If you had a lot of assets that might be a pain, and (2) each asset has to have a unique file name.

With those caveats in mind I have found this makes media really easy to work with and enjoy not having to muck about with file paths.

4 Likes

Absolutely! I would argue there is no point making a new IF system that didnā€™t handle media. Things like;

  • pictures
  • sounds
  • ambient sounds
  • animation (at least simple types)
  • video?

Regarding pictures and sound, you donā€™t really have to support many formats. In fact you could just have WEBP for images and OGG for audio. In reality they are the best deployable formats in any case (at current time).

2 Likes

In principle I agree, but Iā€™m not particularly sure whether that is required for the bare-bones approach of iffinity.

Let me elaborate on that: iffinity produces a single HTML file as its output. So, in principle, you can handle your assets as you would in any HTML: have the according tags (e.g. <img>) with the appropriate paths and just distribute the output HTML along with a folder with your assets. The question, then, is whether iffinity should:

  1. Give you an automated way to do that (i.e., to create a dist folder with everything inside and handle the file paths, for example, or even copy them from all over your machine to the dist folder)
  2. Provide any other tools or mechanisms (that Iā€™m currently not aware of) that could somehow streamline the default asset-handling process that I described in the previous paragraph.
2 Likes

OK understood. In that case, it should automate the dist output as youā€™ve suggested.

I think a big problem with web deployment is managing the source media and the dist media. Letā€™s say your source images are large and in editable formats such as PNG. Your ā€œpublishā€ feature should convert those images to lower resolutions (with various options) and to deployment formats such as webp or jpg. And copy those into a dist folder with the appropriate paths adjusted.

1 Like

Thatā€™s an interesting idea that I hadnā€™t thought of. I could definitely build this in myself, although itā€™s annoying having dependencies on external binaries (one of the reasons I dropped Handlebars.js for my templates). But in this case I guess the fallback is donā€™t convert to a ā€œdeployment friendlyā€ format.

3 Likes

What Iā€™m doing now is converting my ā€œeditā€ format images to a set of lower res ones with various compression factors. The web runtime chooses which one to fetch depending on what system you have. I also measure the time taken and adjust downwards accordingly.

So usually mobile devices have terrible bandwidth, so i jump straight to low res. Desktops are normally good, but it could be a laptop on a slow connection, so here i adjust down systematically.

Regarding depending on external tools, i completely agree. This is always the weak point. My plan now is to make the system also the tools. So the system itself performs this deployment process with no dependencies. Of course, the code to do this is only in the tools build version and not in the release runtime, to avoid bloating it.

3 Likes

@MightyPigeon

For me, iffinity seems practically complete already. When I started using iffinity, I had moved down the Twine path of Harlowe, then Chapbook and was just starting to learn Snowman. Snowman requires a level of coding competence that dealing with media becomes trivial. In itā€™s current state, I like that iffinity doesnā€™t get in the way or carry extra bloat that I will never use. I donā€™t even have to override any ā€œbuilt-inā€ CSS properties. Itā€™s really a clean slate. So I think thatā€™s why I enjoy iffinity so much because you developed it, and I came at it, from the same Snowman mode of thought. When you have a user that ignores the other story formats of Twine and wants to use Snowmanā€¦ what does that tell you about what they are looking for?

I had a problem with Harlowe where running its built-in Harlowe listener caused flickering of other HTML elements. Even in Harloweā€™s current mature state, problems arise due to the complexity inherent in browsers. I couldnā€™t use Harlowe after that, unfortunately.

There is a sayingā€¦

ā€œjack of all trades, master of none.ā€


This is not to put down any desire to evolve iffinity. Iā€™m just thinking that you should keep a version of iffinity that remains true to your current visionā€¦ and do it well. It might already be there, in fact. However, thereā€™s nothing stopping a new fork or repository of iffinityā€¦

There is a funny saying when we try to ā€œoutdoā€ each other. Like when we say, ā€œI love you infinity.ā€ And the other replies, ā€œI love you infinityā€¦ plus one.ā€ Maybe iffinity +1 is the more robust, user-friendly, media savvy app!

3 Likes

I think this is a brilliant summary of a use case.

2 Likes

GIF support would be nice. You can do a lot with some cleverly designed GIFs. My initial take on ADRIFT had the astronaut slowly spinning in empty space, with the sun lazying by his vision every few seconds. I built a whole GIF for this from scratch only to realize that the non-Vorple image support for Inform 7 canā€™t handle GIFs and I had to backpedal and actually rewrite a large chunk of the game late in the process due to this. My fault for not checking first, but, yeah, GIF support would be nice.

3 Likes

Itā€™s really cool to hear you checked out my game! I personally didnā€™t have any issue managing visual assets, at least not on the level that I wished iffinity could help. It was easy to make an assets folder in the export folder, dump all my assets in there, and just refer to them with url(assets/examplepath.jpg) or whatever when I needed them.

iffinity compiles to HTML, which being HTML, comes with built-in ways to display images and GIFs! And videos too, if anyone wants to go that route. It helps a lot.

Managing audio was more complicated for me. Not in terms of where it was stored (I just had a assets/music_real folder for everything), but playing it in-game. I ended up installing Howler for background audio, and then writing my own function to slowly fade out the the background track thatā€™s currently looping and slowly fade in the new background track when you enter the appropriate area for it (code here). Iā€™m not entirely sure itā€™s the most efficient way to do things, I think the way I have it loads all the looping audio at the start of the game even if most people will only hear a few tracks at best, though I could be wrong.

Still, Iā€™m not sure how helpful it would be to add audio support. Sugarcube has a bunch of audio macros, though theyā€™re more limited than Howlerā€™s options, and still pretty complicated to set up the way you want once you get into it. If I remember right, they donā€™t have a macro for ā€œslowly fade out the the background track thatā€™s currently looping and slowly fade in the new background trackā€, so if you wanted to do that in Sugarcube youā€™d have to make a function for it anyway, using their notation.

I like the relative minimalism of iffinity since itā€™s far more customizable and lets you specify exactly how you want things. I only wanted background audio for my game, no one-time sound-effects, so I didnā€™t have to worry about adding in anything for sound effects.

TLDR: Adding more support for visual/audio assets wouldnā€™t hurt, but Iā€™m not sure if Iā€™d use it. It does depend a lot on how itā€™s executed, though. In general I agree with Halā€™s preference for iffinityā€™s relative minimalism and donā€™t think adding new stuff is all that necessary at this point.


Things I did find myself thinking ā€œthis would be nice to haveā€ while working on my gameā€¦ Sugarcube has a <<replacelink>> macro (or someone made one and I put it in all my projects because I liked it so much), where you can click an element and it gets replaced with another element. I actually replicated that behavior with a function of my own (which is much jankier in practice), since I liked it so much. Maybe built-in support for something similar would be nice?

Two other things I made were menu panels that open when you click a specified element and automatically come with a close button, and code to add wrapper html around the iffinity snippet since I got tired of copy-pasting the same HTML divs (used for CSS purposes) around the unique contents of every snippet.

Even if those things didnā€™t get added, I could just use my own code for it. Anyone else could too, though theyā€™d have to add the extra esbuild stuff that Iā€™ve been using. Actually I ended up making my own iffinity template folder that has starter files and a bunch of extra stuff/modules I found helpful in my iffinity projects, and I should maybe put that on Github at some point.


Also, re: the refresh thing, I was thinking about Sugarcube which Iā€™m pretty sure does this out of the box. Itā€™s honestly not that important for me, though, since most browser games reset your progress when you refresh. Twine is more the exception than the rule there. And it is somewhat convenient, but still runs the risk of e.g. if you refresh on a page that increases a variable by 1, the variable would increase again (at least I feel thatā€™s what would happen. Havenā€™t actually tested that kind of thing). Itā€™s deceptive because it seems like nothing has changed, but if thereā€™s complicated game logic it might disrupt the internal workings.

Glad to hear youā€™re doing okay! I wish you a happy Masterā€™s-obtaining-experience and a less hectic schedule. Sounds rough out there.

3 Likes

Sweet! :grin:

2 Likes

Thank you SO much for your input, it really means a lot, especially since youā€™ve made a complete game with iffinity.

Iā€™m particularly interested in the things you mentioned that could potentially be supported by iffinity:

  1. The <<replacelink>> functionality sounds nice for a built-in feature; maybe another iff-tag. Iā€™ll open an issue for that in GitHub and will definitely come back to it.
  2. Your issue with Howler was actually something I was thinking of but never got to deal with it. Obviously, an author may need external libraries. Itā€™s what I tried to do with the library scripts/styles, but when doing this I was thinking of actually downloaded libraries, like a minified version of jQuery, for example, that would eventually be part of the output HTML. An idea here is to give an option of defining library CDNs in the config file, so that they are used directly when playing the game, with no need to download/pack them in the output HTML. The only drawback to this is that your game will then only work online, but Iā€™m not sure whether that is an actual problem. There could even be a boolean flag offline in the config file and when thatā€™s true, instead of appending the CDN(s) to the <head>, iffinity would actually download and pack them (as it already does with jQuery, for example).
  3. What you describe with the menu panels and the need to wrap the default iffinity HTML is what I think could be described as HTML templates, right? I was actually thinking about it. The reason I didnā€™t directly do something to support this is because (for no good reason) I relied on using the tag system and the different levels of scripts to produce HTML via JS. I realize now that this is something that is neither user-friendly nor scalable. Correct me if Iā€™m wrong, but I guess the idea is that you would like to have an HTML (sub)component (eg the HTML code for your menus) that should be part of some/all of your snippets, right? So I guess what iffinity could do about this is give you the ability to define such an HTML (sub)component and then declare whether a snippet should include it or not. In other words, a system similar to what is now being supported for code and styling, but for HTML (sub)components (or even redefining the very structure of the page under the <iff-story> tag in a template-like fashion?). Does this sound like an interesting way to go? Obviously not very well-shaped yet, but Iā€™ll get there!

Looking forward to your opinions on any/all of the above!

3 Likes

When you mention HTML templates, I think of the interface design surrounding a <snippet>. Am I off the mark?

For example, in a JS file I did something like thisā€¦

( Click to show code... )
/*** INTERFACE CONSTRUCTION ***/
let _body = document.getElementsByTagName("body")[0];
let _interface = ' \
<div id="_top"></div> \
<div id="_middle"></div> \
<div id="_bottom"></div> \
';
_body.insertAdjacentHTML("beforeend", _interface);

/*** (!) MOVE <div id="iff-story"> INTO "_middle" ELEMENT ***/
let _iff_story = document.getElementById("iff-story");
let _story = document.getElementById("_middle");
_story.appendChild(_iff_story); // <== (!) appending an existing node MOVES IT to a new parent (associated JS variables and references still work)
2 Likes

Yeah, exactly. I may have made a mess before when trying to explain my thoughts, apologies. Itā€™s a bit fuzzy in my head as well. In fact, itā€™s not a matter of HTML modules; these you can make as regular snippets and then just include them in another snippet via the renderSnippet method. Thus, the real question is whether iffinity should support altering the default HTML structure, in order to accommodate use cases like the ones you mentioned. I think it should; not clear at all how right now. I feel that ā€œtemplateā€ is the correct buzzword, but Iā€™ll keep processing it and see if I end up with any concrete idea. As always, open to feedback/ideas/objections/whatever comes to mind!

3 Likes

I have to admit, I need to play around more with the iffinity API. After I posted, I thought about nested snippets too. To be honest, that is easier than what I did, but that just goes to show how many ways there are to skin a cat.

Eventually, after a few more games come from iffinity, I think a page from Twine can be taken with its Cookbook. (I linked to the first page of the cookbook.)

I like that it suggests basic logic practices, and fun things that might appeal to most authors. I think iffinity would benefit from something like this and, in a weird way, it would allow iffinity to remain bare bones, yet still robust because people could just copy and paste code from the cookbook.

If you think this would be a good idea, Iā€™d be willing to help with that. For example, nested snippets to achieve a new interface design, how to handle audio, saving the game state JSON to local storage, changing a specific snippet when multiple exist, etc. These sound like good recipes! :wink:

3 Likes

Oh, I have another question. With <a> tags being the default rendering of link markup [[ ... ]], I find that I donā€™t like seeing the javascript:void(0) ā€œURLā€ showing up in the browser window on hover. Iā€™ve removed the href programmatically after itā€™s rendered in the browser and this works, but it no longer exists in the keyboard tabbing focus DOM list, though the link still works. The best way I see to get my desired result, keeping browser accessibility and screen readers in mind, is to not use <a> tags, but use <button> tags with CSS to make it render inline, like an <a>.

Question 1: Can I somehow edit the rendering of the [[ ... ]] markup to use <button> instead? ā€¦or should I just use my own HTML/JS and call the iffinity API as needed?

Question 2: Kind of related, can I somehow make my own markup (shorthand code) using iffinityā€™s method of replacing markup? Like, even if it was just HTML output and not doing any other internal stuff.

2 Likes

The Cookbook sounds like an awesome idea actually, and could also be a collaborative/community-driven project (dreamming big, I know, but why not :blush: ). Iā€™ll definitely add this to my todo list. It could even be just a GitHub repo hosted via GitHub pages where people just add their own iffinity recipes. Thanks for the idea!

Answer to Question 1: Very good point and one Iā€™ve also struggled with. Truth is, that is one of the few design choices that had to be made while building iffinity. The story goes that I just ā€œcopiedā€ what was done in Twine/Snowman, where the [[...]] passage links where rendered as <a>. Nothing more than that, actually. I also didnā€™t like seeing this javascript:void(0) in my Snowman game (it happens there as well), but I didnā€™t give a lot of thought to it. You are right, though; itā€™s not nice.

To directly answer your question, unfortunately no, you can not edit the rendering of the [[...]] markup to some other HTML tag, at least not without using some (hacky) coding. But I agree that it could/should be changed. The reason I decided to stick to the rendering of <a>s for the snippet links was the way it is rendered in browsers by default i.e., as a well-recognized link (underlined, different color), which is actually a side-effect of the void href attribute. But I now realize that this again is a design choice that thereā€™s no reason to take away from the author. But because buttons have a rather involved ā€œdefaultā€ CSS, Iā€™m thinking maybe about <a>s without an href. In this case, they are still machine-friendly links, but with absolutely no default style imposed. So, that makes it the responsibility of the author to make them look like however they want, even including whether they want the cursor to turn to a pointer or not on hover (via CSS). Do you think that this is a better approach than using <button>s or is there any advantage that Iā€™m missing? Iā€™m trying to figure out the most barebones and customizable way to implement this, while still not breaking stuff (like the browser accessibility and screen readers that you mentioned).

Answer to Question 2: Iā€™m not sure I get your question. Something different than what renderSnippet does? Can you maybe provide an example?

3 Likes