Home For Fiction – Blog

for thinking people

Patreon LogoPatreon

June 13, 2018

A JavaScript Iambic Pentameter Generator

Literature, Programming

computer, iambic, literature, poetry, programming, technology

First of all, before I explain why I made an iambic pentameter generator, we need to know what an iambic pentameter is.

Chances are, if you found this article you already know, so I’ll be brief. An iambic pentameter is a line of poetry consisting of five “feet”, or groups of syllables. “Penta” in Greek means “five”, so pentameter means that the line consists of five groups of syllables. The iamb refers to a pair of syllables where the first is unstressed and the second is stressed. For instance, the word desPAIR (capitals indicate the emphasis). Hence, an iambic pentameter line consists of ten syllables, of which the 2nd, 4th, 6th, 8th, and 10th are stressedTechnically, this is not necessary. What should occur is that the rest of the syllables (1st, 3rd, 5th, 7th, 9th) are not  stressed..

Here’s four lines of iambic pentameter:

And you, my sinews, grow not instant old
But bear me swiftly up. Remember thee?
Ay, thou poor ghost, whiles memory holds a seat
In this distracted globe. Remember thee?
(Hamlet, I.v.94–97)

And now, without further delay, let’s get to the why’s and how’s of the iambic pentameter generator.

iambic pentameter generator in JavaScript
The Bard would approve!

An (awfully Simple and Alpha-Stage) Iambic Pentameter Generator

The quick question as to why I made such a thing is: because I could. I know it’s not a very satisfactory reply, but when it comes to creativity, sometimes you need to prove (to yourself) that you can do something.

Here is the program:

Click to run the program

The technically-savvy will realize the program is running in an iframe. It links to raw.githack. This means that, as a free service, 100% uptime cannot be guaranteed.

Next Steps

Needless to say, this is only a proof of concept, it’s really, really “alpha”. In other words, it’s only a starting point. What I’d like to do next I will likely never bother implementing but could includes:

home for fiction

Issues

There are two issues that I’m aware of but I can’t do much to improve (at this point at least, I might come up with something).

Also check out my rhyming anapest poem generator! Interested in something with user input? Check my haiku generator!

Looking for the code for this iambic pentameter generator? There you go, you’re welcome: 😉

<!DOCTYPE html>
<html lang="en">
<head>
	<meta name="robots" content="noindex">
	<meta charset="UTF-8">
	<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"
		integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ=="
		crossorigin="anonymous" referrerpolicy="no-referrer"></script>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/rita/1.3.11/rita-full.js"></script>
</head>
<style>
body {
	color:white;
	background-color:black;
}
</style>
<body>

  <input type="button" value="Generate New" onclick="window.location.reload();">
  <br><br>
  <div id="out1" class="theText"></div><br>
	<script>
	//some declarations (parts of speech to pass to RiTa; see RiTa documentation: https://rednoise.org/rita/reference/RiTa/RiTa.randomWord/index.php and https://www.ling.upenn.edu/courses/Fall_2003/ling001/penn_treebank_pos.html)
	var jj = "jj";
	var nns = "nns";
	var nn = "nn";
	var vb = "vb";
	var dt = "dt";
	var vbd = "vbd";
	var nn = "nn";
	var vbg = "vbg";
	var rb ="rb";
	var pspr = "prp$";
	var prep = "md";
	var vbn = "vbn";
	var tempArr1 = [];
	var tempArr2 = [];
	var testingTransitiveArray = [];
	var transitiveArray_group1 = [];
	var transitiveArray_group2 = [];
	var adj1Array = [];
	var verb1Array = [];
	var adj2Array = [];
	var line1, line2, line3, line4;
	var noun1, adj1, verb1, noun2, adj2;
	
	// getting number of syllables
	[rn1, rn2, rn3] = syllable();
	[rn4, rn5, rn6, rn7] = syllable2();
	
	// getting some words with RiTa and DM
	noun1 = wordChoice(nn,2);
	noun2 = wordChoice (nns, 2);
	function getAdj1Arr() {
		return new Promise(function(resolve, reject) {
			resolve($.get('https://api.datamuse.com/words?rel_jjb='+noun1));
        });
	}
	function getVerb1Arr() {
		return new Promise(function(resolve, reject) {
			resolve($.get('https://api.datamuse.com/words?lc='+noun1));
        });
	}
	function getAdj2Arr() {
		return new Promise(function(resolve, reject) {
			resolve($.get('https://api.datamuse.com/words?rel_jjb='+noun2));
        });
	}
	function getTransitive() {
		return new Promise(function(resolve, reject) {
			resolve($.get('https://api.datamuse.com/words?lc=' + verb1 + "&sp=*&max=800"));
        });
	}
	
	getAdj1Arr().then(function(obj) {
		if ((obj != null)) {
			for (var i = 0; i < obj.length; i++) {
				adj1Array.push(obj[i].word);
            }
			adj1 = pickAdjectiveFromDM(adj1Array);
		}
		getVerb1Arr().then(function(obj) {
			if ((obj != null)) {
				for (var i = 0; i < obj.length; i++) {
					verb1Array.push(obj[i].word);
				}
				verb1 = pickVerbFromDM(verb1Array);
			}
			getAdj2Arr().then(function(obj) {
				if ((obj != null)) {
					for (var i = 0; i < obj.length; i++) {
						adj2Array.push(obj[i].word);
					}
					adj2 = pickAdjectiveFromDM(adj2Array);
				}
					getTransitive().then(function(obj){
						if ((obj != null)) {
							for (var i = 0; i < obj.length; i++) {
								testingTransitiveArray.push(obj[i].word);
							}
						
							testingTransitiveArray.forEach(nounFollows);
							console.log(transitiveArray_group1.length); // debug; feel free to remove
							console.log(transitiveArray_group2.length);// debug; feel free to remove

							if  (transitiveArray_group2.length / transitiveArray_group1.length > 3.8) {
								//this means the verb is likely intransitive. This influences are line 1 & 2 construction.
								line1 = capitalizeFirstLetter(RiTa.randomWord(pspr,1)) + " " + RiTa.randomWord(jj,1) + " " + adj1 + " " + noun1 + " " + verb1 + ",";
								line2 = wordChoice(jj, rn5) + " " + wordChoice(nns,rn4) + " " + wordChoice(vb,rn6) + " " + wordChoice(nns,rn7) + ".";
								console.log("intransitive");
							}
							else {
								line1 = capitalizeFirstLetter(RiTa.randomWord(pspr,1)) + " " + RiTa.randomWord(jj,1) + " " + adj1 + " " + noun1 + " " + verb1;
								line2 = adj2 + " " + noun2 + " " + wordChoice(rb, 2) + " " + wordChoice(vbn,2) + ".";
								console.log("transitive");
							}
							
							line3 = capitalizeFirstLetter(RiTa.randomWord(pspr,1)) + " " + RiTa.randomWord(jj,1) + " " + wordChoice(jj,rn1) + " " + wordChoice(nns,rn2) + " " + wordChoice(vb,rn3);
							line4 = wordChoice(jj, rn5) + " " + wordChoice(nns,rn4) + " " + wordChoice(vb,rn6) + " " + wordChoice(nns,rn7)+ ".";
								$('#out1').append(line1+"<br/>"+line2+"<br/>"+line3+"<br/>"+line4+"<br/>"+"<br/>");
						}
					});
			});
		});
	});


	//pick suitable adjective from DM
	function pickAdjectiveFromDM(array) {
		var word = wordChoice(jj,4); //fallback in case DM returns null;
		array.forEach(checkRiTa);
		function checkRiTa(item, index) {
			if ((RiTa.getStresses(item) == "0/0/0/1") || (RiTa.getStresses(item) == "0/1/0/0")) {
				tempArr1.push(item);
			}
		}
		if (tempArr1.length > 0) {
			word = RiTa.randomItem(tempArr1);
		}
		return word;
	}
	
	//pick suitable verb from DM
	function pickVerbFromDM(array) {
		var word = wordChoice(vbd, 2); //fallback in case DM returns null;
		array.forEach(checkRiTa);
		function checkRiTa(item, index) {
			if ((RiTa.getStresses(item) == "0/1") && (RiTa.isVerb(item))) {
				tempArr2.push(item);
			}
		}
		if (tempArr2.length > 0) {
			word = RiTa.randomItem(tempArr2);
		}
		return word;
	}	

	// word picking function
	function wordChoice(pos, syl) {
		var word = RiTa.randomWord(pos, syl);
		if ((RiTa.getStresses(word) == "0/1") || (RiTa.getStresses(word) == "0/0/0/1") || (RiTa.getStresses(word) == "0/1/0/0") ) {
			return (word);
		}
		else {
			return (wordChoice(pos, syl));
		}
	}	

	//random number. Use with getRndInteger(1,3) - for 1, 2, or 3
	function getRndInteger(min, max) {
		return Math.floor(Math.random() * (max - min + 1) ) + min;
	}
	// randomize syllable number
	function syllable() {
		var rna = getRndInteger(1,2);
		if (rna == 1) {
			var rn1 = 2;
		}
		else {
			var rn1 = 4;
		}
		var rnb = getRndInteger(1,2);
		if (rnb == 1) {
			var rn2 = 2;
		}
		else {
			var rn2 = 4;
		}
		var rnc = getRndInteger(1,2);
		if (rnc == 1) {
			var rn3 = 2;
		}
		else {
			var rn3 = 4;
		}
		if (rn1+rn2+rn3 == 8) {
			return [rn1, rn2, rn3];
		}
		else {
			return (syllable());
		}
	}
	function syllable2() {
		var rna = getRndInteger(1,2);
		if (rna == 1) {
			var rn4 = 2;
		}
		else {
			var rn4 = 4;
		}
		var rnb = getRndInteger(1,2);
		if (rnb == 1) {
			var rn5 = 2;
		}
		else {
			var rn5 = 4;
		}
		var rnc = getRndInteger(1,2);
		if (rnc == 1) {
			var rn6 = 2;
		}
		else {
			var rn6 = 4;
		}
		var rnd = getRndInteger(1,2);
		if (rnd == 1) {
			var rn7 = 2;
		}
		else {
			var rn7 = 4;
		}
		if (rn4+rn5+rn6+rn7 == 10) {
			return [rn4, rn5, rn6, rn7];
		}
		else {
			return (syllable2());
		}
	}
	
	function capitalizeFirstLetter(string) {
	  return string.charAt(0).toUpperCase() + string.slice(1);
	}
	
	function nounFollows (item, index) {
		if (RiTa.isNoun(item)) {
			transitiveArray_group1.push(item);
		}
		else {
			transitiveArray_group2.push(item);
		}
	}
	</script>
</body>
</html>

Note: You will need the RiTa library for this to work (see line 9).

Also check out my Iambic Pentameter Checker, that checks a line of poetry and (self-evidently) tells you if it’s an iambic pentameter!