Create a gist now

Instantly share code, notes, and snippets.

@dariusk /
Last active Jan 13, 2017

This lets you use the Wordnik API in Twine to get random nouns and adjectives. I didn't include verbs because conjugation sucks and I'm lazy.#TwineHacks

This Twine macro lets you use the Wordnik API to get random nouns and adjectives for your story.

See a demo in action here.

How to set it up

  • IMPORTANT FIRST STEP: you must have a Wordnik API key to use this! If you don't already have one, go here to register for an API key. If you do already have one, move on to the next step!
  • Paste the contents of the WordnikRandomWords.js file below into a new passage. Call the passage whatever you want, and add the tag "script" to it.
  • Modify the first line of the passage you just created so that instead of "var API_KEY = 'xxxxxxx'" you replace the x's with your actual API key you got from Wordnik.

How to use it

There are two parts to using this. First you need to put placeholder HTML in your text. This looks like the following:

Hello reader! I am going to tell you a random noun: <html><span class="myRandomNoun">...</span></html>. Wasn't that neat?

What you're doing is telling the macro where to put the random word. The class name ("myRandomNoun" in this case) doesn't matter -- it could be 'foo' or 'AuntBertha' or '1234' or whatever, as long as you keep it consistent in the next part.

Then, at the end of your passage, put the following:

<<randomWord myRandomNoun noun 1000>>

This is a command that says "Hey randomWord macro! Find "myRandomNoun" and replace it with a noun that has a corpus count of at least 1000." (See below for what 'corpus count' means.)

So the macro takes the form of:

<<randomWord [class] [part of speech] [minCorpusCount] [article]>>
  • class: can be any alphanumeric phrase. Is just needs to match the placeholder text in your passage.
  • part of speech: For now, only 'noun' and 'adj' are supported. Defaults to 'noun'.
  • minCorpusCount: A number from 1 to... a lot. Wordnik is always scraping the web: a 'corpus count of at least 1000' means that the word appears at least 1000 times in their records. The bigger the corpus count, the more common the word. Play around with this until you get a range of words that is to your liking. Defaults to 1000.
  • article: If this value is 'article' then it include "a" or "an" at the front of the word, as appropriate. Slightly buggy since I just check for vowels ("an university" will show up, for example).

To see a bunch of these features in action, you can look at the Twine code snippet below, which is what powers the demo.

IMPORTANT NOTE: Due to a bug in Twine 1.3.5, macros do not work on the Start passage. You should put your start passage in a new passage called "ActualStart" and then put the following code in the Start passage:

<<display ActualStart>>

This will warp you directly to ActualStart when your game starts up.

One day I saw <html><span class="randomAdjective">...</span></html> <html><span class="hereIsANoun">...</span></html>. I took it, but later that day I was feeling <html><span class="commonAdjectiveWithNoArticle">...</span></html> so I traded it for <html><span class="randomAdjective">...</span></html> <html><span class="hereIsANoun">...</span></html>.
<<randomWord randomAdjective adj 1000 article>>
<<randomWord hereIsANoun noun 1000>>
<<randomWord commonAdjectiveWithNoArticle adj 10000>>
// THIS IS IMPORANT, you need an API key from Wordnik. Sign up for one here:
var API_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
//Erik Karlsson's JSONP fetching script, from here:
JSONP = (function () {
var counter = 0,
head, window = this,
config = {};
function load(url, pfnError) {
var script = document.createElement('script'),
done = false;
script.src = url;
script.async = true;
var errorHandler = pfnError || config.error;
if (typeof errorHandler === 'function') {
script.onerror = function (ex) {
url: url,
event: ex
script.onload = script.onreadystatechange = function () {
if (!done && (!this.readyState || this.readyState === "loaded" || this.readyState === "complete")) {
done = true;
script.onload = script.onreadystatechange = null;
if (script && script.parentNode) {
if (!head) {
head = document.getElementsByTagName('head')[0];
function encode(str) {
return encodeURIComponent(str);
function jsonp(url, params, callback, callbackName) {
var query = (url || '').indexOf('?') === -1 ? '?' : '&',
callbackName = (callbackName || config['callbackName'] || 'callback');
var uniqueName = callbackName + "_json" + (++counter);
params = params || {};
for (key in params) {
if (params.hasOwnProperty(key)) {
query += encode(key) + "=" + encode(params[key]) + "&";
window[uniqueName] = function (data) {
try {
delete window[uniqueName];
} catch (e) {}
window[uniqueName] = null;
load(url + query + callbackName + '=' + uniqueName);
return uniqueName;
function setDefaults(obj) {
config = obj;
return {
get: jsonp,
init: setDefaults
// the randomWord macro. Adapted from Emmanuel Turner's article on creating Twine macros.
try {
version.extensions['randomWordMacro'] = {
major: 1,
minor: 0,
revision: 0
macros['randomWord'] = {
handler: function (place, macroName, params, parser) {
if (params[3] === undefined) params[3] = '';
if (params[2] === undefined) params[2] = 1000;
if (params[1] === undefined) params[1] = "noun";
if (params[0] === undefined) params[0] = "";
var url = '';
if (params[1] === 'adj') {
url = '' + params[2] + '&api_key='+API_KEY;
} else {
url = '' + params[2] + '&minDictionaryCount=5&excludePartOfSpeech=proper-noun,proper-noun-plural,proper-noun-posessive,suffix,family-name,idiom,affix&hasDictionaryDef=true&includePartOfSpeech=noun&limit=2&maxLength=22&api_key='+API_KEY;
JSONP.get(url, {}, function (data) {
var els = document.getElementsByClassName(params[0]);
for (var i = 0; i < els.length; i++) {
var first = data[i].word.substr(0, 1);
if (params[3] === 'article') {
var article = "a";
if (first === 'a' || first === 'e' || first === 'i' || first === 'o' || first === 'u') {
article = "an";
} else {
var article = '';
els[i].innerHTML = article + " " + data[i].word;
init: function () {
} catch (e) {
throwError(place, "randomWord Setup Error: " + e.message);
// Some code to kill the fade-in that happens, which reinitiatlizes all the text elements and messes up my script
History.prototype.originalDisplay = History.prototype.display;
History.prototype.display = function (title, link, render) {
this.originalDisplay(title, link, 'quietly');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment