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
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 displayfunction 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 applyfunction 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 displayfunction 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 strokefunction whichStroke() { let strokeRandomizer = random(10); if (strokeRandomizer >=5) { return noStroke() } else { return null }}// determines which color to applyfunction 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
//we're gonna create a bunch of objects for the//asteroid belt and we're gonna store them in an arraylet ABOs = [];let numABOs;let heightABOs;let sizeABOs;//gonna also create an array of trans-Neptunian //objects in the Kuiper Beltlet 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
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 lifefunction 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 colorsfunction 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 colorfunction 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
// First let's declare some variables we're gonna need// so we can access the mic and reference how loud it islet mic;let vol;// we're gonna need to know where things are goinglet yPosition;let xPosition;// this is how we're gonna run the looplet numCircles;// things are gonna happen with colorlet r;let g;let b;let a;// let's get started// we need a canvas that adapts to it's windowfunction 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 stufffunction 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
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
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
// 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
//defining all of these variables globally so they are accessible all over the placelet 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 gofunction 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 readyfunction 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 fromfunction 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
//We start with zero snowflakes and //no snowmanlet numFlakes = 0let moreSnow = numFlakes*-1/1.1function 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() }}