Concept and Inspiration
For this assignment, I drew inspiration from the the era of late 1990s and early 2000s. At that time, Windows 98 and Windows XP had some unique screensaveers which I still remember and which evokes a sense of nostalgia in me. These screensavers (link) often featured geometric shapes and repetitive, hypnotic patterns, such as oscillating lines, spirals, and pendulum-like movements.
For this, I used robotic arms and “Inverse Kinematics” as my inspiration. From my point of view, I saw this as the perfect oppertunity to blend computational techniques into this visual style. The robot arms represent the pendulums of the past, but with a unique twist. Instead of being merely simple lines, these arms demonstrate the principles of Object-Oriented Programming (OOP), where each pendulum is treated as an independent object, following specific behaviors such as oscillation and length growth. Moreover, inverse kinematics allows each arm to dynamically respond to changing positions, mimicking the flexibility and precision of robotic motion. The result is a digital artwork that blends the nostalgia of retro visuals with the sophistication of modern computational design.
Code Explanation
“Arm” class
This class features a constructor which initializes each pendulum’s amplitude, angle, angle velocity, and length growth. It also has the “update()” function to update the pendulum’s length (amplitude) and oscillation (angle). Using the “display()” function, it calculates the current position of the pendulum and draws a line from the previous position.
// Defining the Arm class class Arm { constructor(amplitude, angle, angleVel, lengthGrowth) { this.amplitude = amplitude; // Initial length of the arm this.angle = angle; // Starting angle this.angleVel = angleVel; // Angular velocity this.lengthGrowth = lengthGrowth; // How fast the arm grows in length } // Method to update the arm's properties (growth and oscillation) update() { this.amplitude += this.lengthGrowth; // Increase length over time this.angle += this.angleVel; // Update angle for oscillation } // Method to display the arm display(prevX, prevY) { let x = sin(this.angle) * this.amplitude; // Calculate x position let y = cos(this.angle) * this.amplitude; // Calculate y position line(prevX, prevY, x, y); // Draw line from previous position to current return { x, y }; // Return current x, y for the next arm } }
“setup()” function
The “setup()” function initializes the canvas size and prepares the environment. It disables fills for the shapes and sets default stroke properties. It randomizes the number of pendulum arms (num_arms) and the other arm’s properties, with each arm receiving random values for amplitude, angular velocity, and growth rate. The arms are stored in an array, each represented as an object with properties for oscillation and growth.
function setup() { createCanvas(800, 800); noFill(); stroke(255); // Initial stroke color strokeWeight(1); // Initial stroke weight // Randomize the number of arms between 2 and 10 num_arms = int(random(2, 10)); // Initialize the Arm objects with random values for (let i = 0; i < num_arms; i++) { let amplitude = random(70, 150); let angleVel = random(0.01, 0.05); let lengthGrowth = random(0.1, 0.5); // Create new Arm and push to the arms array arms.push(new Arm(amplitude, 0, angleVel, lengthGrowth)); } // Initially set the center to an off-canvas position centerX = -1000; centerY = -1000; }
“draw()” function
This function creates a semi-transparent background overlay to maintain the fading trails without fully erasing the canvas. The “rect()” draws a slightly transparent rectangle over the entire canvas, producing the trailing effect. The “translate()” function shifts the origin of the canvas to the clicked point (centerX, centerY), which acts as the center of the pendulum system. A loop iterates over each arm, calculating its new position based on its current angle and amplitude using “Inverse Kinematics.” The arms are drawn as lines connecting from one pendulum to the next, simulating the robot arm movement whos length increases with time.
// Draw Function function draw() { if (hasStarted) { fill(0, 10); // Semi-transparent background to maintain trails rect(0, 0, width, height); // Set the center of the arms to the clicked position translate(centerX, centerY); let prevX = 0; let prevY = 0; // Loop through each arm and update and display them for (let i = 0; i < arms.length; i++) { let arm = arms[i]; // Update arm properties arm.update(); // Display the arm and update the previous position for the next one let newPos = arm.display(prevX, prevY); prevX = newPos.x; prevY = newPos.y; } } }
“mousePressedI()” function
The “mousePressed()” function updates the center of the pendulum system to wherever the user clicks on the canvas (mouseX, mouseY). This triggers the pendulum animation by setting “hasStarted” to true. Upon clicking, it randomizes the stroke color, weight, and number of arms, creating variety and making each user interaction unique. It also reinitializes the pendulum arms with new random values, ensuring a different pattern is generated with every click.
// This function will run when the mouse is pressed function mousePressed() { // Set the new center of the arm system to the clicked location centerX = mouseX; centerY = mouseY; hasStarted = true; // Randomize background and stroke properties stroke(random(0, 255), random(0, 255), random(0, 255)); strokeWeight(random(1, 10)); // Randomize the number of arms between 2 and 6 num_Arms = int(random(2, 6)); // Reinitialize the arms array arms = []; for (let i = 0; i < num_Arms; i++) { let amplitude = random(80, 150); let angleVel = random(0.01, 0.05); let lengthGrowth = random(0.1, 0.5); // Create new Arm objects with random values arms.push(new Arm(amplitude, 0, angleVel, lengthGrowth)); } }
Sketch
Further Improvements which can be made
– Smoother Transitions: Currently, the background might change too quickly when clicking. Adding a smooth transition effect between pendulum sets can make the animation more fluid and visually appealing.
– Scaling upto 3D space: I had originally though of introducing a responsive 3D canvas using “WEBGL” mode in p5.js, but that was making the idea of user interaction a little complex, so I had to drop that for now.
– Damping: Currently, the simulation runs the pendulums until another person clicks it. Introducing damping can be another way to introduce realism to it.
– Collission: Various arms when coming in contact with each other change their path/length can be another aspect which can be looked to.
Reflection
This project modernizes the retro screensaver aesthetic using modern programming techniques such as OOP and inverse kinematics, combined with user interactivity. The code is modular, making it easy to add new features or improvements, and the possibilities for further customization and expansion are vast.