TADS 2 in a Web Browser

Web browsers are finally getting enough features that it’s possible to run the TADS 2 interpreter in them. I’ve made a rough proof-of concept port of the official TADS 2 interpreter to HTML5.

Code:
https://github.com/my2iu/TADS2-html5

Runnable version:
https://my2iu.itch.io/tads-2-interpreter

It requires a very recent browser such as Firefox 55, Chrome 60, or Safari 10.1. Edge will gain support for the necessary features in the fall. Notably, I was able to run some TADS 2 games on Android through a web browser.

The UI is not very polished. I was mostly making the port out of curiosity. The port does have rudimentary support for htmltads, images, and game saving though (I don’t own an iphone, but I suspect that game saving won’t work on the iphone due to the lack of files on the iphone).

-Ming

2 Likes

This is really interesting. I would be curious how much work it would take to do something like this for TADS 3. I’m in process of setting up some classes where TADS is going to be used, mainly due to how similar it is to existing programming languages (structurally, that is) such as C# and Java.

One of the things I’m struggling with is having good interpreters, including for web.

Regardless of all that, however, kudos on this. I’m one of those who think that TADS’ web options are a pale shadow compared to Inform’s. I would like to rectify this or, at the very least, gather together people who can do so. I have a few developers that are willing to help write some interpreters, assuming they can get reliable information on how best to do so with TADS (i.e., specifications). They are interested in doing this in Java, cross-platform C#, Python or even Ruby. Currently the preference is for either Java or Python.

That said, having a JavaScript-based option like this would also be a fantastic addition and make it easy for folks to share works without jumping through too many hoops.

Haven’t looked into emscripten for a while. Last time I checked it, it wasn’t possible to use setjmp/longjmp, which is what TADS 3 relies on for exception handling. And I think that’s still not possible and probably never will be :-/

Just to make sure this is a real blocker and we aren’t dismissing the Emscripten route too hastily… the Emscripten portability guidelines page says “we support proper setjmp/longjmp, i.e., jumping down the stack, but not jumping up to an unwound stack, which is undefined behavior)”.

Are we sure TADS3’s use of setjmp/lngjmp isn’t limited to jumping down the stack? I understand why things such as T3 exception handling might need to jump down the stack, but, hopefully, not up?

I can see also why TADS2 would need to manipulate the stack in very unusual ways, in that restoring a saved game somehow needs to reset the stack to what it was at the point the game was saved. But TADS3 doesn’t actually reset the stack, only object states - i.e., after restoring a game in a given T3 function, we are still in the same T3 function on the same call stack.

I kind of remember too that TADS3 is extremely zealous in looking for random data sources too feed its ransom number generator, and might be using the unwound stack contents for that, but presumably this source could be de-activated.

Could this provide some hope that TADS3’s use of setjmp/lngjmp is limited to simpler things than TADS2, which don’t require such unusual stack manipulation?

This is all pure speculation from someone who has never had a proper look into this; I haven’t checked any of those assumptions, hopes, and speculations, so feel free to dismiss this all as pure gibberish.

Not sure why exception handling would ever need to jump down the stack. It’s always upwards: When an exception occurs in the current frame, you longjmp() back (= up) to the enclosing frame where setjmp() was used.

Maybe it’s a typo and they meant to say “down” instead of “up”?

In any event, this wasn’t supported at all back when I last looked at it. This seems to have changed now. It might be possible to do an emscripten port of TADS 3.

By down I actually meant back, and I was assuming that’s what they meant too. Otherwise I don’t see how jumping back would be, in their terms, “undefined behaviour”.

The DOS version of the Tads3 runtime runs in js-dosbox now.

Example: howtophil.com/tads/index.html

I did find some time to have another look at this project, and I’ve gotten the base TADS3 code to compile with Emscripten now (just basic TADS3 files, not the WebUI TADS3 files). I haven’t tested it extensively, but it seems to be able to start some basic t3 files, so this does seem like a viable path forward to running TADS3 in a browser. Unfortunately, it only works on Chrome in the desktop currently due to browser limitations. You’ll have to compile it yourself if you want to try it since I think it’s still too untested to upload a compiled version.

This is exciting to see! I am planning to compile TADS2/3 in my emglken project, so eventually that will be part of Parchment as well. Eventually.

Why does the way you’re doing it need shared array buffers?

Great news!

Are you targetting webassembly or JS?

I think the TADS3 stuff is running properly now, but I haven’t tested extensively.

my2iu.itch.io/tads3-interpreter

I also cleaned up the UI a bit so that it isn’t completely embarrassing like before.

I tried looking into implementing the WebUI stuff, but that looks iffy because it seems like a lot of the WebUI HTML and JS code is embedded in the game file itself, so it’s not possible to get it working by just changing the TADS interpreter. The game files would have to be modified too somehow with updated HTML and JS code.

The project targets WebAssembly. I find that there are too many strange errors when targeting asm.js with Emscripten. Sometimes the code runs on certain browsers and sometimes it doesn’t. Plus, the code is so large that it loads really slowly.

SharedArrayBuffers were necessary because JavaScript is event based whereas the TADS code is not. For example, the TADS code will stop in the middle of execution to wait for input. Traditional JavaScript does not allow a thread to receive any form of input until it exits, allowing it to receive an event with the new input. Doing that with TADS would cause the TADS interpreter to exit. SharedArrayBuffers provide new synchronization primitives that allow the TADS WebAssembly code to stop and wait for input in a way that’s compatible with the TADS codebase.

Unfortunately, it’s unclear what the fate of SharedArrayBuffers is. I think a lot of the SharedArrayBuffer developers are the same as the WebAssembly people. In the end SharedArrayBuffers are not usable directly by WebAssembly, so those browser developers have lost interest in pushing for SharedArrayBuffers and are instead working on something new for WebAssembly instead. That could take 2-3 years.

Oh, just to clarify. The SharedArrayBuffer stuff was needed for TADS2 because of the design of the TADS2 interpreter. Since I already had everything working for TADS2, getting TADS3 working mostly just involved compiling it. I didn’t actually take a close look at the TADS3 code base.

It’s possible that the TADS3 interpreter has a design that doesn’t require SharedArrayBuffer (notably, an interpreter loop that manages its own stack). TADS2 didn’t use that design. The TADS2 interpreter loop interprets subroutines by actually calling a TADS2 function, storing data in the normal program stack, so that design required the use of SharedArrayBuffer.

I’m using the Emterpreter, which lets it all run in a single thread, but at a performance hit. It hasn’t been too noticeable of a performance hit so far, except for the heaviest Glulx games. I’d expect TADS 2 could cope fine being partially Emterpreted. Not sure about TADS 3.

I tried using the Emterpreter on TADS2 a couple of years back, and I couldn’t get it to work then. The TADS interpreter did different things and corrupted itself when run in the Emterpreter when compared to running as asm.js (TADS in the Emterpreter was interpreting a different set of TADS instructions than what was running in the non-Emterpreter version). Unfortunately, it was too hard for me to figure out how to debug the Emterpreter. It’s possible I had a memory bug or something with my modifications to the TADS code, but I think I had made very few changes to the code at that point (just enough to print out some text). So I had to abandon that approach.

Wow! These are very good news! Will it be possbile then to integrate TADS 2/3 into Lectrote?
If I can test or help out, let me know.

Revivifying this old thread to check in on the current state of Tads in the browser. I saw that @Dannii had added Tads (2 and 3?) to emglken, but it doesn’t seem like that’s hosted in a web interpreter yet?

Looking at the feature support for SharedArrayBuffer perhaps it now three years later has wider adoption? SharedArrayBuffer - JavaScript | MDN

Appreciate any insight!

1 Like

TADS 2/3 support is in Lectrote, and it’s also been added to the IFComp site, so if there are any TADS entries this year, they’ll have online play support. Updating the Parchment that runs on iplayif.com is my next project.

5 Likes

The code I posted that uses SharedArrayBuffer does currently work on Android and other Chrome browsers if you manage to set all the weird COOP/COEP headers needed for SharedArrayBuffer support. Unfortunately, it’s unclear if Apple will ever get around to enabling SharedArrayBuffer support in Safari and iOS, so it’s probably better to use Dannii’s emglk version that works in all browsers.

3 Likes

I use latest Emglken in this player:
https://he4et.github.io/elseifplayer/

You can run textonly TADS games in your browser now =)

7 Likes