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.
let poem = {
persian: “بشنو از نی چون حکایت میکند، از جداییها شکایت میکند”,
transliteration: “Beshno az ney chon hekayat mikonad, Az jodayiha shekayat mikonad”,
english: “Listen to the reed flute, how it tells a tale, complaining of separations”,
};
I wanted the words to be floating around, referring to ‘separation’, and. I want the colours / shapes to be pink, hearts just to refer to love.
I also wanted the user to be able to see the quote altogether so that they can see the final image.
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.
Outcome