How to create a story game with Javascript
Tags: twine, games, javascript
Published: 2022-10-27 11:00:21

---
title: How to create a story game with Javascript
published: true
description: Creating interactive fiction and games using Twine with almost no programming knowledge.
tags: twine, games, javascript
cover_image: https://metacognitive.me/content/images/2022/10/image-7.png
canonical_url: https://metacognitive.me/how-to-create-a-story-game-with-javascript/
---
If you don't know Javascript or what that is, don't worry! You'll be able to learn along the way or even go without it at all. What we are going to explore is an open-source tool to build interactive stories - [Twine](https://github.com/klembot/twinejs)(or see its [website](https://twinery.org/)).

It allows us to make nonlinear stories. It can be presentations, fiction, or text-adventure games. In a nutshell, Twine creates an HTML file with all the content. So, it looks like a usual web page, and the tool is the constructor. You can upload such a "website" on the web or mobile platforms, i.e., anywhere that can parse HTML.

 ![](https://metacognitive.me/content/images/2022/10/image-7.png)*An example of a non-linear story (interactive fiction)*

If you're particularly interested in games / interactive fiction, see an [extensive collection of them](https://itch.io/games/tag-twine). So what sort of games are possible? All of them since we can add Javascript. However, the easiest way is to do text-adventure games. Why? You don't need to program anything or much. You add stories and connect them in a meaningful way using links. I.e., if you choose "A", the story "AA" will be shown, if you choose "B", the story "BB" will be shown.

See also [what you can create](https://twinery.org/cookbook/questions/making.html) with the tool from their docs.

 ![](https://metacognitive.me/content/images/2022/10/Screenshot-from-2022-10-27-12-46-13.png)*An example of an actual Twine page with a story*

Creating such a page takes a few minutes on [the Twine website](https://twinery.org/)(you can do that online). You can add Javascript, videos, music, images, customize styles via CSS and do the linking and playing around. It's a great way to start. Next, I will show you a different approach if you're used to coding in any developer's editor and want version control.

Try online first, especially if your story/game is small and only needs a little custom functionality.

## Why you may need Javascript

You can go without it and feel fine. But if you have custom stuff, you'll use some macros and scripts. What are they? Imagine Twine as a core, but it has various engines that do things differently. It supports four such engines(story formats) to make the creation process more accessible. Each of them varies in complexity. In this article, I'll be using [SugarCube](https://www.motoslave.net/sugarcube/2/).

This story format has many built-in things you may need. For example:

1. Saving a game, resuming it from a save.
2. Various events to react to. E.g., when a story is rendered, started rendering, etc.
3. Macros, i.e., useful built-in blocks/functions. For example, buttons, custom links, conditional rendering, setting variables, DOM manipulations, etc.
4. Audio handling.
5. And many other valuable things.

Let's create a simple project where we want to use custom Javascript and CSS styles, but more importantly - we want to have version control! I don't use the tool's online or desktop version because I can only manage stories as files and have their versions by commit.

You'll need to install [Tweego](https://www.motoslave.net/tweego/), a tool that can parse stories as files in any preferred text editor. Be aware of its limitations, though:

1. When writing this article, the last update of Tweego was two years ago.
2. Thus, you may not have all of the features from the supported story formats(e.g., Sugarcube).

Now you need to create a project folder:

```
$ mkdir twine-project
$ cd twine-project
$ git init
```

You can move the Tweego executable to this folder as well and add it to `.gitignore`

It's up to you how to organize files now! An example structure may look like the following:

```
.gitignore
README.md
bin/
src/
├─ config.tw
├─ styles/
│ ├─ menu.css
│ ├─ main.css
├─ modules/
│ ├─ menu/
│ │ ├─ menu.tw
│ │ ├─ menu.js
```

In the `bin` folder you have the Tweego executable to build the output to HTML(we'll get to that). All the story(game)-related code is under `src` folder. Tweego will put all Twine(`.tw`) files, CSS styles, Javascript scripts into one HTML. Therefore, it doesn't matter what project structure you have.

### Twine format

Now, closer to the coding: what is `config.tw`? This is where your code will be in Twine format. Take a look at the [specification](https://github.com/iftechfoundation/twine-specs/blob/master/twee-3-specification.md). You may name this file whatever you want. It's named `config` for readability. There, we specify the settings for our game:

```
:: StoryTitle
My first game

:: StoryData
{
	"ifid": <a serial number of your game>,
	"format": "SugarCube",
	"format-version": "2.30.0",
	"start": <a name of the story that will be shown first>
}

:: StoryAuthor
<your name if you want>

<<script>>
// in case you'll need to have 3rd-party scripts
// remove this <<script>> section at all for now
importScripts(
	'https://cdn.jsdelivr.net/npm/chance'
)
<</script>>
```

You need to generate a serial number for your game, i.e., IFID. Read more about how to do that [here](https://ifdb.org/help-ifid). But for now, you can use `0000A000-A0E0-00C0-00B0-CF000E000D0E` to skip this boring step.

`format` tells Tweego which story format to use. We'll use `SugarCube`. `format-version` is a version for this story format, currently supported is `2.30.0` only. However, there are newer versions(a limitation of Tweego).

`start` is a story that will be shown first. Let's create a file `start.tw` with this content:

```
:: StartOfMyGame

This is the first screen of my game, yay!

[[Start playing]]
[[Read about the author]]
```

The `::` here indicates the ID of your passage(i.e., a page). It can be anything, e.g., `:: start-of-my-game` or `:: something like this`. Now that you have the ID, change your `config.tw` to have:

```
"start": "StartOfMyGame"
```

After the passage(page) ID, you do whatever you want. In our case, we wrote, _"This is the first screen of my game, yay!"_, and it'll be rendered as regular text, that's it! The `[[Start playing]]` thing is a **link** to another passage(page).

To build that to HTML, run Tweego(it'll be watching for files changes):

```
$ ./bin/tweego -w src -o ./output/index.html
```

Here, we're telling it to watch the `src` folder and build an output HTML into the `output` folder as `index.html`. Run this command, and you'll see the HTML output in that folder. Don't forget to add `output` to `.gitignore`. Open `output/index.html` in a browser and you'll see something like this(with a more dark background color):

 ![](https://metacognitive.me/content/images/2022/10/Screenshot-from-2022-10-27-14-10-54-1.png)

We create the links, but we also need to create such pages. So, we need to change the `start.tw`:

```
:: StartOfMyGame

This is the first screen of my game, yay!

[[Start playing]]
[[Read about the author]]

:: Start playing
<<back>>
It's another page called "Start playing".

:: Read about the author
<<back>>
I'm the author. This is my page.
```

We've added two more pages, so whenever you click on, for example, "Start playing", you'll be redirected to the "Start playing" passage:

 ![](https://metacognitive.me/content/images/2022/10/Screenshot-from-2022-10-27-14-14-55.png)

We see a new link here - "Back"! `<<back>>` is a SugarCube [macro](https://www.motoslave.net/sugarcube/2/docs/#macros-macro-back) that redirects a user to the previous passage(`StartOfMyGame`). It's a more convenient way of doing that than storing a navigation history each time.

We might create these two new passages in the other files or create all the game passages in one file. It doesn't matter because Tweego puts all of the files together into a single HTML file. You don't need to care about importing something!

### Adding Javascript to Twine stories

Let's imagine we want to store some information about a player's choices. There are two approaches:

1. We may use the [`<<set>>` macro](https://www.motoslave.net/sugarcube/2/docs/#macros-macro-set).
2. We may use Javascript.

When using `<<set>>`:

```
:: StartOfMyGame

This is the first screen of my game, yay!

<<link "Start playing" "StartPlaying">>
	<<set $choice to "StartPlaying">>
<</link>>
<<link "Read about the author" "AboutTheAuthor">>
	<<set $choice to "AboutTheAuthor">>
<</link>>

:: StartPlaying
<<back>>
It's another page called "Start playing".
The choice is <<= $choice>>

:: AboutTheAuthor
<<back>>
I'm the author. This is my page.
The choice is <<= $choice>>
```

A few new things here:

1. `<<link>>`[macro](https://www.motoslave.net/sugarcube/2/docs/#macros-macro-link) does the same as `[[]]`, but it adds more customizability. In our case, we kept the link text, but indicated a different passage ID(`StartPlaying`, e.g.). Also, we can do something when a link is pressed, e.g., a `<<set>>` instruction below.
2. `<<set>>` macro stores a variable.
3. `<<= $choice>>` is a macro to evaluate expressions. In our case, it's displaying `$choice` variable we set before.
 ![](https://metacognitive.me/content/images/2022/10/Screenshot-from-2022-10-27-14-38-20.png)

We can achieve the same using Javascript(however, it seems unnecessary complicated in this example):

```
:: StartOfMyGame

This is the first screen of my game, yay!

<<link "Start playing" "StartPlaying">>
	<<script>>
		State.setVar('$choice', 'StartPlaying (Javascript)')
	<</script>>
<</link>>

:: StartPlaying
<<back>>
It's another page called "Start playing".
The choice is <<= $choice>>
```

I removed the second passage to not duplicate the code. Here are a few new things:

1. We still do the scripting inside `<<link>>` macro. But we use `<<script>>` macro now. Inside of it, we use a global object `State`'s method `setVar` which does the same as `<<set>>` in the previous example.
2. We still display the `$choice` variable not using Javascript, but we could find that HTML block using jQuery(which is built-in in SugarCube scripts), and then set the value of it to `$choice`, but it's unnecessary.

When you use Javascript, you have access to the story format's APIs, so it's more customizability. However, you may not encounter such complexity in your game.

That's it for now! There are more things to do in a game, of course. But you have the documentation and tools to discover and learn more independently.

[Source](https://metacognitive.me/how-to-create-a-story-game-with-javascript/)