Curveship

Getting Started

in Curveship-js

The first step in getting started is to read about Curveship-js concepts. The essential concepts, and their associated terms, are referred to throughout this guide.

The recommended way to write a new Curveship-js story file, narrator files, and the corresponding HTML pages is by modifying existing files. In this guide, however, we’ll pretend that we’re writing a story called “The Simulated Bank Robbery” from scratch, and then, we’ll pretend that we’re defining a narrator. We’ll then see quickly how to connect these two files with a Web page. Then we’ll turn briefly to another narrator is defined — one who, unlike the first one, is not a character in the story. And finally, you’ll be exhorted to explore the other three examples.

Beginning the Story File

Easy things are meant to be easy to do in Curveship-js. For instance, it should be easy to define a story that is like a folk tale, with straightforward story elements and little underlying complexity in terms of what fundamentally happens. This document goes line-by-line through robbery/story.js — one reasonably simple example of an underlying story — and the associated narrator file robbery/witness.js.

To write a Curveship-js story file ending in ".js" one needs only define the title, define Places, Actors, and Things, specify a sequence of Events, and then conclude with a final, standard line.

Story files should begin with a comment about the title, creator, and license. If you are modifying an existing story file, be sure to modify this part to avoid confusion.

For clarity, put the title of your story right up top, after those comments:

var title = "The Simulated Bank Robbery";

Defining Existents. After specifying the title, list your Places. Each time you add a line, you’re creating a new Place, and the required JavaScript syntax helps to indicate that:

place.vestibule = new Place();
place.lobby = new Place();
place.guardPost = new Place();
place.street = new Place();

These four places have four tags, “vestibule”, “lobby”, “guardPost”, and “street”. The tags are not what these places end up being called. Nothing in the story file indicates what they will be called. Tags are just the way to refer to these places within the story and narrator files. Tags need to be unique across all Existents. For instance, not only can there only be one Place with the tag “street” — once that tag has been used for a Place, it also cannot be used for an Actor or a Thing.

After this, list your Actors. You use the same syntax, but this time arguments are included to indicate where the Actor is and (optionally) what gender the actor is:

actor.teller = new Actor(place.vestibule, "female");
actor.robber = new Actor(place.street, "male");
actor.guard = new Actor(place.guardPost, "male");

Of the three sorts of Existents, Things come last. They are also each located somewhere. Their location can be a Place, an Actor (if an Actor is carrying them), or even another Thing (if that other Thing is a container, for instance).

thing.slip = new Thing(place.vestibule);
thing.fakeMoney = new Thing(place.vestibule);
thing.bag = new Thing(place.vestibule);
thing.mask = new Thing(actor.robber);
thing.fakeGun = new Thing(actor.robber);
thing.pistol = new Thing(actor.guard);

At the end of this sequence, we can (optionally) provide some additional information, for instance, information about Actors owning Things. This can end up having implications for the generation of possessive noun phrases, eventually:

thing.fakeGun.setOwner(actor.robber);
thing.pistol.setOwner(actor.guard);

The Event Sequence. What remains is to provide a sequence of Events, describing what happens. Events are given tags, just like Existents. They always have an agent (who is performing the action, the first argument). For “intransitive” actions, that’s usually all there is. But they can also have an object (what is acted upon). If they do, that's the second argument. Here’s the beginning of the Event sequence, where events 1, 3, 4, and 8 have direct objects:

ev.read = new Event(actor.teller, thing.slip);
ev.snooze = new Event(actor.guard);
ev.reread = new Event(actor.teller, thing.slip);
ev.coverFace = new Event(actor.robber, thing.mask);
ev.type = new Event(actor.teller);
ev.play = new Event(actor.teller);
ev.beginRobbing = new Event(actor.robber);

Events can alter Existents. This means that, at the story or content level, Places, Actors, and Things can be different before and after certain events. Not every event will make such an alteration, because Curveship-js doesn’t model every detail of the world. But, for instance, Actors moving from one place to another, or handing Things to one another, or transferring ownership of Things, can be represented.

The Event with the tag “beginRobbing” corresponds to the (fake) bank robber entering the bank lobby from the street. In Curveship-js 0.4 there are no actual implications, but in future versions the system should be automatically able to focalize different Actors, which will make use of the locations of Existents. For now, this is how to model the robber moving from the street to the lobby:

ev.beginRobbing.alters(actor.robber, "location", place.street, place.lobby);

The next event doesn’t have any associated alteration:

ev.wave = new Event(actor.teller, actor.robber);

If an Event also has an indirect object (something indirectly affected by the action), a temporal relationship needs to be specified as the third argument. Then the indirect object is the fourth argument, as seen in the next event:

ev.threaten = new Event(actor.robber, actor.teller, temporal.using, thing.fakeGun);
ev.laugh = new Event(actor.teller);
ev.wake = new Event(actor.guard);
ev.seeThreat = new Event(actor.guard, actor.robber);
ev.leavePost = new Event(actor.guard, place.guardPost);
ev.grabFake = new Event(actor.teller, thing.fakeMoney, temporal.into, thing.bag);

Note that Existents can not only be moved between Places, but also, for instance, into a Thing that is a container:

ev.grabFake.alters(thing.fakeMoney, "location", place.vestibule, thing.bag);
ev.turn = new Event(actor.robber, actor.guard);
ev.shoot1 = new Event(actor.guard, actor.robber);
ev.shoot2 = new Event(actor.guard, actor.robber);
ev.fall = new Event(actor.robber);
ev.die = new Event(actor.robber);
ev.dropGun = new Event(actor.guard, thing.pistol);

Events can alter other properties of Existents, not only their location. When the guard drops the gun, for instance, according to this underlying story, he “disowns” it. It no longer belongs to him:

ev.dropGun.reconfigures(thing.pistol, "owner", actor.guard, actor.cosmos);

This actually does have an implication in how the narrative is generated. The pistol is described using a possessive adjective before the guard drops and disowns it, using an article afterwards.

Here’s the rest of the Event sequence:

ev.regret = new Event(actor.guard, ev.shoot1);
ev.cry = new Event(actor.teller);
ev.stare = new Event(actor.guard, thing.pistol);

Wrapping Up the Story File. In a story file, the final line is always exactly this:

var world = new World(place, actor, category, thing, ev);

Categories, by the way, are an advanced, experimental, and definitely optional feature. There are none in “The Simulated Bank Robbery” and their use is not supported in the 0.4 release. But formula nevertheless requires that this last line, exactly as it appears above, is included. This builds a new storyworld with all the components that have been previously defined.

Defining a Narrator

Once the story elements are defined in story.js, you will need at least one narrator file (ending in “.js”) in the same directory. The file should be given a descriptive name, for instance “precise.js” when it defines a precise narrator and “vague.js” when it defines one who speaks generally and vaguely. This file specifies how the narrative discourse or expression is to be generated. This includes defining the way each Existent is named (what particular words are used to indicate it) as well as higher-level aspects of narration. A story file may have many narrator files. In fact, it’s the whole point of Curveship-js to show how the same underlying can be told in different ways.

Take a look at the HTML files in the example/robbery folder and note that there are two different narrator files ending with .js there. We’ll consider witness.js first.

Like story files, narrator files begins with a comment about the title, creator, and license. If you are modifying an existing narrator file, be sure to begin there and modify this part to avoid confusion.

Then, give your narrator a name or short description. Who the story is “told by” will appear in a byline under the title in the generated narrative.

var toldBy = "the bank teller";

Spin. The next section of the narrator file sets any optional spin parameters (detailed in the Technical Reference). These parameters allow a narrator to “spin” the overall narrative in different ways, somewhat like a journalist might put a particular “spin” on a news story. The particular parameters are based on narrative theory. In this narrator file, there are two spin parameters. The first one is pretty straightforward. It sets the “I” of the story to be the bank teller:

spin.i = "teller";

The second one defines a main narrative sequence that includes only a subsequence of all the events. The bank teller is only going to tell us about what she saw. She isn’t going to narrate what happened out on the street or in the guard post, which is behind a one-way mirror. So we define a sequence that allows for the sort of ellipsis (leaving things out) that we are interested in:

spin.main = "0;2;4;5-9;12-22";

This looks a little complex, but it’s the same sort of thing you would specify if you only want to print out a certain subsequence of pages from a document.

Names: Naming Existents. Next, the Names corresponding to the existents are declared. These names mutch match the tags for Existents that are declared in the story file—that’s how Curveship-js knows to connect an Existent with Names. The convention is to order the Names the same way that they appear in the story file, so the Names for Places will be first, then for Actors, and lastly, for Things.

As you can see in witness.js and other examples, “Names” is written in the plural. That’s because, in general, there can be many names used by a narrator for a single Existent.

The simplest way to name an Existent is to provide Names with one string, including an article and a noun (or noun phrase). The first time the Existent appears in the narration, that exact name will be used. After that, if the name begins with an indefinite article such as “a” or “an,” the system will use the definite article, “the.”

names.lobby = new Names("the lobby");

If you provide two strings as arguments to Names, the first one will be used on first reference and the second one (exactly as it is specified) will be used on any subsequent references.

names.street = new Names("a sidewalk outside a bank", "the sidewalk");
names.guardPost = new Names("the guard post");

You may notice that some Names are “missing” — Names are not defined for each Existent. That’s all right in many cases, for a few reasons. The bank teller never needs to refer to the vestibule that she’s in, so there is no need to define a name for that Place. Also, because the bank teller is the “I” of the story, she never gives her own name, and there is no need to have Names defined for the teller, either.

When it comes to naming the (fake) robber, this narrator file uses a special means of naming. The bank teller is in on the fake robbery and knows the supposed robber personally, remember. So of course she knows his name. By defining ProperNames a first and last name for this Actor can be specified. As shown in The Story of an Hour, a courtesey title can also be specified using ProperNames. But here, a first and last name is enough:

names.robber = new ProperNames("Jimmy", "Smith", pronoun.masculine, "my friend");

Pronouns can be specified whether you are using Names or ProperNames, and there are some interesting possibilities provided by being able to specify these at the narrative level. To get started, however, you don’t need to go into that in detail. The previous line also defines a common name for Jimmy, “my friend,” which will never get used by Curveship 0.4 but has been put in place for future versions of the system.

Actors don’t have to have ProperNames, of course. This narrator doesn’t happen to know the name of today’s guard:

names.guard = new Names("our guard", "the guard");

There shouldn’t be too many surprises about the Names assocated with Things. You will see that the bank teller, given her profession, does have a very specific name for the deposit slip:

names.slip = new Names("a completed Form D-22", "the deposit slip");
names.fakeMoney = new Names("some fake money");
names.bag = new Names("a black bag", "the bag");
names.mask = new Names("a Dora the Explorer mask", "the mask");
names.fakeGun = new Names("a gun-shaped object", "the fake gun");
names.pistol = new Names("a pistol");

Note that there are experimental features of Curveship to allow the naming of Existents according to Categories, based on what Existents are parts of, and based on the properties Existents have. You are welcome to try them and to let us know what isn’t working about them. They are not supported in this 0.4 release, however.

Verb Phrases: Representing Events. Just a narrator has idiosyncratic ways of naming each Existent, that narrator also has a particular way of representing each Event. This is done using a VerbPh for each Event. The tag assigned each VerbPh must correspond to the Event tags from the story file.

vp.read = new VerbPh("glance at");
vp.reread = new VerbPh("look over");
vp.type = new VerbPh("do some data entry");
vp.play = new VerbPh("play Solitaire");
vp.beginRobbing = new VerbPh("pretend to rob");
vp.wave = new VerbPh("wave to");
vp.threaten = new VerbPh("pose for");
vp.laugh = new VerbPh("laugh");
vp.leavePost = new VerbPh("pop out of");
vp.grabFake = new VerbPh("place");
vp.turn = new VerbPh("turn to");
vp.shoot1 = new VerbPh("shoot");
vp.shoot2 = new VerbPh("execute");
vp.fall = new VerbPh("fall");
vp.die = new VerbPh("die");
vp.dropGun = new VerbPh("drop");
vp.regret = new VerbPh("recall");
vp.cry = new VerbPh("weep");
vp.stare = new VerbPh("stare at");

A VerbPh can contain prepositions, and so could be defined as “glance at” or “wave to”. It can also include an infinitive after the main verb, as in “pretend to rob.” There are a few other longer phrases that never need tobe inflected differently and can be baked into a VerbPh; “do some data entry” is one, which is the teller’s way of saying “type”. However, a VerbPh is not a full verb phrase in the linguistic sense. It doesn’t contain any direct or indirect objects that correspond to Existents. Nor does it hold adverbs indicating the manner in which an action is done.

Finally, a narrator file always ends with a standard incantation, invoking the run function so that Curveship can realize the narration. The narrator file must end with the following line:

function run() { narrate(title, toldBy, world, spin, names, vp); }

With a story file and one initial narrator file defined, you are ready to view the output. In the next section, you'll tell Curveship how to link up the story and narrator files.

Connecting Story and Narrator with a Web Page

Curveship realizes a narrative, in HTML, using the story file and a narrator file.

To link a story file to a particular narrator file, create (or modify) an HTML file that matches the descriptive filename of the narrator file created above. In the present case, the narrator file is named “witness.js” and so the corresponding HTML file is “witness.html”.

The crucial parts of your HTML file are the last two script elements. They connect your story file with a particular narrator file:

<script src="story.js"><script>
<script src="witness.js"><script>

The rest of the HTML file is formulaic, and you don’t need to change the other references to files unless you move where those files are. Be sure, however, to change the title (in the title element) to reflect your particular story.

To view the generated output, open the HTML file in a web browser.

A Different, “Detached” Narrator

Now that you’ve seen how one narrator is defined, take a look at examples/robbery/detached.js to see how a different sort of narration can be accomplished. This file doesn’t have spin.i defined at all, because there is no “I” of the narrative. It’s told by someone who is not an Actor. There’s also no definition of spin.main because this narrator represents all of the Events. But there are spin parameters set. Check the Technical Reference entry on spin to learn more about what these do and to learn other ways spin can be set.

You can also see that this narrator has some different Names assocaited with Existents and different VerbPh specifications for Events. This narrator doesn’t have Names specified for the bank teller, but Curveship-js provides default names in this case. For an Actor who is female and an adult, “woman” is generated in the output text when Names are not specified.

This narrator has more VerbPh definitions, because this one narrates all of the Events. Narrators have a “default” or “generic” way of representing events, too, using the verb “act.” That’s almost never a good way to represent an event! So you probably don’t want to leave any Event without a corresponding VerbPh.

Further Explorations

This line-by-line explanation is meant to be of help for those new to Curveship-js, but another great way to understand the system is to study the existing examples and modify them. By seeing what your modifications do, you will be able to figure out more ways to get interesting narrative effects out of the system. Check out the other three examples in examples/ — and noodle around with them, changing story and narrator files, to see how they work.

Curveship-js