Poof v1.1.0 Release Notes

Poof v1.1.0 is now out. To use the new version, simply reinstall it from the CDN, or download the file from the same.

This version makes only a few critical but small changes to some of the export options.

First, the PDF export has been simplified. It now requires an Internet connection on first use to cache the scripts used, but the format file has been made significantly smaller (from ~580KB down to ~170KB) Additionally, the PDF export will now no longer generate a blank file on large stories.

The Twee exporter has also been rewritten to respect sort and filter options, so the resulting Twee file is now representive of what’s on screen, not what’s in the data chunk.

The trade-off is that both of these export options now take longer and use more resources, but I believe the benefits largely outweigh the heftier performance.

In future releases:

As always I’ll be monitoring for bugs and problems. I will probably add some styling options to poof.config for the PDF export, like fonts, font size, and maybe line height and spacing.

HolyLandGame.com is Dead

Long live… meh.

Anyway, when I first started working on Holy Land, and in Twine, I ran my related web presence through the aforementioned site, including my old blog. Since I’ve, shall we say, branched out, I decided to make this website, where I can park all my Twine and coding related stuff.

The only thing that hasn’t transitioned over is my old blog. While its possible for me to get those old posts and put them here, I’ve instead decided to make them available via archives.twinelab.net for now. That link will forward you automatically to the old Holy Land Devlog Blogger site, if you’re interested.

This also means that some old links may stop working. They’ve been fixed on my site and around my GitHub repos, but there isn’t much I can do about elsewhere.

There’s a few major benefits to the changes over to twinelab.net, the new blogging software, and the cleaned up web site here, but that’s for another day.

That’s all I have to say for now; I just wanted to clear up any potential confusion.

Serious Fiction for Serious People

I’ve just released an alpha-ish version of Serious, a project I’ve been working on for quite some time. Serious generates static websites from markdown, and there’s nothing really special about that, and that’s sort of the tale of Serious in general. It’s specific and narrow in scope. So let’s try to figure out why Serious exists, who it’s for, and whether I’ve wasted all of my time.

Serial Fiction

I’ve been wanting to get into writing a web serial for literal years. Initially I planned to use Blogger, or some other online blogging software, but what immediately struck me about those platforms was just how annoying it was to present structured, ordered content, particularly in an earliest-to-latest fashion. The only real thing you could do was create a hand-edited page on the site that collected all the episodes, in order, as links. So no excerpts or pretty stuff. Even next and previous buttons didn’t really work so well. They were there, sure, but often lost in a sea of suggestions, tags, similar posts–even ads.

I never did write any serial fiction, and the idea kind of died off. It’s possible on a blogging platform, but you really are fighting the platform on this one. Not only are you getting a lot of things you don’t need, but the things you do need are kludges and amateurish link-lists.

AO3 and FictionPress

I sort of forgot about FictionPress (and its big brother, FanFiction.net) at the time I was looking into doing a serial. AO3 (Archive of Our Own) wasn’t around yet; it’s a newer but very similar take on what the other two were doing. A few years later, maybe 2012-ish I wanted to try my hand at serial fiction again. I had actually seen some success releasing content on FictionPress in my late teens or early twenties, but nothing really episodic. When I came back to it, I found it…annoying. Not in any objective way; in a ‘not for me’ way. Even so, I know I can’t rag on a site that’s doing pretty good things without any explanation, so let me start by saying a lot of nice things.

I think FictionPress is a good choice for a lot of people. Particularly, if you don’t have either a lot of time to figure out your own blog-like thing, or you like the idea of an in-built audience (even if you’ll still need to fight for it). This is also true of AO3 and FF.net to, to a greater or lesser extent. You create a profile, there’s light social media elements, and you can leave comments and reviews on stories, or post your own.

They all look fairly similar, which makes sense: FF.net and FP.com were made by the same people, and AO3 seems to be a more modern take on the same concept. I’m not sure if AO3 was inspired directly or indirectly by the other two, in form or function, but they do wind up giving fairly similar vibes and user experiences.

Here’s some screen grabs. I blurred the text so as to not repost the writing itself without permission.

I’m absolutely not criticizing these sites for having similar sorts of functionality and UIs. It works well, and reinventing the wheel is for idiots (like me). Instead, what I think bothers me about these sites is that they feel like they miss the point for what I want. In the first place, I want my control over the theming of my story. In particular, I prefer a minimalist design when reading (think Medium), but I also want to be able to adjust what I don’t like about whatever pre-baked theme pops out of some web designer’s brain. Blogging and site-builder software typically gives users this ability.

The second thing is that I’m not really interested in the social aspect, or at least this implementation of it. While there are dedicated readers and writers on these platforms, a lot of engagement comes from “trading” reads and comments. You comment me, I comment you. It’s a fine system, and I don’t mind critiquing others’ work or reading it. In fact I enjoy it. But I want people to read my work for the same reason I read theirs; because they want to. Now, any platform can have those gross tit-for-tat undertones, and networking is good in itself, even if you have to engage in a bit of that. But I feel like I could network in a deeper pool on Twitter and write content on my own platform without having to balance that smaller (though more effective short-term) social network baked into the fiction platform.

One service I didn’t mention is WattPad. I think WattPad solves some of the problems of these other sites–particularly, I like the design, the layout, the functionality, and the general reading experience. But it’s social networking aspect is way, way, way more intense. From my own experience and the experience of other authors I’ve talked to, people on WattPad almost never just happen upon your story–you’ve got to go out and network until you’re sick of it, and then network some more, and probably a little more. And then the views will trickle in. It’s honestly a waste of time. It’s possible to make it there, and I’m sure some people have had good experiences; hell the front page shows some who must have. But the time investment for it seems out of whack to me. Whichever group of people wound up writing on WattPad seem to be way more interested in writing than reading, way more so than the other platforms. I can’t really recommend it. Again, I have a small sample size, so who knows? You may have a good experience. But I think the other sites here are better in terms of getting your content into people’s eyes.

Serious Begins

I’ve experimented, to a greater or lesser extent, with all of these platforms over the last decade or so. I don’t really dislike any of them. But as I learned more about web development and similar, I started to wonder, could I create a “blogging” engine for web serials? I knew, thanks to my time experimenting with Hexo (which powers this blog), that such a thing was possible. And my time working with Twine prepared me to understand how exactly a rendering system in a single page web app could be put together.

To start out, the goals of the Serious project were simple:

  • Stories should be separated into episodes. Users should be able to easily see an index of episodes, a list of the most recent episodes, and jump quickly to the first or last episode.
  • Each episode should have comments and the comment thread for each episode should be unique.
  • The generated web app should be simple, easy to read, and require no meaningful input from the author, outside of configuration options.
  • The generated web app should support, at the very least, CSS themes.
  • The author should write their posts in markdown, allowing simple formatting, embedded images, links, and other HTML features with a low barrier to entry for non-programmers.
  • Installing and using Serious should be as simple as possible.

The first thing I needed to do was create an framework that accepted a configuration file and a series of markdown files, and covert those, along with an HTML template, into an app and all the required data. I wrote a script that created an HTML file from a template and a config file, and then created a simple file handler that turned a directory of markdown files into a series of JSON files.

The second stage of development was the most fun for me; generating the web app. I started with marked.js, jQuery, and pure.css as a jumping-off point and wrote Serious’s renderer from there. The renderer parses the URL and determines what exactly it needs to render based on that information; an episode, a meta post, or some sort of episode list (recent episodes in reverse order or the whole episode list in order). In addition to this, I wrote quick sidebar links to take the user to the first episode, the latest episode, the episode list, or the recent episodes list.

Adding Disqus comments wound up being incredibly simple, though at first I made the mistake of assuming it would be complicated in a single page app. Disqus is awesome, and this turned out to be a totally painless process. Implementing Google Analytics was also a snap. After the rendering engine was complete, I hosted it on the awesome jsDelivr CDN to be served to the generated Serious apps.

The next part did prove a challenge, as I’ve never actually made an NPM package before. I had to publish several versions in a row to get things working right, but I learned a lot. I’ve worked with Node enough by now that the errors immediately made sense to me, but each error was hiding another, more critical error behind it. After releasing several versions in a row, I managed to get a stable working version out the door.

The NPM package is a CLI (command-line interface) app called serious. You need Nodejs to use it, but installing it is as simple as typing npm install -g serious-fiction into the console. This gives you access to the CLI; cd over to an empty folder and type in serious init to get started! If you’re interested, learn more here.

I also wrote up a couple themes for Serious, and more are on the way. There aren’t any releases yet, but you can still grab the appropriate theme.css file to use one if you want to.

Is Serious Right for Me?

Serious is probably not a perfect solution for every author, as it will require some know-how or some willingness to learn to actually put it into use for you. For the sake of clarity about who I envision this software is for, here’s a quick run down.

Assuming you’re new to web development, are you willing to learn some programming?

Serious is not meant to be advanced, or complicated, but it does use a lot of programming tools that most professional web developers use to make websites. These tools are built to be fast, effective, and efficient, not easy to use. If you can learn to set the clock on a VCR, you can learn this stuff, but it does take time and willingness. It can be rewarding though, and Serious may be a good way to get started.

Are you looking specifically for a tool to help you make episodic content?

Serious does one thing. It is not for blogs, personal web sites, portfolios, or press kits. There are probably some interesting use-cases for Serious that I haven’t thought of, but it is what it is.

Are you sure you wouldn’t be better served by another platform?

Websites like https://www.fictionpress.com/ and https://archiveofourown.org/, mentioned above, allow authors to make episodic content, too. You should choose Serious instead if you want to create your own site and host it–you don’t want to be boxed into someone else’s platform, or if the concept of building it yourself seems interesting.

Help!

In the near-ish future, I will be writing a complete “author’s” guide to using Serious. This guide will be written specifically for authors who have no experience with programming or the command line. It will cover everything from getting started with Nodejs to writing markdown to deploying your site on GitHub or NeoCities for free, and also a bit about setting up a custom domain.

This will not be a quick write, so if you’re interested in the software but just can’t figure out exactly how to use it, I hope to have something to help soon.

The Future of Serious

There’s a few other things I have in the pipeline for Serious. Some of these may not come to fruition, but I hope to get at least some out there.

  • I would live to try to build an editor for Serious, basically a GUI application that has a rich text editor and compiles your Serious story for you. This is not a small undertaking, and I’d need to make sure the main project is stable and reliable before breaking ground on something like this.
  • A standalone executable will some someday. It will still be a command-line application, but it will come with an installer to set everything up for you and you won’t need Node, though Node with it’s ease of updating will always be the ideal way to use Serious. This will definitely happen, at least for Windows machines, but it will happen after the project stabilizes.
  • More themes are in the pipeline. I feel like we have a few bases covered already, but I’d like to get a few more out there.
  • Swappable templates for the html of the app is a long-term goal, but isn’t currently being worked on. This will allow way bigger UI changes than a theme will allow, and also allow the web app to load it’s scripts locally so authors can edit the rendering engine. Again, this is a long-term goal, no ETA.
  • I think the ability to run custom scripts in the app similar to the way the themes work isn’t a terrible idea. I would need to wait for the project to fully stabilize and have a reliable API, and I’d need to write API docs though. Whether this happens or not will probably be based on if it’s requested. It’s possible, but not low-hanging fruit.

In Other News

Some other news from my world:

This has already been a busy year for me! Once Serious stabilizes, I’ll probably take a break from my open source projects to get back to creating my own things, particularly my games. I also plan to start a web serial, but it may not be released under the “Chapel” pseudonym. Of course, seeing someone using Serious may tip you off if no one else likes the app, so we’ll see.

Poof v1.0.0 Release Notes

They grow up so fast.

As mentioned in the notes for the last release, I’ve moved poof’s distribution to a CDN to avoid using the bandwidth from my personal site to host it. This has made the URL a little less sexy. For now, I’ve kept the old URL functional, but in a few weeks I’ll deactivate it, so be ready for that.

This release is a bit boring, as there’s no real changes anywhere, other than a migration to Gulp v4 to get rid of an annoying security warning from NPM.

Other than that, the move, and the version bump, this is poof 0.5.1 in every other way.

In future releases:

Poof is going into maintenance mode for the foreseeable future, so expect mostly bug fixes and boring releases for a while, with little in the way of new features or excitement.

Holy Land: Second Coming

It’s finally time to talk about Holy Land again. The game feels like it’s been in development hell, almost, a weird issue for a personal project. The issues facing its development have to some extent been author-imposed, but to a greater extent were a result of my inexperience as a developer, and the soul-crushing realization that the game needed a page-one (byte-one?) rewrite.

But that rewrite has been going on for a year (though not full-time, as I’ve been working on other smaller projects and Deep Dive) and the project has finally started to take shape. This is the first time I’ve been able to actually play the new version of the game, and it was a rewarding step, and one that’s left me wanting to discuss it.

Before we get into that, though, a caveat: Holy Land is a ways off. As I’ve transitioned from development of Deep Dive to content writing, I’ve spent more and more time on development for Holy Land, but that development work is not all that close to completion. It will still probably come out sometime after Deep Dive, and I’m not attaching a release date or window to that project yet. But I do have a few things I want to share with you about the nature of the new version of Holy Land. First, though, let’s talk about that rewrite.

Spaghetti

Holy Land (the original version) started development in early November 2016, in Twine 1, SugarCube 2. At that time I was a novice with Twine, web development, knew no JavaScript, and was generally ill-equipped to program a game of this complexity. While the game and its systems obviously worked, they were poorly implemented. The biggest issue was my own growth. I became a way better programmer as I went, and trying to socket better code onto a rickety code base was quickly becoming a mess. Essentially, this version of the game became playable very quickly; as early as late December, the game’s combat systems and UI were usable, and a prototype was released in late January. I don’t want to undersell the prototype, because I think it was impressive from the end-user side. From the development side, though, I might as well have been using line numbers.

As I bolted more and more code onto it, the game started to buckle under its own weight. It became a sunk-cost fallacy for me. The game worked even if updating something as simple as the length of status effects took ten lines of code across various passages, widgets, and story files. I was constantly convincing myself that the finish line was the most important thing–no one would care that the code was garbage. Of course no one would care, so I was half right. But adding newer, better written features (my custom macro set first entered the world as a series of subsystems for Holy Land) became a chore–I’d have to remember where to go and what to change every time I improved something.

It was when Tweego 1.0.0 (or so) released and I started messing with the program that I realized I wanted to move development to Tweego. Twine 1 has issues compiling large stories, and while I managed to work around some of them, by this point the writing was on the wall. Exporting the Twine 1 files to Twee source confirmed what I knew all along, I couldn’t work with this project anymore. Seeing my source code laid bare in a text file reinforced the idea that the game had become an unworkable mess. I knew I had to rewrite the code, so I started by trying to pull out and separate the systems into self-enclosed modules that could be worked on independently. This became such a rat’s nest that I ultimately had no choice but to give up.

In November 2017, development of this version of Holy Land completely ceased. I later grew concerned that no part of the game would ever release, and so I released a “second prototype” in March the next year. There were good concepts and ideas in the game, and I wanted to make sure something of its year-long development saw the light of day. By this point, I wasn’t concerned about a bad release “tarnishing my brand”, something I initially considered could be an issue when development stopped. I just wanted something of it to survive in case I became unable to restart it and get it to a proper release.

Reinventing the Wheel

I was still getting better pretty rapidly at programming, and it occurred to me that starting over immediately wasn’t an ideal use of my time. I threw myself into working on Deep Dive, a project I started for the Twine discord server’s game jam which rapidly grew into something large and unfit for that event, and continued answering questions across a variety of Twine communities, moderating many of those communities, and developing add-ons, tools, and other software for the community.

I conceived of a complete, bespoke “engine” for Holy Land in mid-2017, and started working on it on and off starting in late 2017 or early 2018. The idea was fairly simple. The game needed every system to be a complete, self-contained module that could be imported into a generic “template” that would hold these systems together and bind them to data that held all the game’s characters, enemies, gear, spells, etc. This would also allow me to release Holy Land as an episodic game (my file system macro set is designed to allow save-importing across episodes, and was created for this engine). I call it the HLE (Holy Land Engine, yes I’m not all that creative when I don’t have to be).

For the interested, the engine contains the following components:

  • Fractal/Fracas: This is the game’s combat implementation. If any part of the engine is ever released for other authors to use, this will be it.
  • Pressure: Named for my old SugarCube stylesheet set, this is a library of UI components for the game.
  • CharacterCore: This system takes raw character data and creates SugarCube serialize-able/save-able custom objects with all sorts of goodies.
  • Noteworthy: A side-quest and journal implementation.
  • Quartermaster: An equipment system.

In addition to these major systems, a number of smaller systems also deserve some mention: a traits system, use-based leveling that compliments experience-based leveling, a personality system for the protagonist that allows them to subtly change in reaction to player choices, and even some surprises I’ll keep close to my chest for now.

In short, the HLE is in a good place. The second version of the game itself doesn’t really exist as such, and I plan to take my time with it. But having an “engine,” though incomplete, that is able to spit out functional, fun (if I do say so myself) combat encounters really, really got me excited to spend more time on this game. These small-ish victories are all that keeps creators going when undertaking massive, long-term projects like this.

What’s New?

The second version of the game is very similar to the first, but with a few things simplified and a few others expanded. Something I’ve always wanted to achieve is a sort of load-out-based RPG, where builds and gear are the focus over moment-to-moment strategy. Still, I think the combat in the original version didn’t have enough flair. For this reason, I decided to add a few new bells and whistles. Chief among these is cast-able spells with a cooldown system, a revamped attack system (no more mashing, combos are still possible but work a bit differently) and more dangerous enemies that have more abilities in common with players.

While I’m excited to share more, it’s hard to really explain something like this without letting you play it, and I’d prefer to let the game speak for itself someday through a prototype. The biggest change, though, I think, is the episodic structure. Holy Land will release in chapters now rather than as a single lengthy experience. I also may try to monetize the game in some way, though I haven’t made decisions regarding that quite yet. It won’t be ads, though.

Yet a Ways Off

Don’t expect Holy Land in any reasonable amount of time. At best you can expect some closed-beta prototyping on the Discord server, and maybe a bit of showing off via a short demo sometime next year. I can’t stress enough that this is more of a sharing my excitement sort of post than an announcement, so keep that in mind.

Project Round Up

The year is ending, so here’s the status of some of my other projects right now and what you can expect nest year:

  • poof: My comment-based proofing story format is close to a full release. Once it hits 1.0.0 in a few weeks, it’ll leave active development and enter maintenance until I have further ideas for it.
  • HAL: My Harlowe Audio Library is currently in maintenance. I’m happy with how it turned out, but I will admit that it isn’t seeing the amount of use I was expecting. Harlowe users seem to kind of hate it. I think it’s just scary in certain ways–it feels too JavaScript-y, perhaps, or like there’s just too much there and it’s too complicated, and even a metric ton of documentation doesn’t seem to help much. In short, those who have managed to use it seem to think it’s at least good enough to keep using, but many authors don’t seem to even try it. A second major version will attempt to address this someday, but for now, I think it’s like medicine: it’s good for you, but a spoonful of sugar could help.
  • Custom Macros: In spite of there being no reason to be rushing out versions of this macro set, I still feel like I’m eternally behind with it. Expect new macros, more guides, a better demo, and improved docs throughout next year.
  • Noble Avatars: Sigh. There’s lots of promise here, but I was hoping to get some additional assets for avatars to be able to really make this a thing. In my brain, I imagined Twine users who were good artists would be willing to provide more asset sets in a variety of styles, but it turns out that working for free isn’t a very tempting prospect. I don’t blame people for not wanting to do it, but it does sort of make dedicating my time to a dead-end project like this a hard sell. What’s here works, but I can’t justify finishing it and adding all the polish I wanted for it if there just isn’t much interest from artists in supporting it. And no, I can’t and won’t pay to generate art for an open-source project designed to help authors. I’ll pay for my own art, but that’s about it.
  • Howler for Harlowe: I will begrudgingly fix things when absolutely necessary.
  • Tweego Setup: I have a few updates planned for this repository, but it’s all small, and not very exciting stuff. Nothing that will dramatically change the way you use it or what it does, just a few fixes and some clean up.
  • An Unreleased Project: I’m making another piece of software that is unrelated to Twine that I’ll probably talk about here. It’s for writers wanting to create a certain kind of web content, and I don’t expect it to be that big a deal, but it’s something I want for myself, so I may as well release it in case anyone else wants something similar.
  • TwineLab: TwineLab.net itself will be going through a few changes next year too, and I’m considering adding Google analytics to it as well, though mostly to get used to using it rather than because I need the data. I’m also considering losing some of the ko-fi links I jammed everywhere, as it feels a bit tacky to me, and having those links everywhere has resoundingly not increased my income there (I am fortunate enough to not really need the money; I do all this Twine-stuff for fun on the side, so it’s not really a major issue, but I figured it’d be interesting to see what kind of support I could get, as people have asked about donating before). Something about asking for money for my projects here feels dirty to me anyhow, not exactly sure why, so I may end the ko-fi thing entirely.

Happy Holidays

If you read this blog, thanks for doing so! I hope your holidays are wonderful. I’m getting all this stuff out and off my chest now because I don’t plan on doing much blogging until next year. I’ll no doubt have more things to discuss then.

Poof v0.5.0 Release Notes

Poof 0.5.0 is here, and poof is now considered feature-complete.

This update adds the following things:

Optional, toggle-able line numbers have been added to all source code output, including passage text, JavaScript, and CSS code. This setting is on by default, but can be toggled in the view menu or defaulted to being off in the poof.config special passage via the setting "lineNumbers". Line numbers can appear in the PDF export based on this setting as well.

The linting feature for JavaScript now reports warnings, but has also been made “format aware”, so global story format variables are not warned about. This should help authors catch misspelled variable names and other issues. Your intended build format can be overridden via poof.config; this is particularly important for CLI compilers like Tweego. You can also greenlight an array of global variables for the linter.

The source code of the project has been refactored. The entire codebase is now faster, more efficient and less redundant.

The build process of the format is now cleaner, faster, and more user-friendly, meaning forks and rebuilds of poof are easier than ever. Developer documentation has also been added.

In future v0.5.x builds:

I will mostly be monitoring for bugs. Some clean-up and internal improvements will be made.

In later versions:

Poof is feature complete to my specifications and desires. Feel free to make additional feature requests, though. God willing, the next version will be a stable v1.0.0 release. This rollout will probably see poof moved to a proper CDN, and only development versions will be hosted via TwineLab, to help save me some bandwidth on my personal site.

Poof v0.4.0 Release Notes

Poof 0.4.0 will add a few bells and whistles to your JavaScript and CSS views.

First, syntax highlighting powered by highlight.js has been added to both views. Passage source code doesn’t (and likely won’t ever) get syntax highlighting, though.

Second, a shiny new “lint” button has been added to the JavaScript view. This button uses a client-side, CDN-hosted version of JSHint to help you debug your JavaScript code. It requires a web connection on at least the first use, though the script should cache itself for future offline usage.

This version also includes a critical bug fix for compatibility with Tweego, which expects formats to be valid JSON, not just JavaScript (which is totally fair, mind you). Currently, the v0.3.x format files return a valid JavaScript object that is not valid JSON, which was unwise on my part and only implemented to save a few bytes.

In future 0.4.x versions:

A few patches will be released over the next few days/weeks to make the linter story format-aware to prevent false positives with certain code.

As always, I intend to keep on top of any bugs or weirdness, too.

In later versions:

Depending on the reaction to the linting feature, a similar CSS validation feature may arrive in a future version, though validating CSS in the client-side may prove enough of a challenge that it may get pushed off to a later version or abandoned. JavaScript linting is a much bigger deal anyway, in my opinion.

The PDF export option may get off-loaded to a CDN like the linter, meaning it will work as a cache-able script rather than being built in. This will massively reduce the size of poof, but may not be worth it based on how often this feature is used.

At some point in the future, poof will go through a mild refactor of its codebase and build process to make it easier to work with, particularly for other devs. This will happen sometime prior to the full 1.0.0 release.

I’m considering reworking the codebase to use node modules and webpack (or similar) as well, but such a refactor would increase the format’s size by a not insignificant margin, and only mildly help devs work on it, so I’m not convinced this is the right way to go.

What Is Poof?

I’m a writer by trade. While I dabble in Twine, programming, and web development, writing is my job and, largely, my passion. For me, Twine is an attractive platform for development not just because of its relative simplicity, but also because of its focus on writing and its output to supremely shareable HTML files. But I think that Twine also has a problem, or blind spot at least, when it comes to a good writing, editing, and rewriting workflow. It’s just not all that easy to iterate on your prose the same way you can iterate on your design, mechanics, and code.

Maybe I’ve been spoiled by technology, but I want to be able to draft, fix, update, and draft more. I want to be able to work on my prose and mark it up with a pen, away from the harsh blue light of my computer screen. I want to be able to write notes about changes I want to make in between editing sessions. I want to be able to share my drafts with my friends for feedback, and get their comments and corrections.

So I made a proofing format called poof.

The Old Ways

I am involved pretty heavily in the Twine community, and most of the Twine authors I work with or help out know I’m a writer. They’re also often writers themselves. When I’m asked to critique one of their stories, they’re often looking for more than just impressions, and I want to give more, too. I want to be able to point to a sentence, word, phrase, and say, “hey, I tripped on this.”

What I typically do is hack the JavaScript a bit via the console to report to me the passage title on every new passage. It’s easier to do in SugarCube, but also possible in Harlowe. Then I open a Google doc, write the passage title in, copy and paste some problem text over, and make a comment in the Google doc, writing my thoughts there.

It’s laborious, boring. While in one sense it gives me a moment to think through my reaction, it also makes the process unappealing in general.

It was this dissatisfaction that first have rise to a desire for a proofing format designed to help me make my own critiquing / editing workflow simpler, faster, and more fun. Making a story format, though, isn’t really something you do in an evening, even for one as simple at its core as poof. But I assumed I could make one, if I was so inclined. Regardless, the idea, while there, wasn’t really formed.

The Problem of Paperthin

Paperthin is a format with a clever name and almost nothing happening under the hood. And that’s not a bad thing, per se. The amount of complexity required from a proofing format is directly correlated to what you expect it to do. Paperthin’s job is to put your passage text together into a single doc for viewing. Nothing more or less.

In my opinion, the critical flaw of Paperthin is the unwise (in my opinion) use of CSS pseudo-elements, specifically, the ::before pseudo-element. Paperthin renders each passage’s source code with a ::before holding the passage title. In CSS, pseudo-elements are not considered to be part of the document proper. They’re intended to be used as embellishments, icons, etc; things that don’t contribute to the document’s meaning or formatting. When you copy/paste or print the document, these pseudo-elements are omitted. By using these ::before pseudo-elements, Paperthin hamstrings its own usefulness. It becomes only a simple, readable copy that you can’t do anything else with.

So as an editing tool, importing a game into Twine a pulling up the proofing copy in Paperthin is less helpful overall than I feel it could be. And Paperthin has no JavaScript or commenting or editing, so as a format for critiquing or iterating on a draft, you’re just wasting time. It’s still easier to hack out the passage name in the console and use a Google doc for the heavy lifting.

Decompiling to Twee

There are many ways, including utility formats and third party compilers, to get a story into Twee notation, a plain text format designed for viewing and editing in a text editor. This is a useful feature, and one that wound up being included in poof, but more on my format later.

Many text editors come with spellcheck and other tools that can help you iterate on your prose, but I also think that as a critiquing or editing solution, there are some faults here, the biggest of them being the slowness of implementation.

Let’s talk about the other side of a critique or editing pass for a moment, the part where the original author (who may also be you) goes through the editing notes and weighs and implements the changes. Your editor (who, again, may also be you) just sent you a text dump of your story. Whether they’ve edited directly in the source or added notes, you now need to find every change or suggestion, review it, and decide what to do with it. You’ve been given a haystack full of needles.

While the editing pass was easy, the actual iteration becomes a slog. If you’re doing your own editing, this is less a problem, but only slightly, and that also assumes that your editing pass consists of fixes, not notes, to-do lists, etc. Mine usually consist of both, and I doubt I’m alone in that.

Any effective editing solution requires two major components. First, changes, notes, and edits must be easy to make; second, changes, notes, and edits must be easy to find, review, and implement.

Yeah, Sure, but Illume

Illume is a proofing format that allows authors to export their edits and changes as diffs, and is frankly an incredible solution. Poof is not really intended to be a replacement for Illume, as what Illume does is pretty great.

If you’re unfamiliar, you can use this link to install and check out Illume: https://www.maximumverbosity.net/twine/Illume/format.js.

Illume allows you to edit passages using a built-in editor, flag them as reviewed, and move on. You can export HTML files with your diffs, and use these files as a guide to your reviewing and editing process. Like poof, Illume also allows for a great deal of export options, and has ignore tags.

Whether you choose to use Illume or poof will likely come down to preference. I have nothing but nice things to say about Illume, but I did develop poof anyhow, and for three main reasons:

  1. I think importable / exportable comments are a better solution for editing another author’s work. I think Illume is a superior format for iterating on your own prose, but I think being able to import comments from files makes it easier to work in larger teams, with more people, etc. This isn’t to say you can’t do this in Illume, only that I feel, personally, that being able to collect comments from multiple sources into a single poof view makes poof ideal for this specific use-case.
  2. I think poof offers a different (though not necessarily better) toolset. While you can organize, find, and, filter passages with Illume, the focus of Illume is overwhelmingly on moving each passage from the “needs reviewed” list to the “reviewed” list. Poof has a similar filtering option; you can filter passages by whether they have comments or don’t, meaning after you’re done reviewing a passage, you can throw a comment on it that just says “done” or something, even if you don’t think the passage needs a change.
  3. In a similar vein to both of these, I think poof is just less opinionated than Illume. It makes fewer assumptions about how you want to edit, who you are in relation to the project you’re editing, and how you want to spend your editing time. This means that poof has some benefits for some people compared to Illume, but also pales in comparison in other areas. Being less opinionated means that poof is less specialized and more generic.

What proofing format is right for you isn’t a call I can make, and while I think that Illume is incredible, I also designed poof because it more closely reflects my own desires and editing habits. If you edit like me–that it with notes and comments first, then rewrites, then more notes, then more rewrites, poof will probably fit your habits. If you prefer diving into edits right away, Illume will give you more bang for your buck. I think in either case, managing multiple comment sets is easier than managing multiple diff files, so I think if you’re working in a team, or sending your comments to another author, poof is worth trying.

Iterating on Prose

From this point on, we’re going to focus on poof, and some ways I think you can use it to help iterate on your prose. We’re not going to talk about Paperthin or Illume anymore; compare these proofing formats yourself and reach your own conclusions based on your needs.

Before we get too far ahead, let me clarify a term I’ve been using. They sound jargon-y, and I think I want to make sure we’re on the same page. So what do I mean by “iterating on your prose” anyway.

When I write, I waste no time concerning myself with quality on a first draft. To me, drafting is about words on a page, not perfection. It’s about the finish line, and generating something that can be refined later. Even if you don’t do this, your first draft will be garbage. It needs to be iterated on.

Iteration is the process of taking something, using it as a blueprint, and building something better with it. Your drafts are not your story, but a guide to what your story will ultimately become, through a process of editing, taking notes, and rewriting: iteration. Your prose will never be perfect. Instead, the process of iterating will become less effective until it is no longer worth the time.

This is how I approach writing, and how most of the writers I know do as well, though some to a lesser or greater extent. You don’t need to write like this to use poof or get something out of it, but I think it helps to understand my process to understand why poof is designed the way it is.

Comments

In poof, comments are the primary way you’ll interact with your prose. You can click the pencil icon to create a comment that contains all the passage text of the indicated passage to make edits, or create a new comment to leave notes, ideas, and suggestions. These comments are portable, meaning you can create a bunch of comments, then export a comment file to share with another poof-using person. This person can then import your comments into their poof view, and your comments will be added to any comments that are already there. This means you can use comments from a variety of collaborators, editors, or friends and see them all in one view, and delete them as you address them.

You can filter out passages that have or don’t have comments to help keep you on track. You can also search through and sort passages by a variety of metrics, so you can easily be playing a Twine game in one window, and commenting up those passages in poof in another, without having to hack the game to find the passage title: just filter by passage text.

Comments aren’t a perfect solution to everything: sometimes you just need a diff view like one powered by Illume, so you can create and export your edits. But as a general use way of taking note, recording reactions, and suggesting changes or edits, comments work pretty well, in my opinion.

Other Features

Finding and commenting on passages is probably the best use of poof, but a number of other useful features are included. Here’s a quick breakdown of what else you have at your fingertips:

  • Export to PDF format to print and share. PDF format renders a very shareable copy of your passage content, and one that won’t look crappy if you want a physical copy to mark up with a pen.
  • Export to Twee notation. As mentioned, poof can export to Twee, so you don’t need to get a format or compiler just for that.
  • Export a Twine archive. Twine’s archive option archives all of your stories. You can use this option to generate a much, much smaller archive to create a backup of youor work.
  • View modes. Poof comes with a variety of viewing options, including a simplified mode, a night mode, and more, all designed to help you get comfy and settle in for some reading.
  • You can use a special passage to configure poof’s default settings, fonts, etc, and can tag certain passages to be ignores by poof.
  • You can also view your JavaScript and CSS code with poof.

A Poof-Powered Editing Workflow

As a sort of sign-off, here’s some ideas I had while building poof. When you develop anything as complex as this, it helps to think about what you want and expect users to do with it. As I worked on poof, I thought about how I would want to use it, and how it could be used. Here’s a few examples of how you might build an editing or critiquing workflow using poof based on those design ideas.

Example 1:

You’re playing a game a fellow Twine author asked you to critique, and as you go you have comments, suggestions, and ideas. You import the game into your preferred compiler and open it in poof. As you play, you search for and find the passages you want with poof’s finding and filtering tools and make comments, then export the comment file and send it to the author.

Example 2:

You’re making a game with another dev, either as an editor or sharing the writing load, and need a quick way to share ideas and comments. You can “trade” comment files to see each other’s thoughts and make plans. While this is really no alternative to version control, opening issues on a repo is nowhere near as effective as a means for sharing writing ideas.

Example 3:

You want your non-Twine-using friend to give you feedback on your writing for a Twine game. Assuming they sufficiently understand the concepts of CYOA-style IF and passages, you could create a PDF export and send it to them. They can then import this file into Google Drive and create comments, or print it and mark it up.

The Future

Poof is still in a pre-release state, currently at version 0.3.2. This means poof isn’t done by a long shot. I considered adding diffs similar to Illume, but there’s little chance such an addition will ever be as fully-featured and well-designed as Illume: it will always be an after-thought at this point. While poof is largely feature-complete, there’s still a few bells and whistles and adjustments to be made, and if there’s something you want to see in a future version, raise an issue at the repo.

Note that poof isn’t going to do a complete 180 at this point. Future development on poof is unlikely to change the direction of the project, so if you’re not a fan or prefer another proofing format, there’s a pretty low chance that poof is going to set your world on fire in a later version. But I am open to hearing what you think it could do better anyhow, if you’ve got the time to talk.

Overall, I’m pretty pleased with how this project turned out, but there’s a lot of room for improvement, especially internally. Now that version 0.3.2 is up and most major bugs have been fixed and patched, I’ll probably be taking a week or two off before diving into the 0.4.0 verison, though I will certainly patch bugs if needed.

Pronouns and Game Settings

Options Are Always Best

Here’s a bit of wisdom I sort of stumbled on all by myself that you can use to make your game better: if you can make something into an option or setting, you should. If you want to, for example, have quick time events, give the user a way to shut them off. You may feel they’re integral to your game, and they really might be, but at the end of the day, all it takes is a not recommended next to the switch to tell users they risk having a subpar experience while still letting users with, say, disabilities a chance to at least play your game.

I think the best way to handle pronouns is similar: inconvenience no one, but throw in as much configuration as you can. I don’t need to worry about this for Holy Land, since the player takes the role of static characters, but I was faced with the issue in Deep Dive. I solved it by creating a completely configurable system with “presets” for common pronoun sets, and it wound up looking like this.

alt text

The scripting isn’t short, but it is fairly simple:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
(function () {
var gender = 0, genderList = ['she/her', 'he/him', 'they/them'],
pronouns = { // pronoun presets
sub : ['she', 'he', 'they'],
obj : ['her', 'him', 'them'],
posA : ['her', 'his', 'their'],
pos : ['hers', 'his', 'theirs'],
ref : ['herself', 'himself', 'themselves'],
plur : [false, false, true],
pers : ['woman', 'man', 'person']
};

//dropdown event
$(document).on('change', '#dropdown-gender', function () {
var value = $(this).val(),
sv = State.variables, i,
vals = ['sub', 'obj', 'posA', 'pos', 'ref', 'plur', 'pers'];
$(document).trigger({
'type' : ':gender-drop',
'value' : value,
});
for (i = 0; i < vals.length; i++) {
delete sv[vals[i]];
}

sv.profile.gender = genderList.indexOf(sv.gender);
$('#pronoun-edit').empty().wiki(Story.get('profile-set-pronouns').text);
});

// send to setup
setup.gender = {
selected : gender,
list : genderList,
pronouns : pronouns,
user : false // did user set custom pronouns?
};
}());

And the TwineScript holding it all together:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
:: profile-create2 [creation startup]
/% gender and pronouns %/\
<center>
''Select your character's gender and pronouns:''

<div id='pronoun-edit'>\
<<include 'profile-set-pronouns'>>\
</div>

@@#enter-link;.big-link;<<button 'Continue' 'profile-create3'>>
<<if $sub>>
<<set $profile.pro to {
sub : $sub,
obj : $obj,
posA : $posA,
pos : $pos,
ref : $ref,
plur : $plur,
pers : $pers
}>>
<<else>>
<<set $profile.pro to {
sub : setup.gender.pronouns.sub[$profile.gender],
obj : setup.gender.pronouns.obj[$profile.gender],
posA : setup.gender.pronouns.posA[$profile.gender],
pos : setup.gender.pronouns.pos[$profile.gender],
ref : setup.gender.pronouns.ref[$profile.gender],
plur : setup.gender.pronouns.plur[$profile.gender],
pers : setup.gender.pronouns.pers[$profile.gender]
}>>
<</if>>
<<unset $sub, $obj, $posA, $pos, $ref, $plur, $pers>>
<</button>>@@\
</center>

:: profile-set-pronouns [include creation]
/% set custom pronouns; text inputs %/\
|Presets|//Select a preset.//|<<dropdown '$gender' setup.gender.list>>|
|Subjective |//''She/he/they'' went home.//|<<textbox '$sub' `($sub || setup.gender.pronouns.sub[$profile.gender])`>>|
|Objective |//I like ''her/him/them''.//|<<textbox '$obj' `($obj || setup.gender.pronouns.obj[$profile.gender])`>>|
|Possessive (Adj)|//This is ''her/his/their'' house.//|<<textbox '$posA' `($posA || setup.gender.pronouns.posA[$profile.gender])`>>|
|Possessive|//This house is ''hers/his/theirs''.//|<<textbox '$pos' `($pos || setup.gender.pronouns.pos[$profile.gender])`>>|
|Reflexive|//She/he/they keeps/keep to ''herself/himself/themselves''.//|<<textbox '$ref' `($ref || setup.gender.pronouns.ref[$profile.gender])`>>|
|Noun|//She/he/they is/are a ''woman/man/person''.//|<<textbox '$pers' `($pers || setup.gender.pronouns.pers[$profile.gender])`>>|
|Plural|//She/he/they ''is/are'' here.//|<<if $plur || setup.gender.pronouns.plur[$profile.gender]>><<checkbox '$plur' false true checked>><<else>><<checkbox '$plur' false true>><</if>>|

The Fine Line

I’m cisgender myself, so I tend to always want to think of male and female as “normal”. It’s a view that, while I won’t make excuses for, is ingrained in our culture in ways that make it hard to be mindful of. I think that designers need to be mindful of their own particular biases and presuppositions. It’s not enough to just try not to be a racist, for example. Racism is socially and culturally embedded. The magical black janitor of wisdom is as racist a caricature as black face. If we ever feel that we aren’t influenced by a particular cultural bias, then we must be even more vigilant, because we’re either not affected by it, as we suppose, or we’re affected so much that it feels like a natural state.

In short, not thinking you’re sexist won’t do you much good when you’ve penned yet another manic pixie dream girl whose only purpose is wish-fulfillment, even if you did it with the best of intentions. In this era, not being at least overtly racist or sexist is by some people considered a virtue, but this is like comparing yourself to a child to feel strong.

But this is really beside the point. The point I want to make is that I’m cisgender, and I know in my heart that I think of being cisgender as some sort of “default”. That’s not something I feel great about, but as long as I’m aware of my bias, I can work against it and maybe even change it someday. And I know that when I go about making something, anything, I need to be wary of myself above all else.

The biggest question is always: when does my desire to be inclusive start warping into tokenism or patronization? The answer is not something you’ll find without help.

So when you’re putting together something like this, ask your transgender friends. They’ll help you find a balance. If you don’t have any (or at least aren’t sure if you do), or if you don’t feel comfortable asking them, or if you just want more opinions, try r/asktransgender on reddit.

Remember, you aren’t being heard right now; that comes later, when your piece is published. For now, listen.

Pushback

Sometimes inclusiveness has an actual cost. If you’re recording 1000s of lines of dialog like you’re making a BioWare game, then flexible pronouns will become a pipe dream fast.

In other situations, a game may require gender-based mechanics that just don’t lend themselves well to non-binary gender representation. Take Mount & Blade, where playing a female character makes for a more challenging and interesting experience. Should being transgender make it even harder? Easier if you’re identifying as male? Transgender and intersex people have always existed, but implementing them in this sort of historically sexist context quickly becomes a challenge from a design perspective.

It’s also important to remember that the whole goal of creating a more open-ended pronoun system isn’t for political correctness, or at least it isn’t just about that. Likewise, few players will outright refuse to play a game solely based on whether you allow them to represent themselves well in it, so it’s not really about pandering either. Instead, the goal of something like this is to aid your game, deepen immersion, and allow players to have some level of control over their experience in your world. In a medieval game, for example, pronouns like ‘xe’ may help some players feel more immersed, but for others, even ones who use those pronouns, it can feel forced and immersion-breaking to hear something so modern pop out of a 13th century king’s mouth. As they say, there’s no accounting for taste. But you can let the player make that choice.

There’s also another elephant in the room, particularly when it comes to games. Some people take issue with increased inclusivity. As with the above, sometimes the reasons are justified, at least somewhat. Tokenism doesn’t do much for anyone, and a lot of games are more concerned about scoring points (pun very much intended) with a certain demographic than they are actual representation. That’s how Hollywood wound up with all those magical wise black men. Actual representation, contrary to popular thought, feels real, like it always was meant to be there. That’s unlikely to score points with anyone. It just feels natural, right. But it also it also serves your story and your audience by painting a world that fits itself together and fills in all of its own cracks. It presents your audience with a version of reality they can map to real life and actual experience.

In most cases, when you do it right, most people won’t realize you’re doing anything at all. Sounds awful, right? All that work, just to be invisible. And yet for the author or designer, invisible is the goal. The hand of the author moving the pieces about, pulling the strings, making the marionettes dance, if they can see it all, you’ve failed.

But I digress, this is becoming more of a general inclusivity discussion than one arguing the merits of pronoun configuration.

Every Desicision Is an Argument

This post isn’t titled “Pronouns”, its “Pronouns and Game Settings”. So let’s back up a bit. I mentioned already that in Holy Land, the player controls static characters that already exists. So I guess a certain reading of my above arguments could cause some readers to arrive at the conclusion that I’m a hypocrite. After all, if giving players options is good, then certainly not letting them make their own characters is bad, or at least less good. After all, with a few lines of code, I could replace these static characters with variables defined by the player, right? The answer is that yes, I could. In fact, the only reason Holy Land doesn’t allow character creation is because I made it that way. There is a lot of character customization, so adding a whole creation feature would not be a bridge too far.

Authors have control over their work, not the other way around. Some people, even creators, seem to hold fast to the ridiculous idea that they need to be true to their inspiration or goals at the expense of almost anything else, like some other-worldy muse-like being planted ideas in their head and their greatest duty in life is to create what has been given to them with the utmost precision. George R.R. Martin writes a lot of rapes into A Song of Ice and Fire. The excellent TV show Orphan Black features a murder scene late in the series that is shockingly and unnecessarily brutal. And so on. If you have misgivings, you’re a prude or, even worse, trying to censor everyone and kill art, you damned moral guardian. But the truth is that the creators are responsible for every decision they make, and every line of dialog, every design choice, every sentence is an argument for its own existence. Sometimes these are good arguments, sometimes they’re not as good. Sometimes, we just don’t buy it. If you’ve ever watched a movie or read a book and feel like something just didn’t work or didn’t belong, then you’ve encountered one of these bad arguments; they take you out of the experience. You start thinking about the author or the produciton team or the culture surrounding the work instead of the characters and world of that work.

You can sacrifice a lot of things on the altar of player agency. You can also sacrifice a lot of things in the name of authorial intent or artistic license. Dark Souls doesn’t have an easy mode because it was sacrificed on the latter. Skyrim‘s story is a bit weak because it was sacrificed on the former. Your job is to make those sacrifices. The important thing is whether the sacrifice justifies itself, and there’s no clean way to measure that; in most cases, different people will have different opinions.

So if you can’t tell the story you need to tell with player-created characters, you’re going to need to hope, like I do with Holy Land, that most players will be swayed by the arguments presented.

Everyone Hates Quick Time Events

For this last little tidbit before I disappear to continue working on Holy Land‘s combat engine, let’s talk about player agency and settings one more time, and the fear that many authors, myself to some extent included, have about them. It’s that options for the player always lead to a direct loss of author control.

Let’s look at quick time events again to illustrate this, and we’ll say that your game has a few tense sequences that use them. For you, as the author, these quick time events create tension; they make the scene work and feel exhilarating. But you know that if there’s an option to shut them off, many players will do so before even giving them a chance, because these mechanics are often frustrating to some players. So you have a choice: give an option and risk the entire scene, maybe the entire game, falling flat, or don’t give an option and risk having some players be unable or unwilling to play.

In this case, you need to think about a few things before you decide.

  1. If the whole scene feels less tense without the quick time events, maybe you’re using them as a crutch. Maybe the real problem is that the scene needs to be improved.

  2. It’s tempting to think of yourself or your game as the exception to any given rule, but most players just won’t see it that way, no matter how good a job you’ve done. We know that quick time events are generally unpopular, and we know that the reason is because they’re usually set up like binary choices, but everyone knows which one is the “right” choice, eliminating any real agency. On top of that, they’re frustrating when they lead directly to a game over. I would recommend making sure that your quick time events, at least, really are exceptional. If you’ve done it right, they will improve your game. Players may still not give them a chance, but at least those who do will be pleasantly surprised.

  3. Is it really worth making this decision for the player? I have a few disabled friends who like to game, and for most of them, the quick time event isn’t just annoying or unpopular, it’s impossible (especially those of the “mashing” variety). Quick time events could be cut from most games without altering the overall gameplay, feel, or appeal, and would instantly improve accessibility, letting even more players play. Accessiblity is always worth the work in my opinion, and in this case, worth the risk of a few players having a lesser experience because they wanted to faff about in the options menu before starting.

Players know what they need from a game better than you do. Letting them make those choices is usually going to improve your game.

Back to the Grind

As always, take my two cents here as just that. No one knows your game and what it needs better than you do, and there are no design decisions that will always be right for every project. As for me, I think that letting people play on their own terms to the extent possible without sacrificing your game’s identity is a balance to strive for, even if it’s usually hard to achieve perfectly.

I want to make a few quick notes about this blog post, and what I’ll be writing in the future. Writing a tightly edited, pointed post is usually ideal, but writing in a more stream-of-consiousness style, moving between points on related issues, and taking a few detours here and there helps me write faster and is also more interesting to me as a means of unloading my thoughts about a topic. This post is sort of a good case in point: it’s a bit rambly and long, and I do occasionally get into tangents or make generalizations. But I find this sort of post easier, faster, and generally more entertaining to write. In the future, I want to try to capture things I’ve been thinking about and working on in this way.

My hope is that this will make for somewhat interesting reading, too, but my primary goal will always be to write about something I want to talk about and get off my chest. I love a good argument or debate, too, so if I’m totally wrong about anything, feel free to let me know (in a respectful manner).

Thanks for reading.

Welcome to TwineLab!

Hello world!

TwineLab.net is going to be my new home on the web. I also decided to move my blog off of the blogger platform and onto Hexo, which has been an interesting journey, to say the least. Very easy to mess up, but also easier to configure, even if I haven’t done a lot of configuration just yet.

I plan to try to write more blog posts going forward, but it is a hard sell. I write for a living, and under a different name, so any time I spend blogging is time I spend both not working on my own projects, and not getting paid. But blogging is good for the soul, and I feel like I lead a bit of a double life anymore; my Twine and non-Twine work seldom ever interact; people who want me for non-Twine projects don’t want to hear about Twine and vice-versa. But Twine has become a part of my life in a fairly large way, and it seems wise to give that part of my life more space to vent.

So here we are. TwineLab is going to be dedicated to my Twine adventures. I may occasionally cross over between my Twine and non-Twine work in the future and post about those things here, but for now, this seems the best way to scream into the void.

There is one important caveat here, though. All of TwineLab is client-side, with no server or backend. That means no comments. If you need to talk to me, seek me out as mentioned here. Never mind.

Also note that I’ve decided (at least for now) not to faff about with Google analytics. So I have no idea if anyone will be reading this. For that reason, if you are reading this and you see a disappointing lull in content, reach out to me and let me know.