Home For Fiction – Blog

for thinking people


May 6, 2024

Let’s Make a JavaScript Poetry Assistant

Programming

creativity, javascript, poetry, programming

I’ll be honest with you: This JavaScript poetry assistant began as something meant entirely for my own use. I’m in the middle of a creative project (sneak preview: it’s relevant to Medēn), and I wanted something that would produce rhymes in real time. Indeed, the first name of this program was “Real Time Rhymer”.

Somewhere along the way, other things found their way into the program, all because I needed them. So eventually I realized, if I found this JavaScript poetry assistant useful, others might too.

Of course, the fact that it began as something I didn’t intend to share makes it a little bit Frankenstein-like. It works, but it’s a bit sloppily coded (for instance, some stuff work with jQuery, others with plain vanilla JavaScript). Its aesthetics are also a bit all over the place. And because I’m not going to spend more time working on the program itself, since I’ve got the functionality I wanted, I decided to simply share it. You can use it on this page, and the source code is also available if you want to play with it yourself.

javascript poetry assistant. Program screenshot

A JavaScript Poetry Assistant: The Program

Before I say a couple of things about the programming side, here’s the program itself. After all, if you’re not a programmer and you just want to use it, chances are that’s why you’re here.

Usual caveats apply: The applet is hosted on raw.githack which is a free service. 100% uptime cannot be guaranteed. If nothing appears in the iframe below, try later. If the problem persists, let me know.

If you prefer to use the program in a separate window, click here.

Click to run the program

A JavaScript Poetry Assistant: The Code

As I said, this JavaScript poetry assistant came to being because I needed one. You can sit thinking for hours or days for something to make, but it’s unlikely to produce anything worthwhile if you don’t have an actual need for it.

As with most of my programs revolving around language one way or another, this too uses the excellent RiTa library and the Datamuse API.

You might also rightly ask, why didn’t I just use Rhymezone or Onelook? The answer is, because I just felt like it. Both these platforms return better results than what RiTa can achieve, but RiTa’s speed and simplicity (remember, rhymes are offered real-time) give it plenty of plus points. It’s a better compromise, at least for my use.

Here’s an example of how the program takes the user input and returns rhymes:

textArea.addEventListener('keyup', (event) => {
  // Clear any previous timeout
  clearTimeout(timeoutId);

  // Set a timeout to detect user stop typing
  timeoutId = setTimeout(() => {
    updateLastWord();
  }, 500); // timeout duration

  // Check if user stopped typing or used punctuation
  if (!event.key || event.key.match(/^[^a-zA-Z]+$/)) {
    updateLastWord();
  }
});

function updateLastWord() {
	const currentWords = textArea.value.trim().split(' ');
	const lastWordStr = currentWords[currentWords.length - 1] || '';
	lastWord.innerHTML = "";
	lastWordWithoutPunctuation = lastWordStr.replace(/[^a-zA-Z]+/g, ""); // Remove punctuation from the last word
	
	//rhymes
	RiTa.rhymes(lastWordWithoutPunctuation).then(function(rhymesArray){
		if (rhymesArray.length > 6) {
			rhymesArray.length = 6;
		}
		for (let i=0;i<rhymesArray.length;i++) {
			lastWord.innerHTML += rhymesArray[i]+", ";
		}
	});
}

I’ve slightly edited some details for clarity (i.e. the actual code, as you can see on my Github page, contains a few other functionalities). With RiTa, it’s trivial to find rhymes of a word – just note that it’s an async function, requiring .then().

Using Datamuse

Besides RiTa, the program also uses the excellent Datamuse API. Here’s how it finds stress patterns:

document.getElementById("stressButton").addEventListener("click", function(){
	lastWordStress.innerHTML = "";
	let pattern = document.getElementById("thePattern").value;
	
	//metering patterns
	datamuse(lastWordWithoutPunctuation, false, false).then(function(res){
		for (let i=0;i<res.length;i++) {
			if (RiTa.stresses(res[i].word)==pattern) {
				lastWordStress.innerHTML += res[i].word + ", ";
			}
		}		
	});
});
function datamuse(word) {
    return new Promise(function(resolve, reject) {
        $.get('https://api.datamuse.com/words?rel_trg='+word')
            .done(data => resolve(data))
            .fail(error => reject(error));
    });
}

Again, the code block is slightly modified for clarity, just to show how finding stress patterns works. Also note that, since this relies on Datamuse being up and running, it can possibly slow down or stop working if the Datamuse server is overloaded.

Just Because It’s Simple Doesn’t Mean It’s not Useful

Obviously, whether it’s useful to you depends on your preferences and priorities. I think it might be, if you’re in this sort of thing (aka poetry or rhythm in general, which is as crucial for literature).

No, it’s not pretty. It doesn’t have fancy graphics of effects. Even its functionality is a bit sloppy and “less is more”. But hey, that’s what leaves room for creativity to emerge!