Inspiration
For this week, I was really struggling on what to do I was prompting with Claude AI and decided to go with the generative text option. I knew I wanted to do something with a different language and then decided to go with my mother-tongue, Farsi. Persian poetry is famous and so I wanted to choose a quote that had a theme, replicate that theme in p5.js with shapes and colours.
My name in Farsi ‘خاطره’ means memory, remembrance, longing – something along those lines. It was difficult to find old poetry with that word as it is more modern but I liked the theme of memories and love so I went with the following line from Rumi – Masnavi (مثنوی معنوی)
Rumi is a 13th century poet and was known for his work regarding mystical themes in Islam (Sufism). Majority of his work is in Persian and very famous.
Idea
This was the quote I went with.
Heart
There is no heart shape in p5.js so I was looking at different examples with math function and came across this link. The artist uses sin and cos functions to connect the hearts. https://editor.p5js.org/marlontenorio/sketches/M_BGUpfKL
I edited it to suit the image in my mind. I made it a function with the size parameter because I wanted the heart to have some sort of pounding effect.
drawHeart(size) { beginShape(); let s = size * 0.5; for (let t = 0; t < TWO_PI; t += 0.1) { let x = s * (16 * pow(sin(t), 3)) * 0.03; let y = -s * (13 * cos(t) - 5 * cos(2*t) - 2 * cos(3*t) - cos(4*t)) * 0.03; vertex(x, y); } endShape(CLOSE); }
Interactivity
Click Mouse
I wanted the user to be able to add words or hearts when the mouse is pressed.
function mousePressed() { hearts.push(new Heart()); texts.push(new Text(mouseX, mouseY)); // Limit elements if (hearts.length > 15) hearts.shift(); if (texts.length > 15) texts.shift(); }
I added a limit so the program won’t be too complex. The words added would be from either the Persian, transliteration, or English – and at the point of the mouse. The hearts would simply be added randomly on the canvas.
SpaceBar – pressed
I wanted a way for users to add more words to screen, but a bunch of words. So I added the keyPressed function for the spacebar so that they user can see more words at once.
function keyPressed() { if (key === ' ') { for (let i = 0; i < 3; i++) texts.push(new Text()); }
A / a – pressed
I wanted a way for the user to see the 3 versions of the poem at once, so I just used the A button as that trigger.
I have a Text class that will choose random words from the 3 lines verses and then have the floating simulation similar to the hearts. I also wanted these words to have some lifespan and disappear into the background slowly to reference the theme of the separation and memory.
class Text { constructor(x = random(100, width-100), y = random(100, height-100)) { this.x = x; this.y = y; this.text = this.getRandomText(); this.size = random(14, 20); this.life = 0; this.maxLife = random(400, 800); this.dx = random(-0.3, 0.3); this.dy = random(-0.2, 0.2); }
Here is the display function and how it uses alpha to dictate the opacity. Depending on the life value, the opacity of the text changes.
display() { let alpha = this.getAlpha(); if (alpha <= 0) return; push(); textAlign(CENTER, CENTER); textSize(this.size); fill(340, 50, 90, alpha); noStroke(); text(this.text, this.x, this.y); pop(); } // calculate the opacity value for fading getAlpha() { let ratio = this.life / this.maxLife; // value between 0 and 1 // fade in at the first 20% of life if (ratio < 0.2) return map(ratio, 0, 0.2, 0, 0.8); // fade out at the last 20% of life if (ratio > 0.8) return map(ratio, 0.8, 1, 0.8, 0); // stay mostly visible in the middle return 0.8; }
Complete poem texts
Whenever the ‘a’ button is pressed, I would add the whole lines of poetry to the texts variable so that it would float around exactly like the singular words. I also added the life parameter as the same so that they disappear at the same time.
function showCompletePoem() { texts = []; let centerX = width / 2; let startY = height / 2 - 60; // add complete poem texts let persian = new Text(centerX, startY); persian.text = poem.persian; persian.size = 24; persian.maxLife = 1200; texts.push(persian); let transliteration = new Text(centerX, startY + 50); transliteration.text = poem.transliteration; transliteration.size = 18; transliteration.maxLife = 1200; texts.push(transliteration); let english = new Text(centerX, startY + 100); english.text = poem.english; english.size = 20; english.maxLife = 1200; texts.push(english); }
It is also important to remove ‘old’ texts from the array once their life is ‘complete’. This gets checked in the draw function.
// remove old texts texts = texts.filter(text => text.life < text.maxLife);
Next time
Next time, I would definitely want to add some flute noises to really tie the piece together. Next time I should probably add some message to indicate which key / mouse action equates to each action.