Week 1: Self Portrait
Concept
I wanted to create a self-portrait that reflected my at times chaotic attention span, along with my curly hair. I created a relatively simple portrait, but spent a lot of time tweaking the curls and the eyes (that represent my constantly shifting attention).
Process
Constructing my self-portrait with P5.js was a relatively iterative process, that required bouncing between the provided reference manual and the IDE itself.
Highlight: One of my favorite parts of code is how the curls are drawn. While I was considering what approaches may work, I realized that my multi-variable calculus class could come in handy, representing spirals as parametric equations.
function drawSpiral(centerX, centerY, scale, strokeThickness) {
let numPoints = 1000;
let maxT = 6 * PI;
let step = maxT / numPoints;
stroke(0);
strokeWeight(strokeThickness);
for (let t = 0; t <= maxT; t += step) {
let x = scale * sin(t) * t;
let y = scale * cos(t) * t;
point(centerX + x, centerY + y);
}
}
Improvements: A potential improvement I would like to implement in the future is to randomize the curls in a more organic way, rather than generating uniformly-random points. For instance, they could be generated using my hair semi-circle, with semi-randomly deviations from a perfect line.
Another potential improvement may be to produce an algorithm that can more efficiently draw the rotating spirals. In it’s current implementation, it appears to be quite computationally expensive, and slows down if too many curls are present.
Code
let size = 600;
let midX;
let midY;
let curlNums = 30;
let curlSpeeds;
let curlSizes;
let curlThiccs;
function setup() {
createCanvas(size, size);
midX = width / 2;
midY = height / 2;
curlSpeeds = generateRandomArray(curlNums, -5, 5);
curlSizes = generateRandomArray(curlNums, 1, 2);
curlThiccs = generateRandomArray(curlNums, 2, 5);
curlXs = generateRandomArray(curlNums, 150, 450);
curlYs = generateRandomArray(curlNums, 75, 200);
}
function generateRandomArray(length, min, max) {
return Array.from({ length }, () => getRandomInRange(min, max));
}
function getRandomInRange(min, max) {
return Math.random() * (max - min + 1) + min;
}
function drawSpinningSpiral(
centerX,
centerY,
scale,
strokeThickness,
rotationSpeed
) {
push();
translate(centerX, centerY);
rotate(frameCount * rotationSpeed * 0.01);
drawSpiral(0, 0, scale, strokeThickness);
pop();
}
function drawSpiral(centerX, centerY, scale, strokeThickness) {
let numPoints = 1000;
let maxT = 6 * PI;
let step = maxT / numPoints;
stroke(0);
strokeWeight(strokeThickness);
for (let t = 0; t <= maxT; t += step) {
let x = scale * sin(t) * t;
let y = scale * cos(t) * t;
point(centerX + x, centerY + y);
}
}
function draw() {
background(255);
// Head
noStroke();
fill("#FFD58C");
let headSize = size * 0.6;
let headSizeW = headSize * 0.95;
let headSizeH = headSize * 1.1;
ellipse(midX, midY, headSizeW, headSizeH);
// Eyes
fill(0);
let eyeSize = headSize * 0.1;
// Movement
let xOffset = 5 * cos(frameCount / 10);
let yOffset = 5 * sin(frameCount / 10);
// Left Eye -- todo - ovals
let leftEyeX = midX / 1.2 + xOffset;
let leftEyeY = midY / 1.2 + yOffset;
circle(leftEyeX, leftEyeY, eyeSize);
// Right eye
let rightEyeX = size - leftEyeX;
let rightEyeY = leftEyeY;
circle(rightEyeX, rightEyeY, eyeSize);
// Eyebrows
let eyeBrowWidth = eyeSize;
let eyeBrowHeight = eyeSize * 0.2;
let eyeBrowThicc = eyeSize * 0.2;
noFill();
stroke(0);
strokeWeight(eyeBrowThicc);
// Left eyebrow
let leftEyeBrowX = leftEyeX;
let leftEyeBrowY = leftEyeY - eyeBrowWidth;
arc(leftEyeBrowX, leftEyeBrowY, eyeBrowWidth, eyeBrowHeight, PI, 2 * PI);
// Right eyebrow
let rightEyeBrowX = rightEyeX;
let rightEyeBrowY = rightEyeY - eyeBrowWidth;
arc(rightEyeBrowX, rightEyeBrowY, eyeBrowWidth, eyeBrowHeight, PI, 2 * PI);
// Nose bar
let noseHeight = headSize * 0.08;
let noseTopOffset = headSize * 0.04;
let noseTopX = midX;
let noseTopY = midY + noseTopOffset;
let noseBottomX = midX;
let noseBottomY = noseTopY + noseHeight;
let noseThicc = headSize * 0.02;
strokeWeight(noseThicc);
line(noseTopX, noseTopY, noseBottomX, noseBottomY);
// Nose arc
let noseArcSize = headSize * 0.06;
let noseArcLeftX = noseBottomX - noseArcSize * 0.5;
let noseArcLeftY = noseBottomY;
let noseArcWidth = noseArcSize;
let noseArcHeight = noseArcSize;
arc(noseArcLeftX, noseArcLeftY, noseArcWidth, noseArcHeight, 0, PI);
// Smile arc
let smileArcSize = headSize * 0.4;
let smileYOffset = headSize * 0.25;
let smileArcLeftX = midX;
let smileArcLeftY = midY + smileYOffset;
let smileArcWidth = smileArcSize;
let smileArcHeight = smileArcSize / 2;
arc(smileArcLeftX, smileArcLeftY, smileArcWidth, smileArcHeight, 0, PI);
// Hair
let hairTopX = midX;
let hairTopY = midY;
let hairWidth = headSize;
let hairHeight = headSize / 2;
let hairThicc = headSize / 10;
stroke(20);
strokeWeight(hairThicc);
arc(hairTopX, hairTopY, headSizeW, headSizeH, 1.1 * PI, 1.9 * PI);
// Curls
let curlScale = 2;
let curlThicc = 7;
let curlSpeed = 3;
for (let i = 0; i < curlNums; i++) {
drawSpinningSpiral(
curlXs[i],
curlYs[i],
curlSizes[i],
curlThiccs[i],
curlSpeeds[i]
);
}
}