After trying and struggling with the Twine engine, and after having immediate success (and a lot of fun) building my fusion rocket simulator I have decided to set out and adapt my Clay web framework into something that can generate static content.
The idea is that this would work like Twine works, you build an app at a time in markup language. But unlike twine, I'm not targeting a beginner programmer and deciding too early on a domain specific language. This is a tool that includes developing one's OWN domain specific language for the application. I will use Tcl as the build language for the application, with an sqlite backend maintaining the soup of data that maintains references. The application itself, and all of the interactives, will be in pure Javascript. And rather than try to get Javascript to play live with the data, the application will be mainly working with snapshots of data captured in JSON, and packages with the static HTML and images.
I haven't really used the server-side features of etoyoc.com's engine since the folk festival moved their registration system off. And with javascript, interactives can be delivered on a completely static platform. For eBooks and mobile, most distribution venues don't even want to look at installing an executable if it can be avoided.
So how will the "clayground" work? I am assembling a toolkit of software that ships inside of a self-contained executable, or could be loaded by a resident Tcl application installed on the local operating system. As... most of the people I know in the world use Windows, and Windows doesn't ship with one... did I mention the self-contained executable?
This executable the user will point at a series of mark-up files (read those Tcl scripts) which can be edited in your friendly neighborhood copy of notepad. Inside is a language that first describes what your application is, and then what it does. And because I'm building on top of an existing scripting language with a coherent package system, if someone develops a framework useful across a pattern of applications, that entire bundle of tools can be packaged into a plugin.
Currently my fusion rocket simulator looks like this:
Basically it's a virtual file system. The writeup is a cross between a MIME document and a TCL script. At the top are easy to read headers for a Tcl based indexer:
Title: {Design A Starship for the Sublight Universe} Class: {blog} Date: {Wed May 26 13:54:15 EDT 2021} Content-Type: {html} Format: {clay} date: {Wed May 26 13:54:15 EDT 2021} owner: 619eb03b-0f7d-490f-a2ac-9eb72e4c789d uuid: 57750880-cd29-477d-8498-5f6b79723d30
The content is a script for my ClayHttpd engine.
The magic of this arrangment is that I can embed define new commands mid-script. But this script is running inside of an object that I can ask for help, and that object is itself in contact with the local operating system, and part of a larger application that is aware of the website as a whole. I have a few creature comfort commands to handle common tasks. For instance bolding text, or adding hyperlinks, or calling up Another entry. But for complex things like tables, I can use the object interface.
para {The magic of this arrangment is that I can embed define new commands mid-script. But this script is running inside of an object that I can ask for help, and that object is itself in contact with the local operating system, and part of a larger application that is aware of the website as a whole. I have a few creature comfort commands to handle common tasks. For instance [bold {bolding text}], or adding [link https://en.wikipedia.org/wiki/Hyperlink hyperlinks], or calling up [wiki 57750880-cd29-477d-8498-5f6b79723d30 {Another entry}]. But for complex things like tables, I can use the object interface.}
Each page object also gets a pile of data from the build system/live web server that it can read at runtime.
set T [my tag table] dict for {field value} [my clay get content] { set row [$T row] $row column $field if {$field in {body}} { $row column ... } else { $row column $value } }
title | Clay-Ground - An new interactive framework |
class | blog |
date | Tue Jun 01 15:20:35 EDT 2021 |
content-type | html |
format | clay |
owner | ed44b6ff-12b4-4158-b7ca-03873dd57e42 |
uuid | 416ce7c7-c4f1-4045-b630-eda44642f365 |
body | ... |
mtime | 1622649287 |
set T [my tag table] dict for {field value} [my request dump] { set row [$T row] $row column $field if {$field in {body}} { $row column ... } else { $row column $value } }
That code returns a pretty large result so I've placed it here
A table I can call up as an object, and then add and remove objects under it for the rows and columns. Here is a complex example that builds a live table of data, and delivers it as HTML: table.txt. And I can easily run that same script live in this document:
Point A | Point B | Distance AU | 1 Day DeltaV | 7 Day DeltaV | 14 Day DeltaV | 28 Day DeltaV | 60 Day DeltaV | |
Main Belt Asteroids | Mars | nearest | 0.28 | 484808 | 69258 | 34629 | 17314 | 8080 |
Earth | Mars | nearest | 0.4 | 692582 | 98940 | 49470 | 24735 | 11543 |
Main Belt Asteroids | Earth | nearest | 0.68 | 1177390 | 168198 | 84099 | 42049 | 19623 |
Main Belt Asteroids | Venus | nearest | 0.942 | 1631032 | 233004 | 116502 | 58251 | 27183 |
Main Belt Asteroids | Mercury | nearest | 1.353 | 2342661 | 334665 | 167332 | 83666 | 39044 |
Earth | Venus | farthest | 1.828 | 3165103 | 452157 | 226078 | 113039 | 52751 |
Main Belt Asteroids | Jupiter | nearest | 3.29 | 5696494 | 813784 | 406892 | 203446 | 94941 |
Earth | Jupiter | nearest | 3.97 | 6873884 | 981983 | 490991 | 245495 | 114564 |
Main Belt Asteroids | Mercury | farthest | 5.416 | 9377572 | 1339653 | 669826 | 334913 | 156292 |
Main Belt Asteroids | Earth | farthest | 6.05 | 10475315 | 1496473 | 748236 | 374118 | 174588 |
Main Belt Asteroids | Mars | farthest | 6.61 | 11444931 | 1634990 | 817495 | 408747 | 190748 |
Earth | Saturn | nearest | 8.07 | 13972859 | 1996122 | 998061 | 499030 | 232880 |
Main Belt Asteroids | Jupiter | farthest | 10.41 | 18024469 | 2574924 | 1287462 | 643731 | 300407 |
Earth | Saturn | farthest | 11.22 | 19426949 | 2775278 | 1387639 | 693819 | 323782 |
Main Belt Asteroids | Saturn | farthest | 15.07 | 26093059 | 3727579 | 1863789 | 931894 | 434884 |
Main Belt Asteroids | Uranus | nearest | 16.74 | 28984593 | 4140656 | 2070328 | 1035164 | 483076 |
Earth | Uranus | farthest | 21.2 | 36706892 | 5243841 | 2621920 | 1310960 | 611781 |
Main Belt Asteroids | Uranus | farthest | 25.05 | 43373002 | 6196143 | 3098071 | 1549035 | 722883 |
Main Belt Asteroids | Pluto | nearest | 28.04 | 48550059 | 6935722 | 3467861 | 1733930 | 809167 |
Earth | Pluto | nearest | 28.72 | 49727450 | 7103921 | 3551960 | 1775980 | 828790 |
Earth | Neptune | farthest | 31.5 | 54540901 | 7791557 | 3895778 | 1947889 | 909015 |
Main Belt Asteroids | Neptune | farthest | 35.35 | 61207011 | 8743858 | 4371929 | 2185964 | 1020116 |
Earth | Pluto | farthest | 50.4 | 87265441 | 12466491 | 6233245 | 3116622 | 1454424 |
Main Belt Asteroids | Pluto | farthest | 54.25 | 93931551 | 13418793 | 6709396 | 3354698 | 1565525 |
Now I recognize that some complex interfaces require dynamically building HTML from live data. But I don't think that is the best use of a domain specific language. Pretty much if it's not backed in already, you can use the DOM manipulation tools built into Javascript. Which, as my fusion program shows, can get you really, really far.
My next step is a request from a friend to put together a life D&D character sheet that does all of the calculations so people can spend more time adventuring. I see three basic applications there: One is an interactive character builder for the players. The second is a campaign design/running tool for the DM. And the third is a "collaborative event" module that lets the documents created in the campaign creator and character editor all interact on a battlefield or other scenario. The real fuel for all of that will be that JSON import/export function I added to the fusion rocket tool.
JSON is a just a handy notation for luggable static data. It can be nested, or flat, or nested flat data. But it's per-object. So for the battlefield manager, I would create an object for each of the players, and the monsters the DM has pitted them against, and each object would get a blob of JSON data uploaded to the app. The rules are all implemented in the javascript of the App, and with a few UI tools to allow players to enter their own dice rolls in, we get a nice replacement for the roll-out mat and pile of nebulous tokens. And we can throw this map up on the big-screen in the living room for everyone to enjoy.
On player operates a home computer that uses an HDMI input to the screen (or apple Airplay from a laptop). The other players just lug along their character sheets, or email them to the DM. The DM can email back a transcript of the game's events, and the loot they picked up, for the characters to put back together at home.
When I'm ready to step up my game I can even add graphics.
The idea of the App as the VFS for a website plays well with distributing these tools as eBooks on a Javascript savvy reader. (Though the JSON exchange is probably not going to work.) The fact you are reading this in a browser tells you that clay renders down to static HTML pretty easily too.
What I'm most excited about is porting all of the encyclopedia and world building tools I've developed for the Sublight universe into something I can parse as a website of an eBook. I have a half-assed index and database I build with Microsoft Excel and Graphviz. But the results, because I can only really edit the data in Excel, can look a little wonky. I am working on moving the writeup for each topic to a clay file, and then indexing them ex-post-facto. In that way I can world build bottom up style, like Wikipedia.