conye – speech

My speech project analyzes the sentiment of speech, and a flower will grow or wilt depending on whether or not the speech is flower-approved or not. I was inspired by the random experiments online where people raise 3 plants, say kind things to one of them, mean things to another and do not say anything to the 3rd plant. The plant that was fed kind words supposedly grows better, while the plant that was yelled at grows poorly while the control plant does somewhere in the middle.

Here’s an example experiment to prove that people have actually done this: https://thewholeheartedmind.wordpress.com/2012/01/31/mindful-speech-the-word-power-experiment/

While I’m not convinced that the content of words truly affects plant growth rates, I love the idea of it, so my project is a simplistic simulation of their experiment.

For example:

  • “good dirt” is flower approved.
  • “bad dirt” is flower not approved 🙁

Here is a video of how it works! (disclaimer: I’m aware “die pie” is a dumb rhyme, but I couldn’t think of anything better).

Here are some process pictures.

Toddler stages, when I was trying to get a nice looking flower.

Teenager stages, after I refined the look, but before I defined a specific plant approved language:


Gif of the dead flower (longer clip is in the video)

Overall, while I’m mostly satisfied with how it looks (although I had envisioned it with more flowers and leaves), I’m not satisfied with how it functions. I would have liked to have created a more meaningful speech system. Right now it doubles the impact if the user uses rhyming language, but that’s pretty simplistic. I thought it would have been interesting to use wordnet to generate associated words, and then the sentiment behind those words would also affect the plant, so the user would have to be very precise with their language in case they end up accidentally harming the plant. Also, I discussed the project with Golan and Claire, and they suggested making the project a two-player plant-growing game. I was excited to do so but ended up running out of time on the project.

Also, I set up my code so that I could add more flowers and stems later, but didn’t have time to implement that either, so it’s just a single generated flower.

My background is based on the p5.js pointillism example.

I’m using Rita.js, p5.speech.js and p5.func.js!
Code (not my modified Sentimood.js):

var flowerbundle, myFont;
var flowerbundle, myFont;
var leaf1, leaf2;
var img;
var e = new p5.Ease();
var g = new p5.Gen(); 
var colors, colors1, percents, percents2;
var colorarr;
var growth = 0;
var mySpeechRecognizer;
var mostRecentSpoken = "";
var previousPhrase = "";
var bloodinitialized = false;
var bloodarray = [];
 
 
//=========================================
function initializeMySpeechRecognizer(){
	mySpeechRecognizer = new p5.SpeechRec('en-US'); 
 
	// These are important settings to experiment with
	mySpeechRecognizer.continuous = true;      // Do continuous recognition
	mySpeechRecognizer.interimResults = false; // Allow partial recognition (faster, less accurate)
	mySpeechRecognizer.onResult = parseResult; // The speech recognition callback function
	mySpeechRecognizer.start(); // Start the recognition engine. Requires an internet connection!
 
	console.log(mySpeechRecognizer);
}
 
function initializeSentiment(){
	sentiment = new Sentimood();
} 
var Bundle = class{
  constructor() {
  	this.life= 10;
    this.stem = new Stem(50);
    this.flowers = [];
  }
  addFlower(colors,  number, petalLength, petalWidth, per, rot, x, y){
  	this.flowers.push(new Flower(colors,  number, petalLength, petalWidth, per, rot));
  }
  draw(){
  	this.stem.draw();
  	for(var i =0; i < this.flowers.length; i++){
  			this.flowers[i].draw();
  			if(this.flowers[i].dead && !bloodinitialized){
  				bloodinitialized = true;
  				for(var j = 0; j < 5; j++){
  					bloodarray.push(new Blood(random(-3, 3), this.flowers[i].centery - random(5, 20)));
  				}
  			}
  	}
  }
}
 
var Flower = class {
  constructor(colors,  number, petalLength, petalWidth, per, rot, x, y) {
  	this.rot = rot;
  	this.centerx = 0;;
  	this.centery = 85;
  	this.len = petalLength;
  	this.dead = false;
  	this.max = false;
  	this.width = petalWidth;
    this.colors = colors;
    this.petals = generatePetals(colors, number, petalLength, petalWidth, per);
  }
  draw(){
  	push();
  	translate(this.centerx, this.centery);
  	for(var i =0; i < this.petals.length; i++){ this.petals[i].draw(this.rot); } pop(); fill("#C2894C"); ellipse(0, this.centery, 10, 10); } updateWidth(changefactor){ if(this.len > 30){
  		this.max = true;
  	}
  	if(this.len < 1 || this.centery > 120){
  		this.dead = true;
  	}
  	if((!this.max && !this.dead) || (this.max && changefactor < 0)){
  		 this.len += changefactor * 0.05;
  		 this.centery -=changefactor;
  		 for(var j = 0; j < this.petals.length; j++){
			var p = this.petals[j].lines;
			for(var k = 0; k< p.length; k++){
				p[k].y2 += changefactor * 0.1;
				p[k].regenerate();
			}
		}
  	}	
  }
};
 
function updateBlooddrop(drops){
		for(var i = 0; i < drops.length; i++){ drops[i].y++; if(drops[i].y > 120){
				drops[i].y = flowerbundle.flowers[0].centery - random(5, 20);
				drops[i].x += random(-3, 3);
				if(Math.abs(drops[i].x) > 17){
					drops[i].x = 0;
				}
			}
			drops[i].draw();
		}
}
 
var Blood = class{
	constructor(x, y){
		this.x = x;
		this.y = y;
	}
	draw(){
		noStroke();
		fill("#a31818");
		triangle(this.x, this.y, this.x - 2.5, this.y + 10, this.x + 2.5, this.y + 10);
		ellipse(this.x , this.y + 10, 5, 5);
	}
}
 
var Stem = class{
	constructor(height){
		// this.curves = 
		this.height = height;
		this.max = false;
		this.min = false;
		this.x = 0;
		this.y = 85;
	}
	updateHeight(changefactor){
		if(this.height > 145){
  			this.max = true;
  		}
  		if(this.height < 10){
  			this.min = true;
  		}
  		if((!this.max && !this.min) || (this.max && changefactor < 0)){
  		 	this.height += changefactor;
  		 	this.y -= changefactor;
  		}	
	}
	draw(){
		push();
		translate(this.x, this.y);
		stroke("#84B584");
		for(var i = 0; i < this.height; i++){
			//stroke(51 + 50 * noise(i/50.0), 61 + 50 * noise(i/50.0), 39 + 50 * noise(i/50.0));
 
			strokeWeight(14 * noise(i/50.0));
			point(5 * noise(i/50.0), i);
		}
		// push();
		// image(leaf1, -70, this.height/3, 70, 70);
		// rotate(radians(90));
		// image(leaf2, 0, this.height/4 - 85, 70, 70);
		// pop();
		pop();
	}
}
 
function generatePetals(colors, number, petalLength, width, per){
	var arr = [];
	var degreeStep = 360 / number;
	for(var i = 0; i < number; i ++){
		arr.push(new Petal(degreeStep * i + random(20), colors, petalLength, width, per));
	}
	return arr;
}
 
var Petal = class {
  constructor(rot, colors, len, wid, per) {
  	//int rotation, line array lines, color array colors
 
    this.rotation = rot;
    this.len = len + random(-30, 30);
    this.wid = wid;
    this.lines = generateLines(colors, len, wid,  per);
  }
  draw(rot){
  	push();
  	rotate(radians(this.rotation + rot));
  	translate(-1 * this.wid/2, 0);
  	for(var i = 0; i < this.lines.length; i++){
			this.lines[i].draw();
		}
	pop();
  	}
};
 
// var colors = [color(255, 0, 0), color(0, 0, 255)];
 
 
var Line = class{
	constructor(piecepercents, colors, x, y1, y2){
		this.piecepercents = piecepercents;
		this.colors = colors;
		this.x = x;
		this.y1 = y1;
		this.y2 = y2;
		//array of linePieces
		this.pieces = generatePieces(piecepercents, colors, x, y1, y2);
	}
	regenerate(){
		this.pieces = generatePieces(this.piecepercents, this.colors, this.x, this.y1, this.y2);
	}
	draw(){
		for(var i = 0; i < this.pieces.length; i++){
			this.pieces[i].draw(this.x, this.colors, i);
		}
	}
}
 
function generateLines(col, len, wid, per){
	var arr = [];
	var rand = 0;
	for(var i = 0; i < wid; i++){
		var y1 = -len * g.window(map(i/(wid + 0.0), 0, 1, 0.2, 0.8), "tukey", 0.9) + len;
		var y2;
		rand += random(-0.25, 0.25);
		if(i < 15){
			y2 = len * e.backOut(map(i/(wid + 0.0), 0, 0.5, 0, 1)) + len;
		}
		else{
			y2 = len * e.backOut(map(i/(wid + 0.0), 0.49, 1, 1, 0)) + len;
		}
		y2 += rand;
		arr.push(new Line(per, col, i, y1, y2));
	}
	return arr;
}
 
//returns pieces that make up the a single line
function generatePieces(piecepercents, colors, x, y1, y2){
	//length of both arrays must be the same or you die
	//y1 is the lower one
	var len = y2 - y1;
	var res = [];
	for(var i = 0; i < piecepercents.length; i++){
		res.push(new LinePiece(y1 + y2 * piecepercents[i], y2));
	}
	return res;
}
 
var LinePiece = class{
	constructor(y1, y2){
		this.y1 = y1;
		this.y2 = y2;
	}
 
	draw(x, colors, index){
		strokeWeight(2);
		color = colors[index];
		if(index == colors.length - 1){
		}
		else{
			for(var i = this.y1; i < this.y2; i+=4){
				var r = Math.floor(map(i, this.y1, this.y2, colors[index][0], colors[index + 1][0]));
				var g = Math.floor(map(i, this.y1, this.y2, colors[index][1], colors[index + 1][1]));
				var b = Math.floor(map(i, this.y1, this.y2, colors[index][2], colors[index + 1][2]));
				stroke(r, g, b, 150);
				line(x, this.y1 + i - 10, x, this.y1 + i - 5);
			}
		}
	}
}
 
function preload() {
 	leaf1 = loadImage('leaf1.png');
 	leaf2 = loadImage('leaf2.png');
 	img = loadImage('background.png');
 	myFont = loadFont('Karma-Bold.ttf');
}
 
function setup(){
	createCanvas(600, 500);
	background(255);
	mostRecentSpokenWord = "";
	initializeMySpeechRecognizer(); 
	initializeSentiment();
	noStroke();
	textFont(myFont);
	textSize(15);
	imageMode(CENTER);
  	img.loadPixels();
  	var pointillize = 75;
  	for(var i = 0; i < 1000; i++){
	  		var x = floor(random(img.width));
	  		var y = floor(random(img.height));
	  		var pix = img.get(x, y);
	  		fill(pix, 80);
	  		ellipse(x * 2, y*3, pointillize, pointillize);
  	}
 
	colors2 = [[146, 139, 121], [251, 142, 125], [244, 232, 196], [251, 142, 125], [228, 213, 179]];
 
	colorarr = [colors2];
 
	percents = [.1, .2, .3, 0.6, 0.9];
	percents2 = [.4, .3, .1, 0.8, 0.9];
 
	flowerbundle = new Bundle();
	flowerbundle.addFlower(random(colorarr), 5, map(random(), 0, 1, 5, 10), 40, percents, random(-20, 20), 0, 0);
}
 
function isRhy(arr){
	for(var i = 0; i < arr.length; i++){
		for(var j = i + 1; j < arr.length; j++){
			if(	RiTa.isRhyme(arr[i], arr[j])){
				return true;
			}
		}
	}
	return false;
}
 
function draw(){
	if(flowerbundle.flowers[0].dead){
		fill("#403230");
		drawBackground();
		fill(0);
		text("Your flower is dead.", 10, height - 20);
 
		noStroke();
		push();
		translate(200, 200);
 
		if(growth != undefined){
			for(var k = 0; k < colorarr.length; k++){
				temparr = colorarr[k];
				for(var i = 0; i < temparr.length; i++){
					if(growth < 0 || 
						(temparr[i][0] < 255 && temparr[i][1] < 255 && temparr[i][2] < 255)){
					for(var j = 0; j < temparr[0].length; j++){ temparr[i][j] += growth; } } } } for(var i = flowerbundle.flowers.length - 1; i >= 0; i--){
			var flow = flowerbundle.flowers[i];
				flow.updateWidth(growth * 0.05);
				flowerbundle.stem.updateHeight(growth * 0.05);
				flow.draw();
		}
		flowerbundle.draw();
	}
 
	updateBlooddrop(bloodarray);
	pop();
	return;
	}
 
	if(mySpeechRecognizer.resultString != previousPhrase){
 		mostRecentSpoken = mySpeechRecognizer.resultString;
 		previousPhrase = mostRecentSpoken;
 		yposition = 0;
 		xposition = width / 2;
 	}
 	if(frameCount % 480 == 0){
 		initializeMySpeechRecognizer();
 	}
 
 	drawBackground();
	noStroke();
 
	if(mostRecentSpoken!=undefined){
			growth = sentiment.analyze(mostRecentSpoken).score;
			var wordarr = mostRecentSpoken.split(' ');
 
			fill("#403230");
			text("Speech detected: " + mostRecentSpoken, 10, height - 20);
			var addon = "";
			if( isRhy(wordarr)){
				growth *= 2;
				addon += " Rhyme detected! Impaced doubled!";
			}
			fill("#447f9c");
			text(addon, 300, height - 20);
	}
	else{
		fill("#403230");
		text("No Speech Detected.", 10, height - 20);
	}
 
	noStroke();
	translate(200, 200);
 
	if(growth != undefined){
			for(var k = 0; k < colorarr.length; k++){
				temparr = colorarr[k];
				for(var i = 0; i < temparr.length; i++){
					if(growth < 0 || 
						(temparr[i][0] < 255 && temparr[i][1] < 255 && temparr[i][2] < 255)){
					for(var j = 0; j < temparr[0].length; j++){ temparr[i][j] += growth; } } } } for(var i = flowerbundle.flowers.length - 1; i >= 0; i--){
			var flow = flowerbundle.flowers[i];
				flow.updateWidth(growth * 0.05);
				flowerbundle.stem.updateHeight(growth * 0.05);
				flow.draw();
		}
		flowerbundle.draw();
	}
 
	fill(73, 33, 107, 100);
	noStroke();
	updateBlooddrop(bloodarray);
}
 
function drawBackground(){
	noStroke();
	if(frameCount%20 == 0){
		imageMode(CENTER);
  		img.loadPixels();
  		var pointillize = 75;
  		for(var i = 0; i < 300; i++){
	  		var x = floor(random(img.width));
	  		var y = floor(random(img.height));
	  		var pix = img.get(x, y);
	  		fill(pix, 80);
	  		ellipse(x*2, y*3, pointillize, pointillize);
  		}
	} 
 
	fill("#946746");
	rect(100, 0, 140, height);
 
	fill("#946746");
	rect(0, height - 140, width, 140);
 
	fill("#AC8471");
	rect(130, 0, 20, height);
	fill("#9D7D6B");
	rect(140, 0, 50, height);
	rect(0, height - 120, width, 20);
 
 
	fill("#F2D7BE");
	rect(0, height - 100, width, 100);
	drawVase();
}
 
function drawVase(){
	push();
	translate(200, 200);
	//var brown = color("634A4A");
	fill(0, 0, 0, 100);
	push();
	rotate(radians(15));
	translate(68, -3);
	ellipse(10, 225, 110, 45);
	pop();
 
	fill("#C7B0A4");
	triangle(-50, 150, -35, 220, -35, 150);
	triangle(50, 150, 35, 220, 35, 150);
 
	ellipse(0, 160, 120, 20);
	ellipse(0, 130, 120, 20);
 
	fill("#C7B0A4");
	rect(-35, 150, 70, 70);
	rect(-60, 130, 120, 30);
	ellipse(0, 220, 70, 25);
 
	fill("#473F3B");
	ellipse(0, 135, 115, 20);
	stroke(255, 255, 255, 30);
	strokeWeight(10);
	line(-35, 180, -25, 210);
 
	stroke("#84B584");
	translate(-25, 115);
	for(var i = 0; i < 20; i++){
		strokeWeight(10 * noise(i/50.0));
		point(5 * noise(i/50.0), i);
	}
	fill("#84B584");
	triangle(0, 0, -8, -10, 8, -10);
	pop();
}
 
function parseResult() {
	mostRecentSpoken = mySpeechRecognizer.resultString;
}