Bobbie VanBatenburg โœฆ Multimedia Designer

p5.js is a JavaScript library that makes the browser into a canvas.
It’s built for creative coding: generative patterns, interactive visuals, and experiments that don’t quite have a category yet.

Exactly my kind of tool.


Project Brief : Iterative Foundry MultiLogo

Created as a logo that depicts my creative work as a brand, the Iterative Foundry MultiLogo generates different types of logos using various shapes, colors and fonts, illustrating my love of randomness the iterative nature of design.

View Code
JavaScript
let color1;
let color2;
function setup() {
createCanvas(windowWidth, windowHeight);
frameRate(1);
rectMode(CENTER);
}
function preload() {
logoFont1 = loadFont('RubikGlitch-Regular.ttf');
logoFont2 = loadFont('ClimateCrisis-Regular-VariableFont_YEAR.ttf')
logoFont3 = loadFont('Notable-Regular.ttf')
logoFont4 = loadFont('Jost-VariableFont_wght.ttf');
logoFont5 = loadFont('Amarante-Regular.ttf');
logoFont6 = loadFont('Barrio-Regular.ttf');
logoFont7 = loadFont('ChelseaMarket-Regular.ttf');
logoFont8 = loadFont('Inconsolata-VariableFont_wdth,wght.ttf');
logoFont9 = loadFont('RubikMicrobe-Regular.ttf');
logoFont10 = loadFont('YatraOne-Regular.ttf');
}
// determines at random which logo to display
function whichLogo() {
let sum = windowWidth + windowHeight;
console.log(sum/20);
let logoRandomizer = floor(random(100));
fill(color(whichColor()))
translate(windowWidth/2, windowHeight/2)
if (logoRandomizer <= 25) {
return {
logoTranslate : 70,
logoFill : color1,
logoShape : ellipse(0, 0, sum/4),
logoText : "IF",
fontSize : 200,
textColor : whichColor()
}
} else if (logoRandomizer > 25 && logoRandomizer <= 50) {
return {
logoTranslate : 0,
logoFill : whichColor(),
logoShape : rect( 0, 0, sum/ 2.5, sum/13),
logoText : "ITERATIVE FOUNDRY",
fontSize : random(sum/35, sum/45),
textColor : whichColor()
}
} else if (logoRandomizer > 50 && logoRandomizer <= 75) {
return {
logoTranslate : 0,
logoFill : whichColor(),
logoShape : rect( 0, 0, sum/ 3, sum/6),
logoText : "ITERATIVE\nFOUNDRY",
fontSize : random(sum/20,sum/25),
textColor : whichColor(),
}
} else {
return {
logoTranslate : 0,
logoShape : null,
logoText : whichText(),
logoFill : null,
fontSize : random(sum/25, sum/35),
textColor : whichColor(),
}
}
}
// determines which font to apply
function whichFont() {
let fontRandomizer = floor(random(10));
if (fontRandomizer >= 0 && fontRandomizer < 1) {
return {
font : logoFont1,
weight : null
}
} else if (fontRandomizer >= 1 && fontRandomizer < 2) {
return {
font : logoFont2,
weight : floor(random(300, 700))
}
} else if (fontRandomizer >=2 && fontRandomizer < 3) {
return {
font : logoFont3
}
} else if (fontRandomizer >=3 && fontRandomizer < 4) {
return {
font : logoFont4
}
} else if (fontRandomizer >=4 && fontRandomizer < 5) {
return {
font : logoFont5
}
} else if (fontRandomizer >=5 && fontRandomizer < 6) {
return {
font : logoFont6
}
} else if (fontRandomizer >=6 && fontRandomizer < 7) {
return {
font : logoFont7
}
} else if (fontRandomizer >=7 && fontRandomizer < 8) {
return {
font : logoFont8
}
} else if (fontRandomizer >=8 && fontRandomizer < 9) {
return {
font : logoFont9
}
} else {
return {
font : logoFont10
}
}
}
// determines which text to display
function whichText() {
fill(color(whichColor()));
let textRandomizer = floor(random(10));
if (textRandomizer >= 0 && textRandomizer < 3) {
return 'IF'
} else if (textRandomizer >= 3 && textRandomizer < 7) {
return 'Iterative\nFoundry'
}
else {
return 'Iterative Foundry'
}
}
// determines whether or not to apply a stroke
function whichStroke() {
let strokeRandomizer = random(10);
if (strokeRandomizer >=5) {
return noStroke()
} else {
return null
}
}
// determines which color to apply
function whichColor() {
let colorRandomizer = floor(random(10));
if (colorRandomizer == 0) {
return '#E35E4650'
} else if (colorRandomizer == 1) {
return '#A9463450'
} else if (colorRandomizer == 2) {
return '#731B2850'
} else if (colorRandomizer > 2 && colorRandomizer <= 8) {
return '#2B5D7550'
} else {
return '#f5f1f0'
}
}
function draw() {
background(245,241,240);
whichStroke();
let logoData = whichLogo();
let fontData = whichFont();
let textData = logoData.logoText;
let whichColor1 = color(whichColor());
let whichColor2 = logoData.logoFill;
let fontColor = whichColor1;
}

Project Brief: Solar System

Warning. This one might crash your computer.

Beginning in the core of the sun, this nearly-to-scale representation of the Solar System begins in the heart of the sun. Zoom out and use your mouse to view our neighboring planets, the asteroid belt and the Kuiper Belt.

View Code
JavaScript
//we're gonna create a bunch of objects for the
//asteroid belt and we're gonna store them in an array
let ABOs = [];
let numABOs;
let heightABOs;
let sizeABOs;
//gonna also create an array of trans-Neptunian
//objects in the Kuiper Belt
let TNOs = [];
let numTNOs;
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL);
//we want somewhere between 1500-3000
//objects in the asteroid belt
numABOs = random(1500, 3000);
//we need to assign some values to make
//each asteroid object unique,
for (let i = 0; i < numABOs; i++) {
//distributes the ABOs around the circle
let angleABOs = random(TWO_PI);
//places that circle between mars & jupiter
let radiusABOs = random(1304, 2010);
//creates a varying height for the objects
//in the belt so it's not a flat disk
let heightABOs = random(-120,120);
//there are more smaller objects in the
//asteroid belt so we will make it less
//likely that the randomly-assigned
//size is unrealistically large
let sizeRandomizerValue = random(0, 100);
if (sizeRandomizerValue < 90) {
sizeABOs = random(0.01, 0.3)
}
if (sizeRandomizerValue >= 90 && sizeRandomizerValue <= 95) {
sizeABOs = random(0.3, 0.5)
}
if (sizeRandomizerValue > 95) {
sizeABOs = random(0.6, 0.8)
}
//add the objects to the array with their
//unique data points for location & size
ABOs.push({
//this section was supported by ChatGPT.
//I don't have a deep understanding of how
//cos and sin relate, but I think it's part of
//helping it move in circular direction
x: radiusABOs * cos(angleABOs),
z: radiusABOs * sin(angleABOs),
y: heightABOs,
size: sizeABOs
})
}
//we want somewhere between 1000-1500
//Trans-Neptunian objects in our Kuiper Belt
numTNOs = random(1000, 1500);
//we use the loop to generate them
for (let i = 0; i < numTNOs; i++) {
//distribute the TNOs around a circle
let angleTNOs = random(TWO_PI);
//the location of the belt will be beyond Neptune
let radiusTNOs = random(5000, 8000);
//this creates a thickness to the belt
//so they don't all sit in a thin disk
let heightTNOs = random(-40, 40);
//MY ORIGINAL COMMENTS:here we are assigning the unique data
//points to the TNOs and pushing them into our array
//CHATGPT COMMENT REVISION: I checked to make sure I understood--
//Create a new TNO with randomly generated orbital data.
// Convert its random angle and radius into x/z coordinates to place
// it somewhere along the Kuiper Belt ring. The y-value sets its
// slight vertical offset, and size gives each object its own scale.
// Then push this fully-defined TNO into the array.
TNOs.push({
x: radiusTNOs * cos(angleTNOs),
z: radiusTNOs * sin(angleTNOs),
y: heightTNOs,
size: random(0.02, 1)
})
}
}
function draw() {
background("midnightblue");
orbitControl();
noStroke();
sun();
mercury();
venus();
earth();
mars();
asteroidBelt();
jupiter();
saturn();
uranus();
neptune();
kuiperBelt();
}
function colorTheSun() {
//we're gonna do some crazy stuff with the sun
//first we declare 3 color variables
let goldSun = color(255, 250, 50);
let orangeSun = color(255, 215, 0);
let redSun = color(255, 165, 0);
//ChatGPT helped a lot with this
//we're creating a wave and designating
//that each color be represented during
//a portion of that wave
let t = sin(frameCount*0.05) % 1;
let mappedT = map(t, -1, 1, 0, 1);
//if the wave is in between any of the
//values, we want it to create an inbetween
//color to help the visual transition
let sunColor;
if (mappedT < 1/3) {
return lerpColor(goldSun, orangeSun, mappedT * 3);
}
else if (mappedT < 2/3) {
return lerpColor(orangeSun, redSun, (mappedT - 1/3) * 3);
}
else {
return lerpColor(redSun, goldSun, (mappedT - 2/3) * 3);
}
}
function sun() {
//we want the color of the sun to be
//returned from the corresponding function
let sunColor = colorTheSun()
//the sun is kind of crazy. It's this big
//ball of exploding gas that isn't
//necessarily a static ball, so I want
//it to have a bit of a pulse to it
let sunSize = random(995, 1000)
//it is the primary light source
pointLight(255, 255, 255, 0, 0, 0)
push()
emissiveMaterial(sunColor)
sphere(sunSize);
pop()
}
function mercury() {
//orbital speed
rotateY(frameCount * 0.01136);
push();
translate(1078,0,0);
specularMaterial("darkgray")
sphere(3.503);
//rotation speed
rotateY(frameCount * 0.0034);
pop();
}
function venus() {
//orbital speed
rotateY(frameCount * 0.00444);
push();
translate(1144, 0, 0);
specularMaterial("khaki")
sphere(8.691);
//rotation speed
rotateY(frameCount * -0.00082);
pop();
}
function earth() {
//orbital speed
rotateY(frameCount * 0.00274);
push();
translate(1200, 0, 0);
specularMaterial("royalblue")
sphere(9.149);
//rotation speed
rotateY(frameCount * 0.2);
pop();
}
function mars() {
//orbital speed
rotateY(frameCount * 0.00146);
push();
translate(1304, 0, 0);
specularMaterial("firebrick")
sphere(4.868);
//rotation speed
rotateY(frameCount * 0.194);
pop();
}
function asteroidBelt() {
push();
//orbital rotation speed
rotateY(frameCount * 0.01);
for (let obj of ABOs) {
push();
translate(obj.x, obj.y, obj.z);
specularMaterial(200);
sphere(obj.size);
pop();
}
pop();
}
function jupiter() {
//orbital speed
rotateY(frameCount * 0.00023);
push();
translate(2040, 0, 0);
specularMaterial("burlywood")
sphere(100.39);
//rotation speed
rotateY(frameCount * 0.485);
pop();
}
function saturn() {
//orbital speed
rotateY(frameCount * 0.000093);
//planet
push();
translate(2916, 0, 0);
specularMaterial("papayawhip")
sphere(83.625);
//rotation speed
rotateY(frameCount * 0.448);
pop();
//ring
push();
translate(2916, 0, 0);
rotateX(HALF_PI);
fill(245, 245, 245, 50);
ellipsoid(192, 3, 192);
pop();
}
function uranus() {
//orbital speed
rotateY(frameCount * 0.000032);
push();
translate(4840, 0, 0);
specularMaterial("lightcyan")
sphere(36.421);
rotateY(frameCount * -0.279);
pop();
}
function neptune() {
//orbital speed
rotateY(frameCount * 0.0000166)
push();
translate(7000, 0, 0);
specularMaterial("deepskyblue")
sphere(35.359);
rotateY(frameCount * 0.298)
pop();
}
function kuiperBelt() {
push();
//orbital rotation
rotateY(frameCount * 0.00001);
//use the loop to create our unique TNOs
for (let obj of TNOs) {
push();
//place it in the unique location
translate(obj.x, obj.y, obj.z);
ambientMaterial(200);
sphere(obj.size);
pop();
}
pop();
}

Project Brief: close enCOWnters Game

Use your mouse to change your view (pro tip, zoom out just a lil bit), the keyboard arrows to drive the space ship, and the spacebar to suck up the cows before time runs out!

View Code
JavaScript
let angle = 1;
let grass;
let numCows;
let minDistance;
let cowPositions = [];
let cowY = 0;
let lights;
let numLights;
let radius;
let lightsAngle;
let ufoX = -1;
let ufoY = -1250;
let ufoZ = -25;
let beamX = ufoX -1;
let beamY = ufoY + 775;
let beamZ = ufoZ -25;
let beamIsOn;
let moo;
let music;
let beamSound;
let flySound;
let score = 0;
let timer = 60;
let font;
let hud;
let gameOver = false;
function preload() {
font = loadFont("Roboto.ttf");
console.log(font);
grass = loadImage(
"grass for plane (2000 x 2000 px).jpg");
moo = loadSound("moo.wav");
beamSound = loadSound("Beam.wav");
flySound = loadSound("flyShip.wav");
}
function setup() {
createCanvas(600, 600, WEBGL);
angleMode(DEGREES);
hud = createGraphics(width, height);
//how many cows in this herd?
numCows = int(random(1, 25));
//we want to make sure they stand far
//enough apart from each other
//no siamese twins here.
minDistance = 350;
//we are creating an array of info
//about our herd using a loop that
//keeps going until it has the same
//number of objects as numCows
while (cowPositions.length < numCows) {
//we are proposing random positions for
//cows and assigning color & transparency
const candidate = {
x: random(-2000, 2000),
y: random(360),
z: random(-2000, 2000),
color: cowColorFinder(),
cowY : 0,
cowAlpha : 255
};
let tooClose = false;
//we need to check the distance of the
//randomly generated x, y and z values
//to make sure they are greater than
//our assigned minimum distance apart
//if the positions are too close to an
//already existing cow positions in the
//array, we need to break out of the loop and
//try again. No need to keep comparing to
//the other values in the array
for (const c of cowPositions) {
if (
dist(candidate.x, candidate.z, c.x, c.z) < minDistance) {
tooClose = true;
break;
}
}
//if they are not too close, they
//can be added to the array
if (!tooClose) cowPositions.push(candidate);
}
//I like to see this stuff in the console,
//so I left it here after debugging, even
//though it isn't really necessary
//console.log(
// "finished generating",
// cowPositions.length,
// "cows")
}
function draw() {
background("lightblue");
orbitControl();
directionalLight(255,255,225, 0,1,0)
// grass
push();
//put the grass on the ground
translate(0, 325, 0);
rotateX(90);
texture(grass);
plane(100000, 100000);
pop();
drawCows();
ufo();
flyTheShip();
updateTimer();
drawTimer();
if (timer === 0 ) {
gameOver = true
//drawGameOver();
noLoop();
return;
}
}
function drawTimer() {
console.log(timer)
hud.clear();
hud.textFont(font);
hud.textFont(font);
hud.textAlign(CENTER, TOP);
hud.textSize(32);
hud.textSize(600);
hud.fill(timer > 10 ? "green" : "red");
hud.text(timer, width/2, 50);
push();
resetMatrix();
translate(-width / 2, -height / 2)
drawingContext.disable(
drawingContext.DEPTH_TEST);
image(hud, -width / 2, -height / 2, width, height);
drawingContext.enable(
drawingContext.DEPTH_TEST);
pop();
}
function updateTimer() {
if (frameCount % 60 == 0 && timer > 0) {
timer--;
}
}
function drawGameOver() {
//push();
//resetMatrix();
// drawingContext.disable(
// drawingContext.DEPTH_TEST);
hud.clear();
hud.background(0);
hud.textFont(font);
hud.fill(0, 255, 0);
hud.textAlign(CENTER, CENTER);
hud.textSize(56);
hud.text(
"GAME OVER",
hud.width / 2,
hud.height / 2);
push();
resetMatrix();
translate(-width / 2, -height / 2)
drawingContext.disable(
drawingContext.DEPTH_TEST);
image(hud, -width / 2, -height / 2, width, height);
drawingContext.enable(
drawingContext.DEPTH_TEST);
pop();
}
function ufo() {
//the beam doesn't turn on til we say so
beamIsOn = false;
push();
fill("lightSlateGray");
stroke(0,0,0,30);
translate(ufoX, ufoY, ufoZ);
//ufo orb
ellipsoid(300, 200, 300, 24, 5);
//rotate the saucer
rotateY(angle);
//ufo saucer
ellipsoid(600, 50, 600, 24 , 10);
//this thing needs lights
let sphereX;
let sphereY = 0;
let sphereZ;
radius = 590;
//we'll create 8 lights and distribute them
//around the edge of the saucer
numLights = 8;
for (let i = 0; i < numLights; i++) {
//Chat said use i*numLights*TWO_PI, but
//that results in clumped up lights
//experimentation says use 5.5 in place of TWO_PI
lightsAngle = i * numLights* 5.5;
//Chat told me how to do this part
sphereX = cos(lightsAngle) * radius;
sphereZ = sin(lightsAngle) * radius;
push();
//move the lights into position
translate(sphereX, sphereY, sphereZ);
//I can't tell if the pointLight works, but I'm
//leaving it because I want to believe
pointLight(255,191,0, sphereX, sphereY, sphereZ);
emissiveMaterial(255, 191,0);
sphere(12);
pop();
}
//ufo rotation speed
angle += 100.6;
pop();
}
//let's bring this baby to life
function flyTheShip() {
if (keyIsPressed) {
if (keyCode == UP_ARROW) {
ufoZ -= 200;
beamZ -= 200;
//keeps the sounds from restarting
//and crashing out the audio buffer
if (!flySound.isPlaying()) {
flySound.play()
}
}
if (keyCode == DOWN_ARROW) {
ufoZ += 200;
beamZ += 200;
//keeps the sounds from restarting
//and crashing out the audio buffer
if (!flySound.isPlaying()) {
flySound.play()
}
}
if (keyCode == LEFT_ARROW) {
ufoX -= 200;
beamX -= 200;
//keeps the sounds from restarting
//and crashing out the audio buffer
if (!flySound.isPlaying()) {
flySound.play()
}
}
if (keyCode == RIGHT_ARROW) {
ufoX += 200;
beamX += 200;
//keeps the sounds from restarting
//and crashing out the audio buffer
if (!flySound.isPlaying()) {
flySound.play()
}
}
//use the spacebar to operate the beam
if (keyCode == 32) {
beamIsOn = !beamIsOn
if (beamIsOn == true) {
beam();
//keeps the sounds from restarting
//and crashing out the audio buffer
if (!beamSound.isPlaying()) {
beamSound.play();
}
}
}
}
}
function beam(ufoX, ufoY, ufoZ) {
//the beam is green
push();
fill(0, 255, 0, 35);
noStroke();
//move the beam over the cow
translate(beamX, beamY, beamZ);
//flip the beam in the right direction
rotateX(180);
//why use numbers when you can use variables?
let coneRadius = 400;
let coneHeight = 1575;
//make it GLOW
emissiveMaterial(0,30,0, 1)
cone(coneRadius, coneHeight, 24, 20);
pop();
}
function drawCows() {
//for as many objects are in our array
for (let i = cowPositions.length -1; i >= 0; i--) {
let c = cowPositions[i];
push();
//place them in their custom spots
translate(c.x,c.cowY, c.z);
//rotate them
rotateY(c.y);
//draw the cow, using the color info
cow(c.color, c.cowAlpha);
//check to see if the cow is in the beam
if (beamIsOn) {
let d = dist(c.x, c.z, beamX, beamZ);
//if the cow is in the beam
//lift it up and make it disappear
if (d < 300) {
c.cowY -= 10
c.cowAlpha-= 5
}
}
pop();
//we need to remove the transparent cows,
//so we don't see ghosts once the ship
//sucks them up
if (c.cowAlpha == 0 || c.cowY < -2000) {
cowPositions.splice(i, 1);
//once the cow is in the ship, we hear about it
moo.play()
}
}
}
//this function defines how cows are
//assigned their colors
function cowColorFinder() {
const cowColorNum = random(1000);
//there is a suuuuuper slim chance
//that you will see a magenta cow
if (
cowColorNum <= 1)
return [255, 0, 255]
//chances are, the cow is brown
else if (
cowColorNum > 1 && cowColorNum < 900)
return [150, 75, 0]
//but white ones do pop up here and there
else return [255, 255, 255, 255]
}
//this is how to build a cow, passing in the
//info that we obtained regarding color
function cow(cowColor, cowAlpha) {
//again, not necessary but I like to see it
// console.log("cowColor is", cowColor)
// console.log("cowAlpha is", cowAlpha)
//modify the stroke depending on
//the cow transparency
if (cowAlpha > 50) {
stroke(0,0,0, cowAlpha-200);
} else {
noStroke();
}
//head
push();
fill(
cowColor[0],
cowColor[1],
cowColor[2],
cowAlpha);
sphere(50);
push();
translate(14, -30, 0);
ellipsoid(23, 25, 20);
pop();
push();
translate(-14, -30, 0);
ellipsoid(23, 25, 20);
pop();
//horns
push();
//beige
fill(245, 245, 220, cowAlpha);
push();
//horn 1
rotate(110);
translate(-25, 60, 0);
cone(10, 30);
pop();
//horn 2
rotate(-110);
translate(25, 60, 0);
cone(10, 30);
pop();
//snout
fill(
cowColor[0],
cowColor[1],
cowColor[2],
cowAlpha);
translate(0, 20, 25);
rotateX(-15);
ellipsoid(50, 40, 70);
pop();
//nose
push();
//rosybrown
fill(188, 143, 143, cowAlpha);
rotateX(-15);
push();
translate(0, 8, 83);
ellipsoid(27, 25, 10);
pop();
push();
translate(0, 6, 86);
ellipsoid(10, 22, 15);
pop();
translate(0, 13, 85);
ellipsoid(25, 15, 15);
pop();
push();
//black
fill(0, 0, 0, cowAlpha)
translate(0, 28, 78);
ellipsoid(25, 23, 15);
pop();
//mouth
push();
translate(0, 38, 78);
//stroke(0, 0, 0, 30);
fill(
cowColor[0],
cowColor[1],
cowColor[2],
cowAlpha);
ellipsoid(25, 23, 15);
pop();
push();
//black
fill(0, 0, 0, cowAlpha);
translate(0, 40, 68);
rotateY(50);
ellipsoid(26, 8, 25);
pop();
//ears
push();
fill(
cowColor[0],
cowColor[1],
cowColor[2],
cowAlpha);
//left ear
push();
translate(-65, -20, 0);
ellipsoid(25, 15, 10);
pop();
//right ear
translate(65, -20, 0);
ellipsoid(25, 15, 10);
pop();
//eyes
push();
//brown
fill(150, 75, 0, cowAlpha);
//left eye
push();
translate(-20, -10, 40);
ellipsoid(16, 16, 10);
//white
fill(255, 255, 255, cowAlpha);
ellipsoid(15, 15, 13);
//black
fill(0, 0, 0, cowAlpha);
ellipsoid(14, 14, 15);
pop();
push();
translate(20,-10, 40);
ellipsoid(16,16,10);
//white
fill(255, 255, 255, cowAlpha);
ellipsoid(15, 15, 13);
//black
fill(0, 0, 0, cowAlpha);
ellipsoid(14, 14, 15);
pop();
pop();
//neck
push();
fill(
cowColor[0],
cowColor[1],
cowColor[2],
cowAlpha);
translate(0, 50, 0);
cylinder(35, 80);
pop();
//body
push();
fill(
cowColor[0],
cowColor[1],
cowColor[2],
cowAlpha);
//chest
push();
translate(0, 150, 0);
ellipsoid(75, 80, 80);
pop();
//torso
push();
rotateX(-5);
translate(0, 165, -75);
ellipsoid(55, 80, 100);
pop();
//backside
push();
translate(0, 155, -160);
ellipsoid(85, 80, 80);
pop();
pop();
//udders
push();
//pink
fill(255, 192, 203, cowAlpha);
push();
translate(0, 230, -135);
ellipsoid(60, 35, 45);
pop();
//teat 1
push();
translate(-20, 270, -145);
ellipsoid(7,9,7);
pop();
//teat 2
push();
translate(20, 270, -145);
ellipsoid(7,9,7);
pop();
//teat 3
push();
translate(-20, 270, -125);
ellipsoid(7,9,7);
pop();
//teat 4
push();
translate(20, 270, -125);
ellipsoid(7,9,7);
pop();
//legs
push();
fill(
cowColor[0],
cowColor[1],
cowColor[2],
cowAlpha);
//moooove em all down low
translate(0, 250, 0);
push();
translate(40, 0, 0);
cylinder(25, 120);
pop();
push();
translate(-40, 0, 0);
cylinder(25, 120);
pop();
push();
translate(50, 0, -160);
cylinder(25, 120);
pop();
push();
translate(-50, 0, -160);
cylinder(25, 120);
pop();
//hooves
push();
//black
fill(0, 0, 0, cowAlpha);
translate(0, 65, 0);
push();
translate(40, 0, 0);
cylinder(25, 20);
pop();
push();
translate(-40, 0, 0);
cylinder(25, 20);
pop();
push();
translate(50, 0, -160);
cylinder(25, 20);
pop();
push();
translate(-50, 0, -160);
cylinder(25, 20);
pop();
pop();
pop();
//tail
fill(
cowColor[0],
cowColor[1],
cowColor[2],
cowAlpha);
translate(0, 135, -245);
rotateX(-40);
ellipsoid(2, 50, 2);
pop();
}

Project Brief: Interactive Sound Art

This piece uses the microphone on your device and responds to the sounds around you.

Preview mode is not available for this project.

Click ‘Launch’ to view, then click ‘Allow’ to enable microphone access.

For best results, play music!

View Code
JavaScript
// First let's declare some variables we're gonna need
// so we can access the mic and reference how loud it is
let mic;
let vol;
// we're gonna need to know where things are going
let yPosition;
let xPosition;
// this is how we're gonna run the loop
let numCircles;
// things are gonna happen with color
let r;
let g;
let b;
let a;
// let's get started
// we need a canvas that adapts to it's window
function setup() {
createCanvas(windowWidth,windowHeight);
// I keep messing with the frame rate,
// I'm not sure if it affects anything
// but I'm not ready to delete this yet
//frameRate(50)
// we're gonna start with a pretty
// white background
// I want the background in the setup
// so it will keep drawing on top of it
// and not start fresh each time it runs
background(255);
// let's define our mic variable
// and turn it on
mic = new p5.AudioIn();
mic.start();
}
// now for the fun stuff
function draw() {
// we gotta make the numbers big
// enough to work with
vol = mic.getLevel() * 1500;
// I want my circles to appear
// in random locations on the canvas
xPosition = random(width)
yPosition = random(height)
// I want the volume to dictate how
// big the circles are and I'm going
// to multiply it by one of the most
// fun numbers. JUST FOR FUN.
diameter = vol * 3.14
// I probably should have started the number
// of circles at zero, because technically,
// zero circles exist. But I didn't.
numCircles = 1
// I want my circles to be very transparent but
// I don't want them all to be exactly the same
a = random(5)
// Next we'll define how object
// color determination happens
// based on the volume levels
// ChatGPT helped me figure out
// what numbers to plug in here
// if the volume is less than ten,
// our color values will generate white
if (vol < 10) {
r = 255
g = 255
b = 255
}
// between ten and 40, pink/magenta
if (vol > 10 && vol < 40 ) {
r = random (200, 255)
g = random (80)
b = random(150, 255)
}
// violet
if (vol > 40 && vol < 80) {
r = random(150, 200)
g = random(80)
b = random(150, 255)
}
// indigo
if (vol > 80 && vol < 120) {
r = random(75, 130)
g = random(80)
b = random(130, 200)
}
// blue
if (vol > 120 && vol < 160) {
r = random(0, 80)
g = random(100)
b = random(180, 255)
}
// green
if (vol > 160 && vol < 200) {
r = random(0, 80)
g = random(150, 255)
b = random(100)
}
//white
if (vol > 200) {
r= 255
g=255
b=255
}
// now that we know what volume defines
// which color, let's start a loop to
// draw our objects using numCircles
while (numCircles < 75) {
// let's give them color
// and make the stroke a
// little different
fill(r,g,b,a)
stroke(r+vol, g+vol, b+vol)
strokeWeight(random(1))
// let's add some conditional logic in our loop
// if the volume is above the threshold,
// we get triangles that appear in slightly
// random locations along the edges of the canvas
if(vol > 76) {
triangle(random(width - 1),
random(height / 2),
random(width / 2, width - 1),
random(height / 2, height - 1),
random(1, width - 1),
random(height / 3,height -1))
}
// if the volume happens to be roughly 75
// we get a giant circle in a random location
// based off of the volume and a fun number
if (vol > 74 && vol < 76) {
circle(vol * 3.14,vol * 3.14, vol * 100)
}
// if the volume is below 74 we get circles
if (vol > 10 && vol < 74) {
circle(xPosition, yPosition, diameter)
}
// if the volume is below 10 a big white
// square blanks out our canvas
if (vol < 10) {
fill("white")
square(0, 0, width)
}
// we increment numCircles (probably should have
// been called numObjects, but there were no
// triangles when numCircles was born)
numCircles++
//console.log("vol =", vol,
// "r =", r,
// "g =", g,
// "b =", b,
// "numCircles =", numCircles,
// "windowWidth =", windowWidth,
// "windowHeight =", windowHeight,
// "height =", height,
// "width =", width)
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
redraw()
}
}

Project Brief: How Now Brown Cow?

My first foray into 3D design using WEBGL, this piece demonstrates why itสปs not safe to leave your cows alone in the field.

View Code
JavaScript
let angle = 1;
function setup() {
createCanvas(600, 600, WEBGL);
angleMode(DEGREES);
}
function draw() {
background("lightblue");
orbitControl();
// grass
push();
//put the grass on the ground
translate(0, 325, 0);
rotateX(90);
fill("green");
plane(100000, 100000);
pop();
cow();
ufo();
}
function cow() {
stroke(0,0,0, 25);
//head
push();
fill("brown");
sphere(50);
push();
translate(14, -30, 0);
ellipsoid(23, 25, 20);
pop();
push();
translate(-14, -30, 0);
ellipsoid(23, 25, 20);
pop();
//horns
push();
fill("beige");
push();
//horn 1
rotate(110);
translate(-25, 60, 0);
cone(10, 30);
pop();
//horn 2
rotate(-110);
translate(25, 60, 0);
cone(10, 30);
pop();
//snout
translate(0,20,25 );
rotateX(-15);
ellipsoid(50,40,70);
pop();
//nose
push();
fill("rosybrown");
rotateX(-15);
push();
translate(0,8, 83);
ellipsoid(27,25,10);
pop();
push();
translate(0, 6, 86);
ellipsoid(10,22, 15);
pop();
translate(0, 13, 85);
ellipsoid(25, 15, 15);
pop();
push();
fill("black")
translate(0, 28, 78);
ellipsoid(25,23,15);
pop();
//mouth
push();
translate(0,38, 78);
stroke(0,0,0,30);
fill("brown");
ellipsoid(25, 23,15);
pop();
push();
fill("black");
translate(0,40,68);
rotateY(50);
ellipsoid(26,8, 25);
pop();
//ears
push();
fill("brown");
//left ear
push();
translate(-65, -20, 0);
ellipsoid(25, 15, 10);
pop();
//right ear
translate(65, -20, 0);
ellipsoid(25,15,10);
pop();
//eyes
push();
fill("brown");
//left eye
push();
translate(-20,-10, 40);
ellipsoid(16,16,10);
fill("white");
ellipsoid(15, 15, 13);
fill("black");
ellipsoid(14, 14, 15);
pop();
push();
translate(20,-10, 40);
ellipsoid(16,16,10);
fill("white");
ellipsoid(15, 15, 13);
fill("black");
ellipsoid(14, 14, 15);
pop();
pop();
//neck
push();
fill("brown");
translate(0, 50, 0);
cylinder(35, 80);
pop();
//body
push();
fill("brown")
//chest
push();
translate(0, 150, 0);
ellipsoid(75, 80, 80);
pop();
//torso
push();
rotateX(-5);
translate(0, 165, -75);
ellipsoid(55, 80, 100);
pop();
//backside
push();
translate(0, 155, -160);
ellipsoid(85, 80, 80);
pop();
pop();
//udders
push();
fill("pink");
push();
translate(0, 230, -135);
ellipsoid(60, 35, 45);
pop();
//teat 1
push();
translate(-20, 270, -145);
ellipsoid(7,9,7);
pop();
//teat 2
push();
translate(20, 270, -145);
ellipsoid(7,9,7);
pop();
//teat 3
push();
translate(-20, 270, -125);
ellipsoid(7,9,7);
pop();
//teat 4
push();
translate(20, 270, -125);
ellipsoid(7,9,7);
pop();
//legs
push();
fill("brown");
//moooove em all down low
translate(0, 250, 0);
push();
translate(40, 0, 0);
cylinder(25, 120);
pop();
push();
translate(-40, 0, 0);
cylinder(25, 120);
pop();
push();
translate(50, 0, -160);
cylinder(25, 120);
pop();
push();
translate(-50, 0, -160);
cylinder(25, 120);
pop();
//hooves
push();
fill("black");
translate(0, 65, 0);
push();
translate(40, 0,0);
cylinder(25, 20);
pop();
push();
translate(-40, 0,0);
cylinder(25, 20);
pop();
push();
translate(50, 0,-160);
cylinder(25, 20);
pop();
push();
translate(-50, 0,-160);
cylinder(25, 20);
pop();
pop();
pop();
//tail
fill("brown");
translate(0, 135, -245);
rotateX(-40);
ellipsoid(2,50,2);
pop();
}
function ufo() {
push();
fill("lightSlateGray");
stroke(0,0,0,30);
translate(0, -750, -25);
//it would be more fun if this thing moved
rotateY(angle);
//ufo orb
sphere(90);
//ufo saucer
ellipsoid(200, 25, 200, 24 , 10);
pop();
//the beam is green
push();
fill(0, 255, 0, 35);
noStroke();
//move the beam over the cow
translate(0, -175, -25);
//flip the beam in the right direction
rotateX(180);
//why use numbers when you can use variables?
let coneRadius = 400;
let coneHeight = 975;
cone(coneRadius, coneHeight, 24, 20);
//ufo rotation speed
angle += 0.6;
pop();
}

Project Brief: Random Rainbow Supernova

Warning: This might crash your device.

This piece explores the use of exponents in variables while playing with randomness. Reloading the page will generate a random color scheme each time.

View Code
JavaScript
let particles = [];
let colors = [];
let reds = ["#FFC0C8", "#FFB6C1", "#FF69B4", "#FF1493"," #C71585","#DB7093", "#CD5C5C", "#F08080"," #FA8072"," #E9967A"," #FFA07A", "#DC143C"," #FF0000","#B22222", "#8B0000"," #FFA07A", "#FF7F50"," #FF6347", "#FF4500"," #FF8C00"," #FFA500"];
let yellows = ["#FFD700", "#FFFF00", "#FFFFE0", "#FFFACD", "#FAFAD2", "#FFEFD5", "#FFE4B5"];
let greens = ["#FFDAB9", "#EEE8AA", "#F0E68C", "#BDB76B", "#ADFF2F", "#7FFF00", "#7CFC00", "#00FF00", "#32CD32", "#98FB98", "#90EE90", "#00FA9A", "#00FF7F", "#3CB371", "#2E8B57", "#228B22", "##008000", "#006400", "#9ACD32", "#6B8E23", "#808000", "#556B2F", "#66CDAA", "#8FBC8B"];
let blues = ["#20B2AA", "#008B8B", "#008080", "#00FFFF", "#E0FFFF", "#AFEEEE", "#7FFFD4", "#40E0D0", "#48D1CC"," #00CED1"," #5F9EA0"," #4682B4"," #B0C4DE"," #B0E0E6"," #ADD8E6"," #87CEEB"," #87CEFA"," #00BFFF"," #1E90FF"," #6495ED"," #7B68EE"," #4169E1"," #0000FF"," #0000CD"," #00008B"," #0000B0"," #191970"];
let purples = [" #7B68EE"," #483D8B"," #6A5ACD"," #4B0082"," #800080"," #8B008B"," #9932CC"," #9400D3"," #8A2BE2"," #663399"," #9370D8"," #BA55D3"," #FF00FF"," #DA70D6"," #EE82EE"," #DDA0DD"," #D8BFD8"," #E6E6FA"];
function setup() {
createCanvas(windowWidth,windowHeight, WEBGL);
//let's make it art
background(0);
//assign a random color array
let colorNumber = random(5);
if (colorNumber <= 1) {
colors = reds
}
if (colorNumber > 1 && colorNumber <= 2) {
colors = yellows
}
if (colorNumber > 2 && colorNumber <= 3) {
colors = greens
}
if (colorNumber > 3 && colorNumber <= 4) {
colors = blues
}
if (colorNumber > 4 && colorNumber <= 5) {
colors = purples
}
//add 300 particles and their unique //attributes to the corresponding array
for (let i=0; i < 3000; i++) {
particles.push({
x : 0,
y : 0,
z : 0,
radius : random(200),
xSpeed : random(1, 20),
ySpeed : random(1, 20),
zSpeed : random(1, 20),
xDir : random(-1, 1),
yDir : random(-1, 1),
zDir : random(-1, 1),
})
}
}
function draw() {
frameRate(60);
orbitControl();
//we're not assigning color to the
//spheres, we're assigning it to
// the three different light sources
let c1 = color(random(colors))
directionalLight(c1, 0, 0, 1);
let c2 = color(random(colors))
directionalLight(c2, 1, 0, 0);
let c3 = color(random(colors))
directionalLight(c3, 0, 1, 0);
//do this 300 times
for (let i = 0; i < 3000; i++) {
noStroke()
//give the particles motion
particles[i].x += particles[i].xSpeed * particles[i].xDir;
particles[i].y += particles[i].ySpeed * particles[i].yDir;
particles[i].z += particles[i].zSpeed * particles[i].zDir;
//assign unique x, y, z & radius //parameters to each drawn sphere
push();
translate(particles[i].x, particles[i].y, particles[i].z);
ambientMaterial(255,255,255)
sphere(particles[i].radius);
pop();
}
}

Project Brief: Homage to Andrew James

This piece aims to recreate an interactive art work created by Andrew James. This uses placement on the x and y axis to determine color while integrating randomness into the size and placement of the circles.

View Code
JavaScript
// Let's create an homage to Andrew James!
function setup() {
createCanvas(400, 400);
// I want each rendering to remain on screen so background() is in my setup function
background("darkblue")
}
function draw() {
// I only want this to render a single image, dictated by my for loop, so I get rid of the automatic loop
noLoop()
// I want this loop to create 1300 circles
for (let numCircles = 0; numCircles < 1300; numCircles++){
//I want my circles to appear randomly on the x & y axis at random sizes up to 35 pixels in diameter
let x = random(400)
let y = random(400)
let diameter = random(35)
// I want my colors to generate based on their position on the y axis
//I want red tones at the top
if (y < 100) {
r = random (150, 255)
g = random(50, 100)
b = 0
}
// I want more yellow and orange below that
if (y > random(90,110) && y < 200) {
r = random(200, 255)
g = random (150, 215)
b = random(66)
}
// Below that should be more teal shades
if (y > random(190, 210) && y < 300) {
r = random(66)
g = random(170, 255)
b = random (150, 255)
}
// At the bottom I want darker blues
if (y > random(275,300) && y<400) {
r = random(90, 100)
g = random (100, 150)
b = random (200, 255)
}
// I need some pinks in the upper right quadrant
if (y< random(190, 210) && x > random(225, 250) && y > 75) {
r= random(200,255)
g= random(175, 200)
b= random(175, 210)
}
// Give each circle a color based on their random variables
fill(r, g, b)
// Outline each circle with a randomly-assigned, yet fill-adjacent color
let randomStroke = random(50)
if (randomStroke >25){
stroke(r+random(75), g+random(75), b+random(75))
} else {
stroke(r-random(50), g - random(50), b - random(150))
}
// Draw them circles
circle(x,y,diameter)
circle(x, y, diameter/1.5)
circle(x, y, diameter/2)
circle(x,y, diameter/3)
circle(x, y, diameter/4)
}
}

Project Brief: Random Song Generator

Click the button and a song will be created and played just for you. It is a one of a kind, never to be duplicated song, created by generating a random number of notes in the song arranged in a random order.

JUST FOR YOU.

View Code
JavaScript
//defining all of these variables globally so they are accessible all over the place
let musicNoteA, musicNoteB2, musicNoteC, musicNoteD, musicNoteE, musicNoteF, musicNoteG;
let numKeys = 7;
let keyWidth;
let circleX, circleY;
let numNotes;
let songLength;
let btn;
// load up these sounds so they are ready to go
function preload(){
musicNoteA = loadSound("A.mp3")
musicNoteB2 = loadSound("B2.mp3")
musicNoteC = loadSound("C.mp3")
musicNoteD = loadSound("D.mp3")
musicNoteE = loadSound("E.mp3")
musicNoteF = loadSound("F.mp3")
musicNoteG = loadSound("G.mp3")
}
// get the canvas ready
function setup() {
createCanvas(400, 400);
//we're using HSB colorMode to make the gradient easier to apply
colorMode(HSB, 360, 100, 100)
noStroke()
keyWidth = width / numKeys
// we need a button to trigger the magic
btn = createButton("Play a random song")
btn.mousePressed(playSong)
}
function draw() {
background(220);
// let's make sure the speed is a little different each time
frameRate(random(1, 10))
// ChatGPT helped me with this code to correctly map each color to the piano keys because I wanted them to be a rainbow
for (let i = 0; i < numKeys; i++) {
let h = map(i, 0, numKeys, 0, 360)
//I did not like the aesthetics of Chat's recommendation to randomize the saturation and brightness, so I set those myself
let s = 75
let b = 90
fill(h, s, b)
// draw our piano keys
rect(i * keyWidth, 0, keyWidth, height)
}
}
// this is where the music comes from
function playSong() {
// the sound isn't playing on mobile. According to Chat, the below snippet should resolve the issue, but it doesn't. More about that later.
getAudioContext().resume();
// we will use numNotes to compare to songLength to determine how long each song will be. Chat helped me with the interval settings because my initial code used a loop that didn't stack the notes the way I wanted them and once I got it stacking and playing, it kept playing forever. Working with Chat to refine the interval fixed that.
numNotes = 0;
songLength = int(random(10, 50))
let interval = setInterval(() => {
if (numNotes >= songLength) {
clearInterval(interval);
return;
}
// We'll use a randomly placed little black circle to tell us which note is playing.
fill("black")
let circleX = random(width)
let circleY = random(height)
circle(circleX, circleY, 25)
// here we will define which note plays depending on which key the circle's X coordinate lands on
keyWidth = width / numKeys
if (circleX < keyWidth) {
musicNoteA.play()
}
if (circleX > keyWidth && circleX < keyWidth * 2) {
musicNoteB2.play()
numNotes++
}
if (circleX > keyWidth * 2 && circleX < keyWidth * 3) {
musicNoteC.play()
numNotes++
}
if (circleX > keyWidth * 3 && circleX < keyWidth * 4) {
musicNoteD.play()
numNotes++
}
if (circleX > keyWidth * 4 && circleX < keyWidth * 5) {
musicNoteE.play()
numNotes++
}
if (circleX > keyWidth * 5 && circleX < keyWidth * 6) {
musicNoteF.play()
numNotes++
}
if (circleX > keyWidth * 6 && circleX < width) {
musicNoteG.play()
numNotes++
}
// the above code all runs within the defined interval rules. Below we define the interval between each note. Chat suggested I use 500 as the time to space between the notes, but I had a different idea.
}, random(500));
}
// according to Chat, this, in conjunction with the previously mentioned attempt on line 60 is supposed to solve the issue of sound not playing if you view this from a mobile device. It doesn't work, so I would love advice on how to resolve this issue so I can share my work via social media. I'm sure 99% of my friends are going to look at it via mobile, so this is important. Without sound it's just a rainbow with some stupid black dots bouncing around.
function touchStarted() {
getAudioContext().resume();
}

Project Brief: Snowman

The snowman is built courtesy of an if/else statement in JavaScript that tells him which part should appear when.

He likes warm hugs.

View Code
JavaScript
//We start with zero snowflakes and
//no snowman
let numFlakes = 0
let moreSnow = numFlakes*-1/1.1
function setup() {
createCanvas(400, 400);
}
function draw() {
background("#608da2");
//this thing is moving way too fast,
//so let's slow it down
frameRate(30)
//I want to make it snow. Let's
//randomly place random-sized
//snowflakes everywhere
let x = random(0, 400)
let y = random(0, 400)
let flakeSize = random(0,10)
function snowflakes() {
circle(x, y, flakeSize)
}
snowflakes()
//We will increment the snowflakes
//and use the number of snowflakes
//to determine what happens next
numFlakes++
//Let's let the snow build up on the ground
if (numFlakes < 150) {
moreSnow = numFlakes*-1/1.1
}
rect(0, 400, 400, moreSnow)
//this is the snowman butt - it
//magically appears when enough snow
//hits the ground!
function snowmanButt() {
circle(300, 300, 100)
}
if (numFlakes > 55) {
snowmanButt()
}
//this is the snowman belly - it
//magically appears when enough snow
//hits the ground!
function snowmanBelly() {
circle(300, 225, 75)
}
if (numFlakes > 75) {
snowmanBelly()
}
//this is the snowman head - it
//magically appears when enough snow
//hits the ground!
function snowmanHead() {
circle(300, 175, 50)
}
if (numFlakes > 100) {
snowmanHead()
}
//we'll let him have a face too.
//eyes
if (numFlakes > 125) {
fill("black")
circle(307, 169, 10)
circle(293, 169, 10)
fill("white")
circle(307, 170, 9)
circle(293, 170, 9)
fill("brown")
circle(307,171, 5)
circle(293, 171, 5)
fill("white")
}
//nose
if (numFlakes > 150) {
fill("orange")
triangle(300,174, 265, 180, 302, 185)
fill("white")
}
//mouth
if (numFlakes > 175){
fill("black")
circle(293, 187, 5)
circle(306, 187, 5)
circle(300, 190, 5)
fill("white")
}
//we'll give him some arms too
x1 = 250
y1 = 250
x2 = 265
y2 = 210
function arms() {
line(x1, y1, x2, y2)
line(350, 250, 335, 210)
}
if (numFlakes > 200){
arms()
}
//and now, with help from ChatGPT to figure this one
//out, let's bring him to life
function waveHello() {
let shoulderX = 350
let shoulderY = 250
let angle = sin(frameCount * 0.1) * HALF_PI / 1.5
let armLength = 25
let handX = shoulderX + cos(angle) * armLength
let handY = shoulderY - sin(angle) * armLength
line(shoulderX, shoulderY, handX, handY)
}
if (numFlakes >200) {
waveHello()
}
}