FRISE - the FRee Interactive Story Engine

Writing Games with FRISE

This page describes how to write interactive fiction with the FRISE system. We begin with a very brief discussion of what is interactive fiction and what are the different ways one could organize an interactive story. Then we go over the key coding components of FRISE and how they are useful in creating interactive stories.

Interactive Fiction and Types of Interaction

Interactive fiction is a kind of narrative in which different discrete scenes are presented in response to user choices. Here a scene is usually a small collection of fixed static images, video clips, or mini games, together with a text passage. If compared to a novel, scenes in scale are something between a paragraph and a chapter. Most works of interactive fiction have at most a few hundred scenes. User choices determine the navigation between scenes, and such navigation is usually accomplished by following one of a fixed number of outgoing links or instructions that take one to a different scene. From an author's perspective, a scene is a description of some location or event which is either memorable, or from which an important or memorable choice could be made. For fiction to be interactive, there has to be a choice: If the link between scenes is always "turn the page", then the narrative wouldn't be considered interactive fiction.
Interactive fiction can be broadly classified by whether user choices affect how a given scene is presented. We next consider two common cases and distinguish some interesting sub-cases.

Choose Your Own Adventure

A Choose Your Own Adventure (CYOA) is a story that has a fixed starting scene and in which all the scenes are static (don't change based on the user's choices). Further, each scene has a fixed number of choices for which scene to go to next. To play a CYOA, the user begins from a starting scene, and moves from scene to scene by requesting/following one of the fixed choices that the current scene has. Typically, some scenes have no outgoing choices, and can be viewed as an ending of the story. CYOAs get their name from the popular Choose Your Own Adventure book series that first appeared in 1970s. In these books, the notion of link was achieved by sentences like, "If you want to help the wayward elf, go to page 76."
Even though the interactions in a CYOA are simple, one can often find in them some interesting structures. For example, when writing a CYOA, as describing a scene takes time and energy, often two scenes might share some of their outgoing choices. I.e., both Scene A and Scene B might have as one of their outgoing choices a common Scene C. Another interesting structure is that of a loop or cycle, a sequence of scenes beginning with Scene A and leading back to Scene A. As the original CYOA book series was often read from a parent to their child before bed, the parent eagerly anticipating their free time after the story was done, loops were dreaded and much feared -- they could potentially, indefinitely postpone freedom. CYOAs with no shared outgoing scenes and no loops are called tree-like, those with no loops are called DAGs (short for directed, acyclic graphs). The collection of the scenes in a story and the links between them, is called the stories graph. The Standard Patterns in Choice-Based Games page at the Interactive Fiction Wiki describes some popular choices of story graphs.

Choose Your Scenery Adventure

In a Choose Your Scenery Adventure (CYSA) story, the way a scene is presented is affected by the actions you have previously taken in the story. Typically, this involves passages or links in a scene that may be displayed or not based on your previous actions. A common use case is a scene, or a link from a scene, that tracks if you've visited it on not. So the first time you go to a room, you might see a detailed description of the room, but if you loop back, and visit it a second time, a shorter description is displayed. Another example is you are in scene with a character Bob. The links out are:
  1. Ask Bob about A?
  2. Ask Bob about B?
  3. Ask Bob about C?
  4. Do something else.
When you click on one of the first three links, you are shown a short passage which concludes with a link back to the scene you just came from. However, when the scene draws again, the link you just followed is disabled. You could easily imagine generalizing the visited property of a scene or a link, to the visited-some-number-of-times (2,3,4,...) property of a scene or link. This might come up in a scene with a genie that has a make-a-wish link on it, after the third time you click the link, it becomes disabled. We call a story progressive if whenever there is a loop in the story scenes, there is a tracked quantity, important to game play, that increases so that we never see the same scene twice with the same value of that quantity. Life simulators are an important category of CYSA game, where you go through a cycle of groups of scenes for different times of the day, week, year, etc., however, when you come back to the start of the cycle again, some larger tracked unit has advanced. The fun in playing such simulators is to watch how the characters change or transform over that larger unit.
Rather than just track a single quantity to determine the passages and the links to display in a scene, one could imagine tracking several quantities. For example, how hungry you are as well as how tired you are. Your choices could affect the values of these quantities. You can then have passages or links display based on a combination of these values. For example, the "Cook boots and eat them" link in a scene might only display if you are at least 9 hungry and less than 5 tired.
Often interactive fiction is written such that the reader is the main character, so from the second person perspective. (You did this... You did that...) To enhance the level of immersion, it is common to allow the user to do character customization. This involves allowing the user to choosing the initial values of a collection of tracked quantities and values. Some simple values that might be tracked are the player's name and gender. Such tracked values are often periodically substituted for in passages wherever they are needed. You can also imagine letting the player choose their initial hair length, occupation, etc., but still allow these to change over the course of the game.
In describing a scene to someone else, it is natural to go through a checklist such as the following:
  1. Describe the location.
  2. Describe who and what was there.
  3. Describe what happened.
In the CYSA setting, we might conclude this with a list of plausible things the main character could do next. Let's look at each of the three items above in turn. In describing the location, in the CYSA setting, we might describe some of the tracked quantities of that location. For example, we might track the conditions of a road. This quantity changes depending on when the main character comes to the road: sometimes it is dry, wet, icy, etc. The second item illustrates that where people and objects are might vary over the course of the choices of the CYSA game, so the description of a scene should adapt to handle this. For example, the player might come to the locked, castle door with a key or may come to the locked, castle door without a key. As we can see from the first two points, making a CYSA story potentially involves keeping track of many quantities. The last point, (3) above involves figuring out how these quantities changed based on (1) and (2), and then describing the result. In general, this kind of bookkeeping is much easier for a computer to do than for a human to do.
Return to table of contents .

FRISE and Story Engines

We are now in a position to say some of the basic things a story engine does:
  1. It reads in the files the story author used to represent the story, extracting scenes (in FRISE terminology locations), objects, and tracked quantities, and initializes them. This may be done all at once at the start of the playing a game, or incrementally as the game progresses.
  2. It locates what scene should be presented first. Based on the current state of the locations, objects, and tracked quantities, it outputs this scene to the player.
  3. It waits for and interprets player inputs.
  4. It updates the states of the locations, objects, and internal states according to choices the player makes.
  5. It then presents the appropriate next scene and waits for more player inputs.
In addition to the above, a story engine might also:
  • Keep a record of the sequence of choices the user has made. Often interactive fiction players want to do things like "find all the possible endings", or "try different paths to try to find the best ending." To facilitate this, most engines support: saving the current game state, loading a game from a file, as well as forward and backward history actions to back out or redo choices. If the current game has ended or seems to be going really bad, the user might want to play again or restart.
  • Allow the player to inspect their own character's and other characters' statuses.
  • Allow the player to see their current inventory, what objects they have with them, or in general, inspect what are the objects at a location.
Besides FRISE itself, some popular interactive fiction platforms that handle story engine activities are Twine, Ren Py, RPG Maker, Inform. For each of these, the file format of the story is not HTML, but often can be compiled to a format that can be displayed on the web. I.e., HTML and Javascript or HTML and Web Assembly. Some of these also have a what-you-see-is-what-you-get (WYSIWYG) editor which can be convenient for simple CYOA and CYSA stories, but is less convenient for more complicated ones.
FRISE uses HTML as its file format as HTML is easy to learn and can be used more generally than just for story writing, increasing the odds people already know most of FRISE's tags -- or at least, can learn them quickly. It uses standard Javascript and CSS (Cascading Style Sheets) for more complicated effects, as again, these are the most popular languages for scripting on the web.
In general, it is possible to write a CYOA story for any file format that supports links between documents. CYOAs have been written for HTML, PowerPoint, PDF, epub, markdown, plain old paper from trees, etc. To handle CYSA possibilities, though, these formats are generally lacking without some additional augmentation. Even for CYOAs, there are advantages to using a story engine. To see this consider trying to write a CYOA using a collection of web pages (using just HTML, without any Javascript library or CSS files). You could view such a CYOA in a browser, you could use the browser's back and forward buttons for history. Using the browser's Save Page As... feature, you might even be able to "Save" games, although this would likely be fragile. From an author's perspective, some tasks would be awkward. For example, suppose you want all the times that a character speaks, that an icon of that character shows, that text of who the character is displays, and that the text is formatted in a particular way. You could repeat the HTML code for this in all places where the character speaks. What happens if you want to modify the appearance of a character speaker? You have to change many places. Thus, it makes sense to pull out a lot of these kinds of features useful for interactive storytelling into at least a common cascading style sheet. To do CYSA activities like character customization, tracking, modifying, and displaying state based components of a scene, one pretty much needs a scripting language and it makes sense to group common activities for interactive story telling into one script -- this is exactly what FRISE does. It is also small (less than 100 kilobytes including source comments), so if you know Javascript, it is not to hard too understand what the code is doing.
Enough background! Let's start seeing how to write interactive fiction in FRISE!
Return to table of contents .

A First Interactive Story in FRISE

Get an Editor

To write a FRISE Story, you need a text editor. It doesn't really matter which editor you choose, but you want an editor that writes plain text files without inserting special characters when you save. For example, Notepad on Windows would work, but Word or Google Docs are probably not your best choice. We recommend Pulsar as it is works on Windows, Mac, and Linux and is open source. It is the successor to the Atom Editor created by GitHub. Atom, though, was closed down by Microsoft after they were bought GitHub. Microsoft's Visual Studio Code also works, as does Apple's Xcode. Other popular editors include Sublime, VIM, Emacs. Finally, for games without images, it is possible to get them to run in browser-based IDEs such as https://ide.geeksforgeeks.org/online-html-editor, the only tricky thing is to give complete URLs when including the FRISE css and Javascript to make sure the game code will run. I.e.,
 https://www.frise.org/example_games/frise/css/game.css 
rather than the relative path frise/css/game.css and
 https://www.frise.org/example_games/frise/js/game.js
rather than the relative path frise/js/game.js .

Make Sure You Can See File Extensions

The file extension is the part of a file name after the final period in the name of a file. For example, if the file name was foo.txt, the file extension is txt . File extensions are often used to tell the computer or browser what kind of file it is dealing with. This allows the computer or browser to open the file correctly. As most humans don't care about the file extension, it became common practice not to show the extension when displaying the file name on many operating systems. We will be interested in creating web pages to make FRISE stories. I.e., files that have the extension html. However, often text editors save using the default file extension of txt. It can happen that you can save a file with a name like foo.html thinking you are saving an HTML file, but because you can't see the file extension, you are in fact saving a file whose name ends with html.txt, which will show in a browser as a plain text file rather than a normal web page. Here are some links to see File extensions on Windows 11 and File extensions on macOS.

Prepare a Folder for Your Story

The typical folder structure for a FRISE story looks like:
 
 name_of_story
  index.html  (your story)
  images (a folder of images used by your story)
  frise  (the folder with the frise code)
For each category of files you might have, you could add a separate sub-folder. If a particular category has too many files in it, you might want to split it into sub-categories. For example, if your project has lots of a cat and dog images, has short video clips and audio clips, and you created your own custom Cascading Style Sheets (css) and Javascript scripts, you might use the structure:
 name_of_story
  audio
  css
  index.html
  images
    cats
    dogs
  frise
  js
  video
Web pages in general are cross-platform. To help ensure this, you should avoid punctuation characters (you can use _ and -) in file names and try to stick to lowercase names and extensions. If your story uses media such as images and video, you should be aware that the choice of file format can affect file size a lot, and hence, how big your project will be to download if you host it somewhere. For example, the fork image used in the example on the main page of this web site was generated using DALL E 2. The original image was a png, a compressed but lossless format, and was about 1.8 million bytes. This was then cropped to its current dimension 366x480 using the Preview app that comes with Mac, giving a png of size 247 thousand bytes. Converting it to jpg file, a lossy file format, produces an image that is hard to tell from the previous png, but that takes only 60 thousand bytes. If you were to convert it to webp (this can be done using a program like ffmpeg, but not currently by Preview), the file size would be 13 thousand bytes.
One easy way to get a good starting structure is just to get the Example Downloads , rename the folder to what you want, and delete the examples but leave the frise folder and images folder (deleting images you don't want). Then you can create and start editing an index.html file in this folder.

Create an HTML File

An HTML document is built by nesting different kinds of tag pairs (formally, called elements) to say how the document should look. A tag pair consists of a start tag, which might look something like <tag_name>, and an end tag which would look </tag_name>. HTML has a fixed list of allowed tag names, some of which we will briefly introduced below. It also allows the use of experimental tag names which begin with x- and can be followed by alphabet letters (upper or lower case), _, -, or digits. Tags are required to nest correctly, so
 <x-a>
   <x-b></x-b>
 </x-a>
is okay, but
 <x-a>
   <x-b></x-a>
 </x-b>
is not. Let's look at a minimal HTML document:
 <!doctype html>
 <html lang="en">
 <head>
    <title>Minimal HTML File</title>
 </head>
 <body>
 </body>
 </html>
You could make this document by using one of the text editors we discussed earlier, typing or copy-pasting the text in, saving it. To then view it, you could double-click on the saved file in your File Explorer. The line <!doctype html> indicates to the browser the version of HTML being used, in this case, at least HTML 5. I.e., it tells the browser a modern version of HTML is being used. After this declaration, any valid HTML document begins with a <html> tag and ends with a </html> tag. The html element is required to have two children: a head element and a body element. The head contains information about the whole document. It needs to have at least a title element, but might also contain meta elements to describe the content of the document, and link elements used to associate style sheets and other documents with the current one to be used by the browsers drawing the document. The body element contains the main content a user sees in the browser window when the document is displayed. In the case above, the body tag has no content. Below is what the above document looks like in Firefox:
A picture of the minimal html document
Notice the title text appears in the tab name, but the content of the browser screen is white -- the body element had no content, so nothing was displayed. One last thing to point out about this example before we leave it, is that the open html tag is written as <html lang="en">. Here lang="en" is what is called an attribute. In this case, it tells the browser that the text language used within the document will be English, fr would have indicated French, es indicates Spanish, etc. An attribute has either the format attribute_name="attribute_value" or attribute_name='attribute_value' . Most browser will also understand if you drop the quotes entirely. In general, the allowed attributes of a start tag depend on the start tag. HTML considers any attribute name that begins with data- followed by alphabet letters (upper or lower case), _, -, or digits, a user-defined attribute and treats it as legal.
Let's briefly look at some HTML elements which can appear in the body of an HTML document, before giving our first FRISE example. Body elements come in two main types: Block elements which are rendered by the browser within some kind of rectangular region, and Inline elements which affect some property of the text within a block level element. We first give some block elements:
  • h1, h2, ..., h6 : These are heading elements and are used to give a heading for a passage in a document.
  • ul, ol : These are respectively unordered and ordered list container elements. They typically contain one or more li, list item, children.
  • p : A paragraph element is used to represent a paragraph of text. It may or may not have a text-indent as the start of its first line.
  • div : A division element is used to represent a generic container for text.
  • hr : A horizontal rule element is used to draw a horizontal line. This element may be written with or without a close tag. I.e., one can write either just <hr> or <hr></hr>.
Some example HTML inline elements are:
  • br : A break element is used to indicate a line break. Like the <hr> element, using a close tag with this element is optional.
  • a : An anchor element is used to link the current HTML document location to another location either within the current document or a different document. This might look like: <a href="destination_url">link_text</a>. This will be drawn by the browser as a link with text link_text which, when clicked, will take the user to destination_url. If destination_url ends with #some_fragment, then the default browser behavior is to scroll to where in that document there is a tag with attribute id="some_fragment".
  • img : An image element is used to indicate an image should be drawn at this point in the document. A typical format for the use of this tag is <img src="url_to_image_file" alt="What to display if the image cannot be found or if browser has images disabled" >. Like the <hr> and <br> elementw, using a close tag with this element is optional.
  • b : A bold element is used to indicate a range of text should be in bold face.
  • i : A italic element is used to indicate a range of text should be in bold face.
  • span : A span element is used to indicate a generic inline region of text.
Within an HTML document, you can use <!-- some comment --> to write a comment in your code which will be ignored by the browser when rendering your document. Below is a simple html document illustrating some of these tags in action:
 <!doctype html>
 <html lang="en">
 <head>
    <title>An HTML Document Illustrating Common HTML Elements</title>
 </head>
 <body><!--This is an example HTML comment -->
     <h1>An HTML Document with Some Common Elements</h1>
     <p>Once upon a time, there was a young tag that wanted to rule the world.
     Its name was <b>hr</b> and it looked like:</p>
     <hr id="hr_tag">
     <div>Many tags felt a <i>chill</i> when hr was near them. This
     was for three reasons:
     <ol>
     <li>hr was always acting as a separator.</li>
     <li>hr had a linear way of thinking.</li>
     <li>hr was very thin-skinned.</li>
     </ol>
     </div>
     <p>It was well-known that the br tag wanted to take a break<br> from him.
     </p>
     <p>The first <a href="#hr_tag">a</a> tag twin by contrast seemed somehow directly
     linked to him, the second twin thought he was a <a href="https://www.yahoo.com/">yahoo</a>.</p>
     <p>The hr tag hardly noticed as he was more interested in the img tag --
     she was a real looker:</p>
     <img src="images/frise_gal.jpg" alt="An image of the Frise Gal">
 </body>
 </html>
To see this example you can either try to type it in and save it in your text editor, obtain it as part of the FRISE Download or view the common html element example online. To see the within-document a tag link in the above code in action, you will need to make your browser window tiny, so that the HTML document uses a scroll bar to completely display.
Return to table of contents .

Create an HTML File with a FRISE Game in it

Let's now look at a minimal HTML document containing a FRISE story.
 <!doctype html>
 <html lang="en">
 <head>
    <title>A Minimal FRISE Game</title>
    <meta charset="UTF-8" >
    <link rel="stylesheet" href="frise/css/game.css" >
    <!--You could link your own/other stylesheets here-->
 </head>
 <body onload="initGame();">
 <x-game>
 <x-object id="main-character">
    <x-position>start</x-position>
    <x-name>Frise Guy</x-name>
    <x-icon>images/frise_guy.jpg</x-icon>
 </x-object>
 <x-location id="start">
     <x-present>
     <h1>The Adventure Begins</h1>
     <img src="images/fork.jpg" alt="A left road and a right road separated by
        a giant stainless steel fork -- Made in Dall-E" class="rounded" >
     <x-speaker name="main-character">Whoa! That's a big fork!</x-speaker>
     </x-present>
 </x-location>
 </x-game>
 <script src="frise/js/game.js" ></script>
 <!--You could link additional Javascript code here-->
 </body>
 </html>
The code above is actually a subset of a slight modification of the code on the FRISE landing page. It is a game consisting of a single scene and looks like:
A Picture of the Minimal FRISE game.
If can be found as part of the FRISE Download . It introduces three new standard HTML tags we haven't seen in action before <meta>, <link>, and <script>. The <meta> and <link> tags, like <hr>, <br>, <img>, do not need to be closed. The <meta> tag is used to provide information about the overall document, in this case, that the document is encoded using a particular Unicode format. The <link> says to use the file frise/css/game.css as a stylesheet for the current document. The <meta> and <link> tags can only be used within the <head> element. Finally, the <script> tag says load and run the code in the file frise/js/game.js. <script> tags can occur in the head or body of an HTML file, but as they often block the rendering of web pages until they finish loading and running, they are traditionally placed towards the end of an HTML file. As we said before, tags beginning with <x-...> are valid modern HTML, but by default the browser doesn't have an individualized way of processing them, so if we didn't have any CSS and Javascript files associated with the HTML document with instructions about them, they would be treated as generic block-level elements. I.e., as if they had been <div> tags. The frise/js/game.js script is where we tell the browser how to execute these tags, however, CSS files, and this one in particular, have an important role in controlling how the document looks, so we'll unpack a little how CSS works before discussing the rest of the code. A CSS file contains a sequence of CSS declarations. A CSS declaration looks like:
 some_selector_path_1,
 ...
 some_selector_path_m {
     some_property_of_an_element_1: value_1;
     ...
     some_property_of_an_element_n: value_n;
 }
For example, consider:
 p.red #foo span 
 {
     color: red;
     font-size: 10px;
 }
This selector says look for <p> elements whose start tag has a class attribute that contains red (I.e., <p class="maybe_other_stuff red maybe_other_stuff"> ). Then, within this element, look for a descendant element with id="foo", then within that element try to find a span element. If this complete path is found, the declaration says, color the text of that span element red, and use characters at most 10 pixels high for the text. This declaration had only one selector path, but if there were more than one (comma separated), the style commands would apply to all of them. In the case of a FRISE game, we initially don't want any of the text within the <x-game> element to display until the appropriate time, so the frise/css/game.css stylesheet has the declaration:
 x-game,
 x-action
 {
    display: none;
 }
This says don't show anything within an x-game or x-action element. In our FRISE story code, we see that the img element's class attribute has rounded as its value. This will match with the following declaration in the FRISE stylesheet:
 .rounded
 {
     border-radius: 20px;
 }
which will cause the corners of the image to be drawn rounded. As a last example of something the FRISE stylesheet does, we note the FRISE Javascript detects if the current browser is a mobile device or if the language being displayed is a right-to-left language. In the former case, it adds mobile to the html element's class attribute and in the latter adds rtl to the html element's class attribute. Using this, the FRISE stylesheet can appropriately modify how to display certain aspects of the game, for example, by setting the text-direction to be right-to-left:
 .rtl
 {
     direction: rtl;
 }
Returning to the story code, although the script tag in the code loads and runs the frise/js/game.js script, the code just declares what functions, classes, etc. it has so that the browser knows about them. It doesn't execute any particular function at this point. This is because it needs to wait until the browser has finished loading the whole game document and built a so-called Document Object Model (DOM) for it. The tag <body onload="initGame();" > tells the browser that when this load process is finished call the Javascript function initGame(), the main entry point function in the frise/js/game.js script. initGame() will use the browser's DOM to extract from the x-game element information about the story.
A FRISE game (when talking about FRISE we will tend to use the words game and story interchangeably) consists of a collection of game objects and game locations. A game object is defined using the x-object element. It is used to represent a person, a thing, etc. in the game. A game location is defined using the x-location element. It is used to represent places where a game object could be. In addition to objects and locations, FRISE also uses the x-action element to represent an action snippet of Javascript that can be called. In a FRISE story, each instance of x-object, x-location, x-action is required to have an id attribute and the value of that attribute must be unique. So a given x-object (or x-location or x-action ) cannot have the same id as another x-object, x-location, or x-action. This is actually convenient for writing stories as you can use your editor's Find feature to quickly find an x-object, x-location, or x-action even in a large document. All FRISE games are required to have a game object with an id with value main-character and this object must be positioned at a valid game location. Thus, a minimal FRISE game has to have at least one game object and one game location. The FRISE script will always draw the scene given by the location the main-character object is at.
In our story code, one can see there is an <x-object id="main-character"> tag. To position this object, this x-object element has an x-position element whose contents are start. This says the main-character is positioned at the game location whose id is start. If we look further in the code we see there is a <x-location id="start"> tag. This is the location which the FRISE script will initially draw. The rest of the main-character code in our example defines properties of the main-character : the x-name element says the main-character has a property name whose value is Frise Guy, the x-icon element says the main-character has a property icon whose value is images/frise_guy.jpg. In general, you can feel free to make up whatever properties you want for your objects. For example, you could specify an honorific property whose value is Doctor for your object by adding <x-honorific>Doctor</x-honorific> to a game object's x-object definition. We will see in a moment how we can tell FRISE to draw or alter the value of a property. The name and icon properties are particularly useful, because they are used in presenting scenes that have an x-speaker element.
To complete this first FRISE code example, let's look at the start location. A game location, like a game object, can have properties, and you are free to add properties using <x-some-property>value</x-some-property> code within its x-location definition. By default, the FRISE script will populate a game location's items property with the list of all objects positioned at it. To render a location, the FRISE script looks at each x-present element within it in sequence. If the x-present does not have a ck attribute, or the condition in the ck attribute is met, then the contents of the x-present are added to a div element with id='game-content' within the body element of the HTML document that is the game. This causes the HTML from the x-present element to become visible in the browser. The FRISE script also adds to each x-speaker tag in the game-content div, additional code so that the name property is displayed and so that it has an img element using icon 's value as the src attribute's url. So in the case of our first FRISE story, when the start location is rendered, we will see the h1 element, the img element with an image of a fork, and the modified x-speaker element with the name and icon of the main-character.
Return to table of contents .
Readers interact with a FRISE story typically by clicking on links (a elements) or x-buttons of the currently displayed scene. Links can either be to a different web page or can be to within the current document. Links to outside the current document are treated as normal web page links and cause the browser to go to whatever web page was requested. So a clicking a link <a href="https:/www.yahoo.com/">Yahoo</a> will take you out of the story and to the Yahoo! homepage. In-story link urls all begin with the sharp symbol, #. When an in-story link is clicked, the FRISE Javascript computes one turn of the story/game. During a turn, the url of the link or button is used to determine any initial actions that need to be carried out and then the main-character is moved to a new location specified by the URL. Finally, each object and location is given an opportunity to compute some code on how to update itself. Let's look at a small example:
 <!doctype html>
 <html lang="en">
 <head>
     <title>A FRISE Game With Anchors and Links</title>
     <meta charset="UTF-8" >
     <link rel="stylesheet" href="frise/css/game.css" >
 </head>
 <body onload="initGame();">
 <x-game>
 <x-object id="main-character">
    <x-position>start</x-position>
 </x-object>
 <x-location id="start">
     <x-present>
     <h1>The Big Choice</h1>
     <a href="#choose-a">I Choose A</a> -
     <x-button href="#choose-b">I Choose B</x-button>
     </x-present>
 </x-location>
 <x-location id="choose-a">
     <x-present>
     <h1>Choice A</h1>
     <p>You have arrived at Choice A</p>
     <p><a href="#start">Go back to start</a></p>
     </x-present>
 </x-location>
 <x-location id="choose-b">
     <x-present>
     <h1>Choice B</h1>
     <p>You have arrived at Choice B</p>
     <p><a href="#start">Go back to start</a></p>
     </x-present>
 </x-location>
 </x-game>
 <script src="frise/js/game.js" ></script>
 </body>
 </html>
The above code can be found in the FRISE Download . You will notice that each location in this example, as well as in our previous examples, begins with an h1 heading for the location output. There is no requirement to do this, but it can make it easier for player's to know how your scenes are changing. This particular example has three locations which look like:
A Screenshot of Location 1 A Screenshot of Location 2 A Screenshot of Location 3
All of this story's anchor tags and x-button elements have in-story links. That is, the values of their href attributes all begin with #. An x-button is a FRISE tag that can be used in same way as an anchor tag, but is drawn as a button. Each of these urls does not have a semi-colon in it, so FRISE will not try to determine an action to carry out before following the link. The rest of each link url says which location the main-character should go to if the link is clicked. So <a href="#choose-a">I Choose A</a> says the main-character should be moved to the location with id value choose-a if it is clicked. In the case of this game, none of the objects or locations specify an default action to take before the new location is rendered, so clicking on the I Choose A link will immediately draw the location of the present tags location choose-a.
Return to table of contents .

Actions and Default Actions

In-story urls can also specify a set of actions to run before moving the main-character to a new position. The general format on an in-story url is:
 #action_1_name;action_2_name;...;action_n_name;next_location_id
Such a url would execute the Javascript code in the x-action elements with id 's action_1_name, action_2_name, ..., action_n_name, then it would move the main-character to next_location_id. Let's take a look at a FRISE story that uses these more complicated urls:
 <!doctype html>
 <html lang="en">
 <head>
     <title>A FRISE Game Using Actions</title>
     <meta charset="UTF-8" >
     <link rel="stylesheet" href="frise/css/game.css" >
 </head>
 <body onload="initGame();">
 <x-game>
 <x-object id="main-character">
    <x-position>start</x-position>
    <x-money>0</x-money>
 </x-object>
 <x-location id="start">
     <x-visits>0</x-visits>
     <x-present>
     <h1>Earn Money</h1>
     <p>You have visited this location ${loc('start').visits} times.</p>
     <p>You currently have ${obj('main-character').money} dollars.</p>
     <p>What would you like to do:</p>
     <ul>
     <li><a href="#click-links-for-money;start">Click links for a $1</a></li>
     <li><a href="#barista(5);start"
         >Work as a barista for 5hrs at $15/hour</a></li>
     <li><a href="#barista(8);start"
         >Work as a barista for 8hrs at $15/hour</a></li>
     <li><a href="#reset;start" >Restart Game</a></li>
     </ul>
     </x-present>
     <x-default-action>
         if (obj('main-character').position == 'start') {
             let visits = parseInt(loc('start').visits);
             visits++;
             loc('start').visits = visits;
         }
     </x-default-action>
 </x-location>
 <x-action id="click-links-for-money">
     let money = parseInt(obj('main-character').money);
     money++;
     obj('main-character').money = money;
 </x-action>
 <x-action id="barista">
     let money = parseInt(obj('main-character').money);
     let pay = parseInt(args[0]) * 15;
     obj('main-character').money = money + pay;
 </x-action>
 <x-action id="reset">
     game.reset();
 </x-action>
 </x-game>
 <script src="frise/js/game.js" ></script>
 </body>
 </html>
As with the other example games, the above code can be found in the FRISE Download or Viewed Online. This example consists of one location start, and all of the game links after performing their actions go to start. The example illustrates in-story urls where the game action is referenced with (#barista(5);start) and without an argument (#click-links-for-money;start). The latter case, says perform the code in action click-links-for-money then go to the start location. The code in the click-links-for-money action is:
 let money = parseInt(obj('main-character').money);
 money++;
 obj('main-character').money = money;
In the above, obj is the name of a Javascript function, a piece of Javascript code that can be executed on demand. The notation obj('main-character') says execute the code associated with obj using the string 'main-character', the parameter of the function. In this case, this obj function uses the name provided to return the game object corresponding to the main-character. Given a Javascript object some_object, we can access a property some_property of it using the notation some_object.some_property. So obj('main-character').money refers to the money property of the main-character object. In the game above, this was initially set to 0 by the x-money element in the HTML. In a FRISE game, all property of objects or locations set by HTML code are initially strings (a sequence of character) rather than numbers. In Javascripts operations like '+' behave differently according to the type of the operands. So "hi" + "there" is the string hithere and "7" + "3" is "73"; whereas, when we add the numbers 7 + 3 we get 10. To convert a string into an integer, if that is possible, we can use the Javascript function parseInt. The line let money = parseInt(obj('main-character').money); says make a new temporary variable (a place to store something in Javascript) named money (this is what let does), then take the value of the money property of the main-character object, convert it from a string to an integer, and copy it into this temporary variable. The next line money++ say add one to the value currently stored in money. Finally, obj('main-character').money = money; says copy the value from the temporary variable back into the main-characters 's money property.
We can display the value of properties of game objects and game locations, or for that matter, any Javascript expression using the syntax ${ expression_to_display } anywhere within an x-present element. So the line in the above game's start location's x-present element:
 <p>You currently have ${obj('main-character').money} dollars.</p>
will print out the current value of the main-character 's money property. If this property currently is a number, Javascript will automatically convert it to a string before printing it.
The two in-story urls #barista(5);start and #barista(8);start each call the barista action with an argument. In general, it is legal to have multiple arguments, for example #some_action(arg_1, arg_2,...arg_n);... When given such a URL, before FRISE starts executing the code of the action it creates a Javascript array args, a sequence of variables indexed by the numbers 0, 1, 2..., with the values of the arguments. So args[0] will get the value of the first supplied argument, args[1], the value of the second, etc. Thus, when barista(5) is being processed args[0] will get the value 5. So if we look at the code for the barista action, the first line will create a variable money with the value of the number parsed from the main-character 's money property. The second line creates a variable pay with the value 5 parsed as a number from args[0] times 15 (* means multiply). Finally, the last line adds these two quantities and stores the result back into the main-characters 's money property.
The last in-story link in the example above, #reset;start, calls the reset action. This action has a single line game.reset() . Here game is a variable created by FRISE when initGame() is executed. It has two main properties game.objects and game.locations which are used to store all the game objects and game locations of the game. It also has several useful member functions (aka methods) useful for managing a FRISE game. Over the course of this, or any CYSA game, the value of objects and locations may change. The reset member function empties game.objects and game.locations, then re-parses the objects from the HTML document of the game. Going to the start location after calling the reset action, thus, effectively restarts the game.
We have written the x-action elements above outside of any x-object or x-location. It is also legal to write them inside and may be convenient to do so if it helps keep game actions near the locations and objects which call them.
The last piece of the FRISE story above we have yet to explain is the x-default-action element within the start location. Any object or location can have a default action defined by such an element. If it has one, then this code will execute once per game turn, after the main-character has been moved, but before the current location is presented. The default action code for the start location:
 if (obj('main-character').position == 'start') {
     let visits = parseInt(loc('start').visits);
     visits++;
     loc('start').visits = visits;
 }
uses a Javascript if statement. An if statement has the format if (some_condition_is_met) { ...} is processed by first checking whether some_condition_is_met is true, if it is, then the code in {} is processed, otherwise, it is not. In this case, the condition is checking whether the main-character is currently at the start location. As there is only one location in our game, and we did position the main character there, the condition holds. So the next three lines run and add 1 to the visits property of the start location. Although, in this case, using an if statement to check that the main-character is at the location is unnecessary, it is actually a useful idiom to use for more complicated games where the main-character is moving and we might not want to do some action unless the main-character is currently at some location.
One final point about using x-action and x-default-action. If the code within the element uses the < or > symbols, then the browser can become confused and think it is part of a tag, and so not process your game document correctly. On the other hand, browser's are clever enough to ignore < or > symbols within script tags. For this reason, FRISE supports writing x-action and x-default-action elements with the alternative syntax:
 <script type="text/action">
 // some code
 </script>
and
 <script type="text/default-action">
 // some code
 </script>
respectively.
Return to table of contents .

Presentations with Conditions

In the last game example, we saw how we can use Javascript if statements to conditionally execute code in a default action. The next game example shows different ways to conditionally show text when presenting a location.
 <!doctype html>
 <html lang="en">
 <head>
     <title>A FRISE Game Using Actions</title>
     <meta charset="UTF-8" >
     <link rel="stylesheet" href="frise/css/game.css" >
 </head>
 <body onload="initGame();">
 <x-game>
 <x-object id="main-character">
    <x-position>start</x-position>
    <x-money>0</x-money>
 </x-object>
 <x-location id="start">
     <x-present>
     <h1>Earn Money</h1>
     <p>You currently have ${obj('main-character').money} dollars.</p>
     <p>What would you like to do:</p>
     <ul>
     <li><a href="#click-links-for-money;start">Click links for a $1</a></li>
     </ul>
     </x-present>
     <x-present ck="parseInt(obj('main-character').money) >= 2">
     <ul>
     <li><a href="#movie">Go to $2 movie night</a></li>
     </ul>
     </x-present>
     <x-action id="click-links-for-money">
         let money = parseInt(obj('main-character').money);
         money++;
         obj('main-character').money = money;
     </x-action>
 </x-location>
 <x-location id="movie">
     <x-present ck="parseInt(obj('main-character').money) >= 0">
     <h1>At the Movies</h1>
     <p>You decided to use your hard-earned cash to enjoy a movie.
     You purchased a ticket and found a seat in the theater.</p>
     <p>And then...</p>
     </x-present>
     <x-present ck="parseInt(obj('main-character').money) >= 0"
        stage="pause(2000)">
     <p>The theater went dark and the movie began to play. But there was no
     sound. So you shouted, "Turn on the sound!"</p>
     </x-present>
     <x-present ck="parseInt(obj('main-character').money) >= 0"
        stage="clickProceed('You looked around...')">
     <p>You could dimly see the outline of the projectionist at the
     back of the theater fumbling with his equipment. Finally, you
     got to see the movie and it was awesome. Thoroughly satisfied,
     you returned to your flat and fell asleep...</p>
     <p><a href="#reset;start"
         >You woke up the next day ready for work...</a></p>
     </x-present>
     <x-present ck="parseInt(obj('main-character').money) < 0">
     <h1>You're Broke!</h1>
     <p>You don't have enough money to see the movie.</p>
     <p><a href="#reset;start">You lose, but decide to play again.</a></p>
     </x-present>
     <x-default-action>
     if (obj('main-character').position == 'movie') {
         let money = parseInt(obj('main-character').money) - 2;
         obj('main-character').money = money;
     }
     </x-default-action>
 </x-location>
 <x-action id="reset">
     game.reset();
 </x-action>
 </x-game>
 <script src="frise/js/game.js" ></script>
 </body>
 </html>
The above code can be found in the FRISE Download or Viewed Online. This game has two locations. You start out with 0 dollars. The main-character 's money property is used to track this. At the first location, start, there a is a link to: "Click links for a $1". This executes the click-links-for-money action and then goes back to the start location. The code for click-links-for-money was discussed in the previous game example. Unlike any of our previous examples, the start location now has two x-present elements, and the second one has the attribute ck (called a check condition -- you can in fact use check rather than ck if you like) with value parseInt(obj('main-character').money) >= 2. This gets the main-character 's money property, a string, converts it to an integer and compares it with 2. If money is greater than or equal to 2, the check condition is satisfied, otherwise, it is not. If the condition is satisfied then the contents of the x-present are shown. In general, FRISE will show all the x-present elements that either don't have a check condition, or whose check conditions are satisfied. Further the order of presentation will be as written in the HTML. Although the above FRISE story only uses check conditions with a single comparison, one can in fact use any Javascript boolean expressions. Expressions like the above are boolean expressions. Besides simple boolean expression, if b1 and b2 are boolean expressions, so are b1 && b2 (which is satisfied if and only if both b1 and b2 are), b1 || b2 (which is satisfied if and only if either b1 is satisfied or b2 is satisfied), and !b1 (which is satisfied if and only if b1 is not satisfied). In addition, to ck conditions, FRISE supports else-ck conditions. For example,
 <x-present ck="obj('sky') == 'blue'">
     <p>What a lovely day!</p>
 </x-present>
 <x-present else-ck="obj('sky') == 'gray'">
     <p>It seems cloudy!</p>
 </x-present>
 <x-present else-ck="true">
    <p>I don't know what the weather is.</p>
 </x-present>
In the above, if the ck condition on the first x-present element is satisfied, FRISE won't attempt to check the else-ck 's, and so their x-present elements won't be display. On the other hand, if obj('sky') == 'gray' holds, the first ck condition will fail, so that x-present won't be shown, the second x-present will be displayed as its else-ck holds, and the last else-ck won't be evaluated. If obj('sky') == 'pink' , the first two x-present 's won't be display, else-ck="true" trivially holds, and so the last x-present will be displayed.
Sometimes you might want to nest conditional checks, for example, you might want to vary the scene that is presented by day, then for a given day, vary the text by what character's are present. Although you can accomplish this by having a sequence of ck's, each of which has a boolean combination of day and character conditions, this makes for awkward looking code. FRISE does not support nesting x-present elements, however, it does support data-ck="some_condition" attributes on any HTML tag which can achieve this desired result:
 <x-present ck="baseLoc().day == 3">
   <div data-ck="obj('cool-guy').position == here()">
   The cool guy is here.
   </div>
 </x-present>
In the above FRISE story, if you have at least $2 at the start location, you will see a link to go see a movie. Clicking this link takes you to the movie location. Looking at the code, one can see the default action of the movie location is to debit the amount of money of the main character by $2, corresponding to purchasing a movie ticket. Recall presenting a location occurs after the default actions are all done. A ticket purchase should only be successful if you have at least 0 dollars after the purchase. The conditions on the first three x-present elements of the movie location check this. So they correspond to what you will see if you could purchase a ticket. The last x-present check handles the case where the amount of money you have is less than zero to present something else. You can get the less than zero case if you stay on the movie location and click reload a few times (each time debiting $2) until your money goes negative.
If you look at the second two x-present elements of the movie location, you see they each have a stage attribute. This controls the staging/arranging/mise-en-scène of the given x-present element. I.e., what goes on before the contents of the element are presented. Currently, FRISE supports two options for stage, either pause(some_milliseconds) which waits some_milliseconds many milliseconds before showing the contents of the x-present, or clickProceed(link_text) which displays a link with text link_text that must be clicked before the contents of the x-present are displayed. In the above code, we pause two seconds, before displaying, "The theater went dark...". Then we present a link with text, "You looked around...", which when clicked shows, "You could dimly see..."
Return to table of contents .

Moving Objects and Non-Player Characters

In our examples so far, we have only used default actions on locations, and we have wrapped our code to always check that the main-character is at the location before doing anything. The next example has an object with a default action that executes every turn. It shows that FRISE can be used to write games where objects move around according to some rules. I.e., we could write games with non-playing characters (NPC) with some weak kind of AI behavior.
 <!doctype html>
 <html lang="en">
 <head>
     <title>A FRISE Game With Objects That Move
     </title>
     <meta charset="UTF-8" >
     <link rel="stylesheet" href="frise/css/game.css" >
 </head>
 <body onload="initGame();">
 <x-game>
 <x-object id="main-character">
    <x-name>You</x-name>
    <x-position>office</x-position>
 </x-object>
 <x-object id="sarah">
    <x-name>Sarah</x-name>
    <x-position>office</x-position>
    <x-icon>images/sarah.jpg</x-icon>
    <!--Sarah's icon was the Dall-E 2 query
        smiling twenty-something girl in front of a blue background -->
    <x-default-action>
    if (obj('sarah').position == 'coffee-shop') {
        game.moveObject('sarah', 'office');
    } else {
        game.moveObject('sarah', 'coffee-shop');
    }
    </x-default-action>
 </x-object>
 <x-location id="office">
     <x-present>
     <h1>Office</h1>
     </x-present>
     <x-present ck="obj('sarah').position == 'office'">
     <x-speaker name="sarah" expression="Surprised">I didn't
     expect you to be in on the weekend!</x-speaker>
     </x-present>
     <x-present>
     <div>
     <x-button href="#office">Stay Here</x-button>
     <x-button href="#coffee-shop">Go to the Coffee Shop</x-button>
     </div>
     </x-present>
 </x-location>
 <x-location id="coffee-shop">
     <x-present>
     <h1>Coffee Shop</h1>
     </x-present>
     <x-present ck="obj('sarah').position == 'coffee-shop'">
     <x-speaker name="sarah" >Oh, hey. Long time no see!</x-speaker>
     </x-present>
     <x-present>
     <x-button href="#coffee-shop">Stay Here</x-button>
     <x-button href="#office">Go to the Office</x-button>
     </x-present>
 </x-location>
 </x-game>
 <script src="frise/js/game.js" ></script>
 </body>
 </html>
The above code can be found in the FRISE Download or Viewed Online. This game has two object, the main-character and sarah, and it has two locations, an office and a coffee-shop. The game starts with the main-character and sarah at the office. Before this location draws itself though, sarah 's default action runs. The code for the default-action is:
 if (obj('sarah').position == 'coffee-shop') {
    game.moveObject('sarah', 'office');
 } else {
    game.moveObject('sarah', 'coffee-shop');
 }
we have seen if statements before, but this is our first example of an if-else statement. The first part of the if-else statement checks if sarah is at the coffee-shop. If this is the case, then the moveObject member function of the game object is executed with the arguments sarah and office. This member function changes the location of an game object (sarah, in this case) to a target location (office ). In an if-else statement, if the theif condition fails then the else block is executed. The else block moves sarah to the coffee-shop. As sarah 's initial position when the game begins is the office, this default action will move sarah to the coffee-shop before anything is drawn. Each turn thereafter, sarah will alternate between the office and the coffee-shop. At each of the locations there are two x-button 's: "Stay Here" and "Go to the_other_location" (where the_other_location is either the Office or teh Coffee Shop depending on where you are). These links take you to where you'd expect. Each of the two locations has a condition x=present element for the case when sarah is at that location. If both the main-character and sarah are at the office, then a speaker element with sarah saying, "I didn't expect you to be in on the weekend!" is shown. If both the main-character and sarah are at the coffee-shop, then a x-speaker element with sarah saying, "Oh, hey. Long time no see!" is shown. This pretty much completes the description of this game except to note that is also uses one feature of the x-speaker element we haven't seen before, the expression attribute. If present, this attribute will but next to the speaker's name in bold-face, the contents of this attribute.
Return to table of contents .

Presentations with Form Elements

As we mentioned in the Choose Your Own Scenery section , interactive fiction is often written from the second person perspective (You do this... You do that...). To enhance the immersive aspect of this, it is useful to allow the player to do character customizations. This requires getting inputs from the player beyond just the links they click. Luckily, HTML comes with a set of tags for collecting using inputs on web forms. FRISE let's you use most of these elements to collect information. Below is a short game to illustrate how this works.
 <!doctype html>
 <html lang="en">
 <head>
     <title>A FRISE Game with Form Element</title>
     <meta charset="UTF-8" >
     <link rel="stylesheet" href="frise/css/game.css" >
 </head>
 <body onload="initGame();">
 <x-game>
 <x-object id="main-character">
    <x-name>You</x-name>
    <x-real-name>Frise Guy</x-real-name>
    <x-age>30</x-age>
    <x-handedness>right</x-handedness>
    <x-issue>I have a toothache.</x-issue>
    <x-position>examination-room</x-position>
    <x-icon>images/frise_guy.jpg</x-icon>
 </x-object>
 <x-object id="doctor">
    <x-name>Doctor</x-name>
    <x-position>examination-room</x-position>
    <x-icon>images/doctor.jpg</x-icon>
    <!--Doctors's icon was the Dall-E 2 query
        serious looking black doctor with a green background -->
 </x-object>
 <x-location id="examination-room">
     <x-present>
     <h1>Examination Room</h1>
     <x-speaker name="doctor">Before we do an examination, let's begin
     with some intake questions...</x-speaker>
     <x-speaker name="doctor">What is
         <label for="mc-name">your name</label>?</x-speaker>
     <x-speaker name="main-character">I am <input id="mc-name"
         data-for="main-character" name="real-name">.</x-speaker>
     <x-speaker name="doctor">How <label for="mc-old">old</label> are you?
     </x-speaker>
     <x-speaker name="main-character">
     <select id="mc-old" data-for="main-character" name="age">
     <option value="20">20</option>
     <option value="30">30</option>
     <option value="40">40</option>
     <option value="50">50</option>
     <option value="60">60</option>
     </select>
     </x-speaker>
     <x-speaker name="doctor">Which is your dominant hand?
     </x-speaker>
     <x-speaker name="main-character">
     <input type="radio" id="left-hand" data-for="main-character"
        name="handedness" value="left">
     <label for="left-hand">Left Hand</label>
     <input type="radio" id="right-hand" data-for="main-character"
        name="handedness" value="right">
     <label for="right-hand">Right Hand</label><br>
     </x-speaker>
     <x-speaker name="doctor">What
         <label for="mc-issue">issue</label> brings you in today?
     </x-speaker>
     <x-speaker name="main-character">
     <textarea id="mc-issue" data-for="main-character" name="issue">
     </textarea>
     </x-speaker>
     <x-speaker name="doctor">
         <a href="#summary">Okay, let me summarize...</a></x-speaker>
     </x-present>
 </x-location>
 <x-location id="summary">
     <x-present>
     <x-speaker name="doctor">
     <p>Your name is ${obj('main-character')['real-name']}.</p>
     <p>You are ${obj('main-character').age} years old.</p>
     <p>You are ${obj('main-character').handedness} handed.</p>
     <p>You came here because ${obj('main-character').issue}</p>
     <p>Is that correct?</p>
     </x-speaker>
     <x-speaker name="main-character">
     <a href="#examination-room">I think we should go over it again.</a>
     </x-speaker>
    </x-present>
 </x-location>
 </x-game>
 <script src="frise/js/game.js" ></script>
 </body>
 </html>
The above code can be found in the FRISE Download or Viewed Online. The game consists of two objects: the main-character and a doctor, and two locations: an examination-room and a summary location. The main-character starts in the examination room. The present element in the examination-room is a conversation between the main-character and the doctor where the doctor asks some intake questions and the main-character gives responses which can be changed by the user altering their initial values. There is a "Let me summarize" link at the end of this present element that takes one to a summary location. Here the potentially modified values of main-character 's responses are presented. The code for the examination-room location illustrates the following common HTML elements for receiving user input:
  • input : this element by default is drawn as a single line text field. Its type attribute though can be used to make it look like a variety of different input controls. The values for type that FRISE supports are: text, for the default text field; range, for a slider to select a value in a range; radio for radio buttons; color, to choose a color; date, to choose a date; email, to input an email; number, to choose a number; tel, to input a telephone number; time, to choose a time; and url, to choose a url.
  • textarea : this element is for multi-line text input. Its size and shape can be controlled by setting its width and height properties using CSS.
  • select : this element is used to draw a dropdown control. The different possibilities for this dropdown are determined by the option elements it contains.
The example game above illustrates a default input element, a radio input element, a textarea, and a select dropdown. To say what object or location a given input, textarea, or select element's values should be bound to, FRISE uses the data-for attribute. Recall for any HTML element, any attribute that begins with data- is considered a user-defined attribute and is always considered valid. To say what property of that object or location to bind to we use the name attribute of the input, textarea, or select element. So, for example, the values in the text field given by:
 <input id="mc-name" data-for="main-character" name="real-name">
will initially come from the value of obj('main-character')["real-name"], and if the user makes any change to the text field, the new value is written back to obj('main-character')["real-name"]. Here we are using the alternative syntax some_object['some_property'] to refer to a property of an object rather than some_object.some_property, because the property real-name contains a hyphen, so Javascript will confuse it for a minus sign if you use the latter syntax.
When working with user input fields, for accessibility reasons, you should attach a label to the field saying what kind of value is expected for that input. In HTML, this is done with the label element. For example,
 <label for="mc-name">your name</label>
says that the user input field with id="mc-name" should be given a value corresponding to your name.
Return to table of contents .
A navigation bar is a common collection of information, controls, and links always visible, or at least quickly accessible, from every location in a FRISE game. The image below shows a FRISE game with a navigation bar:
An Image of a Game with a Navigation Bar
The controls at the top of the navigation bar are present by default. The hamburger menu button is used to expand or collapse the navigation bar, the arrow buttons are used to go forward/backward through the game history. In FRISE, navigation bars are created by having a x-main-nav element within the x-game element. This is illustrated by the following FRISE story:
 <!doctype html>
 <html lang="en">
 <head>
     <title>A FRISE Game With a Nav Bar</title>
     <meta charset="UTF-8" >
     <link rel="stylesheet" href="frise/css/game.css" >
 </head>
 <body onload="initGame();">
 <x-game>
 <x-main-nav>
     <h1>The Counter Game</h1>
     <h2>by Chris Pollett</h2>
     <div class="left nav-label">
         <b><label for="start-total">Total Counts</label>:</b></div>
     <div><input id="start-total" type="range" name="total"
         data-for="start" min="0" max="100" disabled></div>
     <div><x-button href="#reset;start">Restart</x-button></div>
     <x-action id="reset">
         game.reset();
     </x-action>
 </x-main-nav>
 <x-object id="main-character">
    <x-position>start</x-position>
 </x-object>
 <x-location id="start">
     <x-counter1>0</x-counter1>
     <x-counter2>0</x-counter2>
     <x-total>0</x-total>
     <x-present>
     <h1>Two Counters</h1>
     <p>Counter 1: ${loc('start').counter1}</p>
     <p>Counter 2: ${loc('start').counter2}</p>
     <p>
         <x-button href="#addcount(1);start">Add to Counter 1</x-button>
         <x-button href="#addcount(2);start">Add to Counter 2</x-button>
     </p>
     <x-action id="addcount">
         let counter = "counter" + args[0];
         let counter_value = parseInt(loc("start")[counter]);
         let total_value = parseInt(loc("start").total);
         counter_value++;
         total_value++;
         loc("start")[counter] = counter_value;
         loc("start").total = total_value;
     </x-action>
     </x-present>
 </x-location>
 </x-game>
 <script src="frise/js/game.js" ></script>
 </body>
 </html>
The above code can be found in the FRISE Download or Viewed Online. This story consists of a single object, main-character positioned at a single location, start. The start location's present element outputs the values of the two start location properties counter1 and counter2, both initially 0, and outputs two x-buttons, which link to respectively #addcount(1);start and #addcount(2);start that can be used to add to these counters. The code for addcount first stores in a temporary variable, counter, the result of concatenating the string 'counter' with the argument that was supplied (either 1 or 2 in this case), it then looks up that property (either counter1 or counter2 ) of the start location and converts it from a string to an integer, adds one to it, and stores it back in this property. It also add 1 to the total_count property of the start location.
This game has a navigation bar which is defined in the main-nav element. As exhibited by the game code, within this element we can put standard HTML tags, and they will show in the navigation bar. Since the navigation bar is relatively narrow, one should be judicious in the choice of elements used and their width. The particular HTML for this game's navigation bar, uses an h1 and h2 element to show respectively the title of the game and the author. It then displays the current value of the total_count property of the start location using a range input element. This element has a disabled attribute to prevent a user from directly manipulating its value. Finally, it has a Restart x-button with link #reset;start, the reset activity calling game.reset() that was discussed in an earlier example.
Return to table of contents .

Making a Save Screen

Being able to save and load the current game state is an important feature that a story engine can provide. The FRISE game object has member functions to facilitate loading and saving games from either a file, or a modern browser's sessionStorage object. The actual HTML code, buttons, etc., for presenting to the user save/load options is left though to the game developer. Below we give an example game which uses a saves location with an HTML table in its present element to display current sessionStorage game saves, and then beneath this to give links to save to and load from disk. In the code downloads, we actually give two versions of this story, the one below which is slightly longer, but uses less Javascript, and a shorter one using Javascript for loops.
 <!doctype html>
 <html lang="en">
 <head>
     <title>A FRISE Game With a Save Screen</title>
     <meta charset="UTF-8" >
     <link rel="stylesheet" href="frise/css/game.css" >
 </head>
 <body onload="initGame();">
 <x-game>
 <x-main-nav>
     <h1>The Counter Game</h1>
     <h2>by Chris Pollett</h2>
     <div class="left nav-label">
         <b><label for="start-total">Total Counts</label>:</b></div>
     <div><input id="start-total" type="range" name="total"
         data-for="start" min="0" max="100" disabled></div>
     <div><x-button href="#saves">Saves</x-button></div>
     <div><x-button href="#reset;start">Restart</x-button></div>
     <x-action id="reset">
         game.reset();
     </x-action>
 </x-main-nav>
 <x-object id="main-character">
    <x-position>start</x-position>
 </x-object>
 <x-location id="start">
     <x-counter1>0</x-counter1>
     <x-counter2>0</x-counter2>
     <x-total>0</x-total>
     <x-present>
     <h1>Two Counters</h1>
     <p>Counter 1: ${loc('start').counter1}</p>
     <p>Counter 2: ${loc('start').counter2}</p>
     <p>
         <x-button href="#addcount(1);start">Add to Counter 1</x-button>
         <x-button href="#addcount(2);start">Add to Counter 2</x-button>
     </p>
     <x-action id="addcount">
         let counter = "counter" + args[0];
         let counter_value = parseInt(loc("start")[counter]);
         let total_value = parseInt(loc("start").total);
         counter_value++;
         total_value++;
         loc("start")[counter] = counter_value;
         loc("start").total = total_value;
     </x-action>
     </x-present>
 </x-location>
 <x-location id="saves" >
    <x-save-rows></x-save-rows>
    <x-present>
    <table class="save-table">
    <tr><th colspan="4">Saves
        <x-button class="float-opposite" href="#previous">X</x-button>
    </th></tr>
    ${loc('saves')['save-rows']}
    <tr><td colspan="2">
        <x-button
            href="#save-disk;previous">Save to Disk</x-button></td>
        <td><x-button
            href="#load-disk;exit">Load from Disk</x-button></td>
        <td><x-button
            href="#delete-slot-all;exit">Delete All</x-button></td></tr>
    </table>
    </x-present>
 <x-location id="saves" >
     <x-slot1>Save</x-slot1>
     <x-filename1>...</x-filename1>
     <x-filled1>not-filled</x-filled1>
     <x-delete1>disabled</x-delete1>
     <x-slot2>Save</x-slot2>
     <x-filled2>not-filled</x-filled2>
     <x-delete2>disabled</x-delete2>
     <x-filename2>...</x-filename2>
     <x-slot3>Save</x-slot3>
     <x-filename3>...</x-filename3>
     <x-filled3>not-filled</x-filled3>
     <x-delete3>disabled</x-delete3>
     <x-slot4>Save</x-slot4>
     <x-filename4>...</x-filename4>
     <x-filled4>not-filled</x-filled4>
     <x-delete4>disabled</x-delete4>
     <x-slot5>Save</x-slot5>
     <x-filename5>...</x-filename5>
     <x-filled5>not-filled</x-filled5>
     <x-delete5>disabled</x-delete5>
     <x-present>
     <table class="save-table">
     <tr><th colspan="4">Saves
         <x-button class="float-right" href="#previous">X</x-button>
     </th></tr>
     <tr><th>1.</th>
         <td><x-button class="${loc('saves').filled1}"
             href="#slot(1);exit">${loc('saves').slot1}</x-button></td>
         <td id="slot1" class="save-name">${loc('saves').filename1}</td>
         <td><x-button class="${loc('saves').delete1}"
             href="#delete-slot(1);exit" >Delete</x-button></td>
     </tr>
     <tr><th>2.</th>
        <td><x-button class="${loc('saves').filled2}"
             href="#slot(2);exit">${loc('saves').slot2}</x-button></td>
        <td id="slot2">${loc('saves').filename2}</td>
        <td><x-button class="${loc('saves').delete3}"
             href="#delete-slot(2);exit">Delete</x-button></td>
     </tr>
     <tr><th>3.</th>
         <td><x-button class="${loc('saves').filled3}"
             href="#slot(3);exit">${loc('saves').slot3}</x-button></td>
         <td id="slot3">${loc('saves').filename3}</td>
         <td><x-button class="${loc('saves').delete3}"
             href="#delete-slot(3);exit">Delete</x-button></td>
     </tr>
     <tr><th>4.</th>
         <td><x-button class="${loc('saves').filled4}"
             href="#slot(4);exit">${loc('saves').slot4}</x-button></td>
         <td id="slot4">${loc('saves').filename4}</td>
         <td><x-button class="${loc('saves').delete4}"
             href="#delete-slot(4);exit">Delete</x-button></td>
     </tr>
     <tr><th>5.</th>
         <td><x-button class="${loc('saves').filled5}"
             href="#slot(5);exit">${loc('saves').slot5}</x-button></td>
         <td id="slot5">${loc('saves').filename5}</td>
         <td><x-button class="${loc('saves').delete5}"
             href="#delete-slot(5);exit">Delete</x-button></td>
     </tr>
     <tr><td colspan="2">
         <x-button
             href="#save-disk;previous">Save to Disk</x-button></td>
         <td><x-button
             href="#load-disk;exit">Load From Disk</x-button></td>
         <td><x-button
             href="#delete-slot-all;exit">Delete All</x-button></td></tr>
     </table>
     </x-present>
     <x-default-action>
     if (obj('main-character').position == 'saves') {
         game.initSlotStates();
     }
     </x-default-action>
     <x-action id="slot">
     if (args[0]) {
         game.saveLoadSlot(args[0]);
     }
     </x-action>
     <x-action id="delete-slot">
     if (args[0]) {
         game.deleteSlot(args[0]);
     }
     </x-action>
     <x-action id="delete-slot-all">
         game.deleteSlotAll();
     </x-action>
     <x-action id="save-disk">
         game.save();
     </x-action>
     <x-action id="load-disk">
         game.load();
     </x-action>
 </x-location>
 </x-game>
 <script src="frise/js/game.js" ></script>
 </body>
 </html>
The above code can be found in the FRISE Download or Viewed Online. This story is mainly the same as the navigation bar example story. The game content area displays two counters and beneath this there are two buttons which allow one to increase their respective counts:
An image of the start location for a game with a saves Location
Unlike that game which only had a start location, this game also has a saves location which is linked to in the navigation bar.
An image of the saves location for a game with a saves Location
The saves location code first defines five blocks of four properties: saves1, filled1, filename1, delete1, ..., saves5, filled5, filename5, delete5. These blocks each correspond to a storage place for one game save in sessionStorage Here sessionStorage is a part of a browser's memory where a web site can store key-value pairs which will persist until the tab the web-site was loaded in is closed. FRISE uses the pair "slot" + game.id + slot_number (where + is concatenation) as the key, and a string representation of a game state as the value, when saving a game to sessionStorage. So in saves location's property blocks, the first group corresponds to data for slot_number with value 1, the second for slot_number 2, etc. The code at the start of saves location gives values for each slot under the assumption that no games have yet been saved. If the main-character is at the saves location, then the default-action for the saves location executes the code game.initSlotStates(); The initSlotStates() member function of game searches for all keys of the form "slot" + game.id + slot_number in sessionStorage and then adjusts the values of the saves locations saves + slot_number, filled + slot_number, filename + slot_number, delete + slot_number properties according to the game saved at that key. This code finds all such keys even if there were more than five slots, so the saves screen can be customized in more ways than the example given above. The value of saves + slot_number is "Save" if the slot is empty (so you can store a game), and "Load" if the slot has a game stored in it (so you can load that game). The value of filled + slot_number is either "filled" or "not-filled" according to if a game is stored in a slot. The filled + slot_number is used to style buttons to load games with background color blue and to style buttons to save games using background color white. filename + slot_number is the time at which the game was saved if the slot is used, and is otherwise, "..." The value of delete + slot_number is used to style the delete button for a game slot. If there is no game save, then its value is disabled so the the delete button will be disabled; otherwise, it is empty.
To present the state of the game save slots, the example above uses an HTML table element. An HTML table element typically consists of a sequence of tr elements, each used to specify one table row. A tr element in turn typically consists of a sequence of th and td elements, where a th elements is used for a table heading, and a td element contains one table data item. The table above has a class attribute save-table. This is used by frise/css/game.css to style the width, border, text-alignment, etc. properties of how the table should be drawn. The first row of the table has a button with an X on it, which when clicked goes to #previous. The #previous url is a special FRISE url which always goes back to the previous game state in the game history. The only td element in the first row uses a colspan="4" attribute to say that it should be four columns wide. The next five rows of this table each present information about one game save slot. Each uses the corresponding property block of the saves location discussed in the previous paragraph. In a given row, the Save/Load button links to #slot(slot_number);exit. Here the slot action calls game.saveLoadSlot(args[0]); to either save the current game to the clicked slot, if no game is in the slot; or load the game data that is currently in the slot, if game data is in the slot. exit is a special FRISE command which stays in the current location for the game turn, but does not add to the game history. The Delete button in a given row links to #delete-slot(slot_number);exit. Here the delete-slot action calls game.deleteSlot(args[0]) to delete any game data in the slot_number slot if present. The last row of the save table has three buttons Save to Disk, whose action calls game.save() to make launch a file menu for saving the current game state to disk, Load to Disk, whose action calls game.load() to launch a file menu for loading the current game state from disk, and Delete All, whose action calls game.deleteSlotAll() to delete all session data stored in any game save slots.
Return to table of contents .

Making an Inventory System

In many interactive stories, it is common for the main character to pick up items and use them as the game progresses. To remember what items the character currently has, there is usually an Inventory button which takes one to a location that displays the character's belongings. The example below shows how such an inventory location can be made in a FRISE game. It also illustrates how we could have written the saves location of the previous FRISE example using Javascript for loops.
 <!doctype html>
 <html lang="en">
 <head>
     <title>A FRISE Game With an Inventory</title>
     <meta charset="UTF-8" >
     <link rel="stylesheet" href="frise/css/game.css" >
 </head>
 <body onload="initGame();">
 <x-game>
 <x-main-nav>
     <h1>Pick Up Stuff Game</h1>
     <h2>by Chris Pollett</h2>
     <div><x-button href="#inventory">Inventory</x-button></div>
     <div><x-button href="#saves">Saves</x-button></div>
     <div><x-button href="#reset;room-a">Restart</x-button></div>
     <x-action id="reset">
         game.reset();
     </x-action>
 </x-main-nav>
 <x-object id="main-character">
    <x-position>room-a</x-position>
 </x-object>
 <x-object id="keys">
     <x-name>Keys</x-name>
     <x-carryable>true</x-carryable>
     <x-position>room-a</x-position>
 </x-object>
 <x-object id="wallet">
     <x-name>Wallet</x-name>
     <x-carryable>true</x-carryable>
     <x-position>room-b</x-position>
 </x-object>
 <x-location id="room-a">
     <x-present>
     <h1>Room A</h1>
     <p>You can:</p>
     <ul>
     ${loc('room-a')['pickup-list']}
     <li><a href="#room-b">Go to Room B</a></li>
     </ul>
     </x-present>
     <script type="text/default-action">
     if (obj('main-character').position == "room-a") {
         loc('room-a')['pickup-list'] = getPickUpList("room-a");
     }
     </script>
 </x-location>
 <x-location id="room-b">
     <x-present>
     <h1>Room B</h1>
     <p>You can:</p>
     <ul>
     ${loc('room-b')['pickup-list']}
     <li><a href="#room-a">Go to Room A</a></li>
     </ul>
     </x-present>
     <script type="text/default-action">
     if (obj('main-character').position == "room-b") {
         loc('room-b')['pickup-list'] = getPickUpList("room-b");
     }
     </script>
 </x-location>
 <x-location id="inventory">
     <x-present>
     <h1>Inventory</h1>
     <div>You are carrying the following items:</div>
     ${getInventoryList()}
     <div><x-button href="#${obj('main-character').old_position}"
         >Return</x-button></div>
     </x-present>
 </x-location>
 <x-location id="saves" >
     <script type="text/default-action">
     if (obj('main-character').position == 'saves') {
         let num_slots = 5;
         let save_loc = loc('saves');
         for (let i = 0; i < num_slots; i++) {
             if (typeof save_loc['filled' + i] == 'undefined') {
                 save_loc['filled' + i] = "";
                 save_loc['slot' + i] = "";
                 save_loc['filename' + i] = "";
                 save_loc['delete' + i] = "";
             }
         }
         game.initSlotStates();
         save_loc.present = [];
         let save_table = `
            <table class="save-table">
                <tr><th colspan="4">Saves
                  <x-button class="float-opposite" href="#previous">X</x-button>
            </th></tr>`;
         for (let i = 0; i < num_slots; i++) {
             let filled = save_loc['filled' + i];
             let slot = save_loc['slot' + i];
             let filename = save_loc['filename' + i];
             let delete_text = save_loc['delete' + i];
             save_table += `
             <tr><th>${i}</th>
                 <td><x-button class="${filled}"
                     href="#slot(${i});exit">${slot}</x-button></td>
                 <td id="slot${i}" class="save-name">${filename}</td>
                 <td><x-button class="${delete_text}"
                     href="#delete-slot(${i});exit" >Delete</x-button></td>
             </tr>`;
         }
         save_table += `
            <tr><td colspan="2">
                <x-button
                    href="#save-disk;previous">Save to Disk</x-button></td>
                <td><x-button
                    href="#load-disk;exit">Load from Disk</x-button></td>
                <td><x-button
                    href="#delete-slot-all;exit">Delete All</x-button></td></tr>
          </table>`;
          save_loc.present.push([null, "", false, save_table]);
     }
     </script>
     <x-action id="slot">
     if (args[0]) {
         game.saveLoadSlot(args[0]);
     }
     </x-action>
     <x-action id="delete-slot">
     if (args[0]) {
         game.deleteSlot(args[0]);
     }
     </x-action>
     <x-action id="delete-slot-all">
         game.deleteSlotAll();
     </x-action>
     <x-action id="save-disk">
         game.save();
     </x-action>
     <x-action id="load-disk">
         game.load();
     </x-action>
 </x-location>
 <x-action id="pickup">
     let object_id = args[0];
     game.moveObject(object_id, 'inventory');
 </x-action>
 <x-action id="drop">
     let object_id = args[0];
     //if at inventory location old position is where click on inventory from
     let position = obj('main-character').old_position;
     game.moveObject(object_id, position);
 </x-action>
 </x-game>
 <script src="frise/js/game.js" ></script>
 <script>
 function getPickUpList(location_id)
 {
     let list = "";
     let items = loc(location_id).items;
     for (const item_id of items) {
         let item = obj(item_id);
         if (item.hasOwnProperty("carryable")) {
             list += `<li><a href="#pickup(${item_id});${location_id}">` +
                 `Pick up ${item.name}</a></li>`;
         }
     }
     return list;
 }
 function getInventoryList()
 {
     let list = "";
     let items = loc("inventory").items;
     for (const item_id of items) {
         let item = obj(item_id);
         if (item.hasOwnProperty("carryable")) {
             list += `<li>${item.name} [<a href="#drop(${item_id});exit">` +
                 `Drop</a>]</li>`;
         }
     }
     if (list != "") {
         list = "<ul>" + list + "</ul>";
     }
     return list;
 }
 </script>
 </body>
 </html>
The above code can be found in the FRISE Download or Viewed Online. This game has three objects, the main-character, keys, and wallet; and it has four locations, room-a, room-b, saves, and inventory. The saves location looks the same as the previous example game. The other three rooms look like (depending on what has been picked up):
What Room A Looks Like Initially
What Room B Looks Like Initially
What the Inventory Looks like
The main-character starts in room-a, the keys start in room-a , and the wallet starts in room-b. Each room has a link to the other room as well as links to pick up any non-main-character objects that happen to be in the room. If an object is picked up, it is moved to the inventory location. Clicking the Inventory button in the navigation bar takes one to the inventory location where one can see the objects currently in the inventory. The inventory location has links next to each object allowing that object to be dropped into the room the main-character had been in before going to the inventory location. The inventory location also has a button to return to the previous location.
Let's now examine the code to implement the above functionality. First, because the action's and default-action's involve < and > symbols, the alternative syntax for these tags we mentioned in the Actions and Default Actions section is being used, namely,
 <script type="text/action">...</script>
rather than
 <x-action>...</x-action>
and
 <script type="text/default-action">...</script>
rather than
 <x-default-action>...</x-default-action>
Next, let's look at how the saves location code has changed from the code in the Making a Save Screen section. In this version, the HTML code doesn't have any <x-slot1>...<x-filename1>,... etc tags and it doesn't have any x-present element. Instead, the saves location default action first creates the slot + slot_number , filename + slot_number , then it calls game.initSlotStates() (as before), and finally, it dynamically creates the present data for the location. To do the first and last of these, it makes use of Javascript for loops. A for loop is a way to tell Javascript to repeat an action a certain number of times. A traditional for has the format:
 for (initial_values_of_loop_variables; 
      condition_to_stop_repeating; 
      how_to_change_variables_each_iteration) {
      code_to_execute_multiple_times
 }
So in the code above, the first for loop runs five times and creates the slot properties in saves location with empty values. The code after game.initSlotStates() , creates a string save_table with the HTML code for the save table from the earlier Save Game example. The string with the HTML for the first table row with the close button is just directly stored in save_table. The next five rows are created using a for loop to create and concatenate each slot's information a single row at a time. Finally, the Save to Disk, Load from Disk, and Delete all Slots row is concatenated. Once a string has been made for the HTML save table, we need to tell FRISE to use it as part of the presentation of the saves location. Internally, in FRISE, the contents of a location's x-present tags are represented as a property present of that location. This property is an array (a numbered sequence) of triples (check_condition, staging, contents), each triple representing one x-present. So to make a new present item for FRISE to display at the saves location, we push one more triple to the end of the array with the line:
 save_loc.present.push([null, "", save_table]);
The actions linked to from the saves location are exactly as in the previous example.
The code for the two room locations is essentially the same, execpt that in wherever Room A uses 'a' or 'A', Room B uses 'b' or 'B'; and wherever Room A uses 'b' or 'B', Room B uses 'a' or 'A'. So we will only discuss the code for Room A. The room-a location has a single x-present element. The only dynamic item on it is:
  <ul>
  ${loc('room-a')['pickup-list']}
  <li><a href="#room-b">Go to Room B</a></li>
  </ul>
the code ${loc('room-a')['pickup-list']} adds additional list items for things that can be picked up at this location. loc('room-a')['pickup-list'] is created by the default action of the room's location:
 if (obj('main-character').position == "room-a") {
    loc('room-a')['pickup-list'] = getPickUpList("room-a");
 }
Here getPickUpList is a Javascript function that is defined in <script> tags at the end of the example code:
 function getPickUpList(location_id)
 {
     let list = "";
     let items = loc(location_id).items;
     for (const item_id of items) {
         let item = obj(item_id);
         if (item.hasOwnProperty("carryable")) {
             list += `<li><a href="#pickup(${item_id});${location_id}">` +
                 `Pick up ${item.name}</a></li>`;
         }
     }
     return list;
 }
This function takes a single argument location_id (the name of a location). It then builds up a string containing the HTML to draw list items which can be picked up. To do this, it first makes a temporary variable items by looking up the game location object corresponding to location_id, getting its items property, and storing it in items. As we have mentioned before, the items property of a location is an array of all the names of game objects at that location. To cycle through these items, we use a for...of loop, this cycles through the array and does the same action, once per element of the array. In this case, for each name of an object at the location, we get the corresponding object, and store it in item. Then we check whether item has the carryable property, if it does, we concatenate onto the string list HTML code for a list item containing a link to pick up item. Notice in the example code, the objects keys and wallet each have <x-carryable>true</x-carryable> , but main-character does not, so even though main-character might be in a location, it will not have a list item to pick it up. The function getPickUpList returns the list that it makes (the line return list; ). So the code:
 loc('room-a')['pickup-list'] = getPickUpList("room-a");	 
would get the list made for room-a and would store it in the pickup-list property of room-a.
The code for a url to pick up an item has the format: #pickup(${item_id});${location_id}. So if the current room was room-a and the item was keys , this would look like #pickup(keys);room-a. This would cause FRISE to call the action pickup with args[0] set to keys. After the action was done, it would set the main-character 's location to room-a. The code in the pickup action is:
 let object_id = args[0];
 game.moveObject(object_id, 'inventory');
Here game.moveObject(object_id, location_id) is a member function of game that removes a game object with name object_id from its current location's items array and adds it to the items array of the location with name location_id (hence, moving the location of the object). So in this case, the item to be picked-up (keys, in our example) would be moved to the inventory location.
This completes the description for how the code for the two rooms works. We are, lastly, left with describing how the code for the inventory location works. The inventory location only has one x-present element which contains:
 <h1>Inventory</h1>
 <div>You are carrying the following items:</div>
  ${getInventoryList()}
  <div><x-button href="#${obj('main-character').old_position}"
   >Return</x-button></div>
The code ${getInventoryList()} executes the Javascript function getInventoryList() defined in <script> tags at the end of the code example to get a string of HTML with the items currently in the inventory location and with links next to each to allow the user to drop that item. After this list, there is a Return button with location #${obj('main-character').old_position}. Each game object in FRISE has an old_position property which is the last location the item was that wasn't the saves location. Using the main-character's old_position for Return is different than using #previous because the latter pops an element off the game history, the former adds an element to the game history -- even if both move the main-character to the same location. This is important because if we drop an item, we don't want to back up the history and immediately undrop it.
The code in getInventoryList is:
 function getInventoryList()
 {
     let list = "";
     let items = loc("inventory").items;
     for (const item_id of items) {
         let item = obj(item_id);
         if (item.hasOwnProperty("carryable")) {
             list += `<li>${item.name} [<a href="#drop(${item_id});exit">` +
                 `Drop</a>]</li>`;
         }
     }
     if (list != "") {
         list = "<ul>" + list + "</ul>";
     }
     return list;
 }
This is very similar to the code in getPickUpList. The two main differences are that the above code is always for a single location (inventory ) rather than a location given by an argument location_id and that the link url in the above code used in the list items is now #drop(${item_id});exit. So if wallet was in the inventory, there would be a link to #drop(wallet);exit to drop it. Clicking this link would execute the code in the drop action with args[0] = 'wallet' . As we have said before, exit is a special FRISE command which stays in the current location for the game turn, but does not add to the game history. The code for the drop action is:
 let object_id = args[0];
 //if at inventory location old position is where click on inventory from
 let position = obj('main-character').old_position;
 game.moveObject(object_id, position);
This moves the given game object from the inventory to the last location that wasn't the saves location that the main-character was at before it went to the inventory. This complete the description of the game with an inventory example.
Return to table of contents .

Writing a FRISE story in a Language Other Than English

If you want to write a FRISE story in a language other than English, there are only a few things you need to change (except the use of the language itself). First, the open html tag at the start of your story should be switched from <html lang="en"> to <html lang="IETF_Language_Tag_You_Want">. Wikipedia contains a list of IETF Language Tags and the language they correspond to. If you are using FRISE's save system, then there are a small number of strings that are actually output by the FRISE script in English. To change them to your desired language, you should replace the script include line
 <script src="frise/js/game.js" ></script>
with:
 <script src="frise/js/game.js" ></script>
 <script>
 locale = "IETF_Language_Tag_You_Want";
 tl[locale] = {
     "init_slot_states_load" : "Load", //translate "Load" to your language
     "init_slot_states_save" : "Save", //translate "Save" to your language
     "restore_state_invalid_game" : "Does not appear to be a valid game save"
          //translate "Does not appear to be a valid game save" to your language
 };
 </script>
If the language you want to write in is a right-to-left language, and you are using a main-nav element, then add to the end of the script element above:
 is_right_to_left = true;
Example 9C in the FRISE Download shows the result of translating our save game example to Arabic.
Return to table of contents .

Useful Abbreviations and Fields

FRISE contains a helper functions which can save on typing and make your story/game more succinct. In this section, we briefly describe them.
  • baseLoc() - this function always refers to the Location object that the main-character begins the game at. It can be useful to store global properties of your story/game at this location, then use this function to quickly access them.
  • mc() - this function is an abbreviation for obj('main-character'). Since the main character is often referred to when writing the Javascript portions of your game, this function saves a lot on typing.
  • here() - this function is an abbreviation for loc(mc().position), the Location object that the main character is currently at.
  • isHere() - this method only returns true in a default action of the Location the main character is currently at; otherwise, it returns false. Default actions run once per game turn for all objects and locations. This allows for interesting game aspects like more dynamic non-playing characters. On the other hand, some times we want the default action of a location to only execute if the main character is there. This can be achieved using isHere() and code like:
     <script type="text/default-action">
     if (isHere()) {
         //do something
     }
     </script>
    
  • some_location.visited - if some_location is a Location object, then visited will contain a count of how many turns the main character has spent at that location. So if the main character has never been to that location, this count will be 0.

Coding Strategies and Debugging Hints

In this section, we try to collect some wisdom about developing stories in FRISE and in making sure that they work as intended. Earlier in A First Interactive Story in FRISE , we described a useful basic folder structure to use with FRISE stories. One way to get this structure and an initial starting game is to just download the FRISE example folder, and delete all but one of the examples stories, then rename this story as desired and work from there. For example, if you chose story The Inventory Game, you would have already set up some initial code for both saving games and a simple inventory system.
Sometimes it is useful in a game to have a location which is used to track quantities which might be used each step of the game and whose values need to be updated each turn. If this is the case, one can create a location whose id is global which has properties for these quantities and whose default action specifies how they change.
Within the story HTML file it can be useful to group similar story elements in similar locations to make them easier to find. So one might an organization such as:
 start of HTML
  title and other head document content
  start of game
    nav bar for game
    objects of game beginning with main character
    locations in game beginning with global and start locations
    inventory location
    save location
    actions used by more than one location
  end of game
  Javascript to import frise
  Javascripts for functions used by many objects or locations
 end of html
Similarly, it can be useful to adopt a convention for the ordering of parts of game objects and locations. For game objects this could be:
 start object making sure to have an id attribute
   x-name element
   x-position element
   x-icon element (if has)
   additional object properties
   default action
 end object
And for locations:
 start location making sure to have an id attribute
   properties that you want to track for the location
   present properties - making sure location has at least one x-present element
                      - making sure HTML you want to draw is in some x-present element
   actions which might be invoked following a link from this and no other location
   default action for this location
 end location
Even with this organization, to find an object or location quickly it often makes sense to use your editor's find function and search for id="id-of-object-or-location-looking-for".
When one is creating a object or location, it can speed things up to take an existing object or location and edit it. If one does this, make sure that the first thing one does is change the id of the object or location. One should also adopt the practice that as soon as one types open part of something that has an open and close part to it to immediately type the corresponding close part before filling in the contents. Thus, if you type <x-object id="foo"> you should immediately type </x-object> before filling in the rest of the object. This will avoid issues about improperly closed or nested tags. Invariably, you will at some point still make errors such as having two objects or locations with the same id, or mis-nested, misspelt, or unclosed tags. To catch these errors, you can use the W3C Validator and select Validate by Direct Input and copy and paste your story HTML there. It will output a report of any broken HTML.
Most modern browser's come with a developer tool pane. Below is a picture of how to find this tool pane in Firefox on a Mac. A quick web search will tell you how to find it for other browsers and on other platforms.
Firefox Web Developers Tools
The Web Developer tool pane looks something like:
Firefox Web Developers Tool Pane
As we can from this image, the developer pane has several available tabs. The image shows the Inspector tab. This particular tab lets you click on HTML code presented in a tree view format and see visually in the browser window what drawn part of the browser window corresponds to it. The left hand side of this tab window shows the cascading style sheet settings currently in effect for the selected element. This tab can be very useful for debugging the visual look of your story.
Right next to the Inspector tab is the Console tab. Any errors in your Javascript as well as any console.log messages will be shown in this tab. This can often help in figuring out why clicking on a link or a button in your story doesn't work. In the Console tab one can also print out the current value of game objects and locations for the current state of the game. For example, suppose you play a couple steps into your game and want to see what are the values of the properties of the main-character . You could open up the Console tab and type:
 game.objects['main-character']
The screenshot below shows the kind of output this produces:
Echo variables in Console Tab
For locations, the analogous syntax would be game.locations['location-you-want-info-for'] . You can also test calling functions from the console. For example, typing game.debug() will call game's debug() function. This debug function prints to the console all objects that haven't been assigned a position, and all location's that don't have an x-present element.
A common problem when working on a game is to change some piece of code or an image, and then to refresh the story page but not see any change. This can happen because image content is cached or session data (FRISE's memory of the previous game state) is stale and has not refreshed. Most browser's have a key-stroke sequence which will force all elements on a page to be reloaded. For example, for Firefox on a Mac, the sequence is CMD-SHIFT-R. Alternatively, from the Network tab of the Developer Tools one can disable cache and do a reload. To delete old session data that the browser has for your story, you can go to the Storage tab in the developer tools, select Session Storage and the web page of your story and then delete each of the key-values stored under your story's location.
As a final coding and debugging hint, the Developer Pane's Accessibility Tab is useful for doing automated inspections of how easy your game is to use for people of different abilities. For example, on Firefox it has tools to simulate how you game might look to someone who is color blind. This can be very helpful in choosing color schemes for your stories to make sure that they will be visible to as wide a range of user's as possible.