Skip to content

dnsorlov/TheaterJS

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

92 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TheaterJS

TheaterJS is a typing effect mimicking human behavior.

  • Everything you need to know is demonstrated and explained in this codepen.
  • The demo is also available on the TheaterJS page.

Feel free to submit any suggestions/issues and contribute to TheaterJS.

Note: Please make sure you are not confusing this project with TheatreJS.

Usage

  var theater = new TheaterJS();
  
  theater
    .describe("Vader", .8, "#vader")
    .describe("Luke", .6, "#luke");
    
  theater
    .write("Vader:Luke.", 600)
    .write("Luke:What?", 400)
    .write("Vader:I am...", 400, " your father.");
    
  theater
    .on("say:start, erase:start", function () {
      // add blinking caret
    })
    .on("say:end, erase:end", function () {
      // remove blinking caret
    })
    .on("*", function () {
      // do something
    });

Features

Documentation

The first step is to create a new theater instance. Available options are:

name default description
erase true Whether or not to play the erase animation before writing the following speech.
autoplay true If true, plays the scenario automatically. Otherwise, you'll have to call theater.play()
locale "detect" Customize keyboards, see Keyboards & Localization.
var theater = new TheaterJS({ erase: true, autoplay: true, locale: "detect" });

Multiple actors

In TheaterJS, you can describe multiple actors, each one having its own experience. Their experience is what defines their ability to talk and play (to type actually).

theater.describe("Vader", .8, "#vader");

In this example we described a new actor named "Vader", with an experience of .8 (must be comprised between 0 and 1) and a voice "#vader". Its voice is actually what will be used to print out the speech, for Vader it's an HTML element (through a css selector).

A voice can be of two type:

  • An HTML element (or a css selector string which will result in an HTML element). The element's innerHTML is used to set its value.
  • A function that will be invoked with four arguments:
    • newValue the new speech value
    • newChar the new typed character
    • prevChar the previous character
    • speech the whole speech

Note: as for all functions called by TheaterJS, the context (this) is set to the current instance.

An actor's experience is used to calculate its speed, accuracy and invincibility.

property description
speed Defines how fast the actor types.
accuracy How often he makes mistakes.
invincibility How much characters he'll type without making any error after fixing a mistake.

Those properties can be configured independently:

theater.describe("Vader", { speed: .8, accuracy: .4, invincibility: 2 }, "#vader");

Here Vader is pretty fast but makes a lot of mistakes.

Mistakes

An actor has more or less chances to make a mistake depending of its experience. A mistake result in typing an other character than the real one. The wrong character is then erased and the correct one typed.

When an actor fixes a mistake, he'll then type x characters without any chance to make a mistake. For example, if Vader makes a mistake he will fix it and type the 8 next characters perfectly (since he has an experience of .8).

Keyboards & Localization

When making a mistake, a random character near the mistyped one is used instead. Assuming you are using a qwerty keyboard, mistyping a q would result in a a, s or w. If the mistyped character is not mapped in TheaterJS (e.g. %), it'll use a random one.

By default, TheaterJS' locale is set to "detect" which means it'll try to get user's language. Note that if there's no support for user's language, it will fallback to "en". If you do not want to use the detect feature, you can configure the locale as follows:

var theater = new TheaterJS({ locale: "fr" });

The main version of TheaterJS includes support for qwerty (en) and azerty (fr) keyboards. However, adding support for Russian would be as simple as:

TheaterJS.prototype.keyboards.ru = ["йцукенгшщзх", "фывапролджэ", "ячсмитьбюъ"];

Available locales can be found in build/locales. If you want to use russian locale, make sure to include the theater.run.js or theater.ru.min.js file.

<script src="path/to/theater.js"></script>
<script src="path/to/theater.ru.js"></script>
<script>var theater = new TheaterJS({ locale: "ru" });</script>

Available locales:

If you are interested in adding support for another language, take a look at the contributing file.

Mapping a new keyboard

A keyboard is mapped based on its physical representation. For example, below are the implementations of qwerty and azerty.

/*
  [q][w][e][r][t][y][u][i][o][p] // qwertyuiop
  [a][s][d][f][g][h][j][k][l]    // asdfghjkl
  [z][x][c][v][b][n][m]          // zxcvbnm
*/

TheaterJS.prototype.keyboards.en = ["qwertyuiop", "asdfghjkl", "zxcvbnm"];



/*
  [a][z][e][r][t][y][u][i][o][p] // azertyuiop
  [q][s][d][f][g][h][j][k][l][m] // qsdfghjklm
  [w][x][c][v][b][n]             // wxcvbn
*/

TheaterJS.prototype.keyboards.fr = ["azertyuiop", "qsdfghjklm", "wxcvbn"];

Variable speed

As humans, our speed when typing is not linear but more or less variable. Once again, it all depends on our experience. A highly experienced actor will be somewhat constant and fast while a beginner's speed will vary a lot.

Scenario creation

TheaterJS is actually about writing a scenario.

theater
  .write("Vader:I am your father.")
  .write(" For real....")
  .write(-1)
  .write(600)
  .write(function () { /* do something */ });

This example showcase 5 of the 6 possible action. Let's examine it.

Note: the write method accepts an indefinite number of arguments.

theater
  .write("Vader:Hello!")
  .write("How are you doing?");

Is equivalent to:

theater.write("Vader:Hello!", "How are you doing?");

Set actor & speech

theater.write("Vader:I am your father.");

The argument passed to the write method is a string prefixed by an actor's name. It actually adds three scenes:

scene name description
actor Set the current speaking actor to the passed one.
erase Erase the current speech value.
say Type the speech.

What if you don't set a new speech to replace the current one but want to append a speech instead?

Append speech value

theater.write(" For real...");

Here the string is not prefixed by an actor's name and creates only one scene:

scene name description
say Type the speech.

Erase x characters

theater.write(-1);

When passing a negative number to the write method, it will erase x characters of the current speech value. In this example, it would erase 1 character and Vader's speech would become "For real..." instead of "For real....".

Wait

theater.write(600);

Positive numbers create a wait scene which makes a break/pause lasting for the amount of the argument (ms).

Callback

theater.write(function () { /* do something */ });

Passing a function to write creates a call scene which calls the function when the scene is played. Remember, the context is set to the current TheaterJS instance.

Scene object

In fact, the previous snippets are just shorthands. The arguments are "parsed" and transformed into scene objects.

theater
  .write("Vader:I am your father.")
  .write(" For real....")
  .write(-1)
  .write(600)
  .write(function () { /* do something */ });

Is exactly the same as:

theater
  .write({ name: "actor", args: ["Vader"] })
  .write({ name: "erase", args: [] })
  .write({ name: "say", args: ["I am your father."] })
  .write({ name: "say", args: [" For real...."] })
  .write({ name: "erase", args: [-1] })
  .write({ name: "wait", args: [600] })
  .write({ name: "call", args: [function () { /* do something */ }] });

The scene object has two keys:

  • name (string): the scene's name is used to call the appropriate method when the scene is played.
  • args (array): an array of arguments passed to the method.

Using the shorthands are clearly funnier but also limiting. For example, what if your callback does some asynchronous task?

Asynchronous callback

Let's say you want to make the screen blink for 2 seconds before calling the next scene. In this case you'll have to pass true as a second argument to the call scene.

theater.write({ name: "call", args: [blink, true] });

When this scene is played, the execution of the scenario will be "paused". To play the next scene and continue the scenario, you will need to call this.next().

function blink () {
  var self = this; // current TheaterJS instance
  
  setTimeout(function () {
    // do something
    return self.next();
  }, 2000);
}

Events

TheaterJS has a built-in event handler.

Register event

  theater
    .on("say:start", function (event, args...) {
      console.log("a say scene started");
    })
    .on("say:end", function (event, args...) {
      console.log("a say scene ended");
    });

The value before the : is the event's scope while the other part the string is the event itself. To add a listener on several events, just separate them by a comma:

theater
  .on("say:start, erase:start", function (event) {
    // add blinking caret
  })
  .on("say:end, erase:end", function () {
    // remove blinking caret
  });

Note: use theater.on("*", function (event, realEvent, args...) {}); if you want to listen to all events.

Publish event

theater
  .emit("scope", "event", ["your", "arguments", "go", "here"])
  .emit("customEvent", ["you might not need the event part"]);

The emit method accepts up to three arguments. The first being the "scope", the second the event and the third the arguments. If you don't need to specify an event, simply skip it.

Changelog

  • 1.2.0 Adding support for html within the speeches
  • 1.1.0 Actor's speed, accuracy and invincibility can be configured independently.
  • 1.0.0 First release.

About

Typing effect mimicking human behavior.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 96.5%
  • CSS 3.5%