This project is all about creating an interactive experience from the seek command where a magnifying glass chases after footprints on the canvas. When the magnifying glass “catches” a footprint, a new one pops up randomly, representing the excitement of exploration and discovery.
Highlight I’m proud of
I’m proud of how smoothly the magnifying glass interacts with the footprints. Watching it follow the moving footprints is super satisfying, especially with the slight speed difference
class MagnifyingGlass {
constructor(x, y) {
this.pos = createVector(x, y);
this.vel = createVector(0, 0);
this.acc = createVector(0, 0);
this.maxSpeed = 1.5; // Slower speed for magnifying glass
this.maxForce = 0.05; // Slower acceleration
}
seek(target) {
let force = p5.Vector.sub(target, this.pos);
force.setMag(this.maxSpeed);
force.sub(this.vel);
force.limit(this.maxForce);
return force;
}
applyForce(force) {
this.acc.add(force);
}
update() {
this.vel.add(this.acc);
this.vel.limit(this.maxSpeed);
this.pos.add(this.vel);
this.acc.set(0, 0);
}
followFootprints() {
if (footprints.length > 0) {
let target = footprints[footprintIndex].pos;
let distance = p5.Vector.dist(this.pos, target);
// If close to current footprint, remove it and generate a new one
if (distance < 5) {
footprints.splice(footprintIndex, 1);
this.generateNewFootprint();
if (footprints.length > 0) {
footprintIndex = footprintIndex % footprints.length;
}
} else {
let force = this.seek(target);
this.applyForce(force);
}
}
}
Reflection and ideas for future work or improvements
Footprint Movement: Right now, the footprints just wander randomly. I could make them move in more interesting patterns or even have them react to the magnifying glass’s position to make things more interactive.
Sound Effects: Adding sound effects for when the magnifying glass catches a footprint could make the experience even more engaging.
For this project, I wanted to explore a difficult reality in my life, reimagining it in a way that feels more approachable and symbolic. This past year and half I am living the mose painful realities of my life—the loss of my familys home and loved ones to the brutality of war. It’s a huge wound that I carry with me everyday, yet try to keep moving. Initially, I considered expressing this darkness without censorship using a visual of rockets aimed toward a target , with an explosive effect, but I decided on a subtler approach that relies on symbolism. To bring the concept together, I used warm, fiery colors to create a cohesive and evocative experience.
Highlights:
I began by reviewing the slides, reading the chapter, and watching the coding terrain videos to refresh my memory and better understand Autonomous Agents. I began by looking at the code we did in class and began to experiment with it. I decided to focus mainly on the seek and flee rather than pursue and evade because I thought they worked best with my idea.
I created an array of 3 targets equal in distance from one another in an angle and another array of 23 vehicles, keeping in mind the 0 index. I tried to figure out how to divide these vehicles towards their targets; I realized I could do this by first creating a target index and dividing ‘i’ by 8, the number of vehicles each target will get, and then passing the target with the target index. Further, I made the default of the program to seek a target until the mouse is pressed; it flees. I also added some transparency to the code, enhancing the flow of the points and the overall aesthetics. For the vehicle class, it was similar to the ones in class for flee and seek. I change them a little to make them work best for my visualization. For the flee functionality, I did not want the points to leave the canvas to fix this, so I created conditions to check the x and y edges of the canvas.
let vehicles = [];
let targets = [];
function setup() {
createCanvas(650, 650);
// Initialize 3 main targets and place them near each other with equal distance
targets.push(createVector(width * 0.25, height / 2.5));
targets.push(createVector(width * 0.5, height / 2));
targets.push(createVector(width * 0.75, height / 1.5));
// Create an array of vehicles that are grouped to each target
for (let i = 0; i < 23; i++) {
// Each 8 vehicles share the same target 0 to 17 its 18 so 8
let targetIndex = floor(i / 8);
//each vehicle has a random intial position and a target
let vehicle = new Vehicle(random(width), random(height), targets[targetIndex]);
vehicles.push(vehicle);
}
}
function draw() {
background(0,15);
for (let target of targets) {
//draw targets as a circle for now
//yellowish flamy like
fill(255,232,8);
stroke(255,154,0);
strokeWeight(2);
circle(target.x, target.y, 10);
}
// Update and show vehicles
for (let vehicle of vehicles) {
if (mouseIsPressed){
vehicle.flee(vehicle.target);
vehicle.update();
vehicle.show();
}else{
vehicle.seek(vehicle.target);
vehicle.update();
vehicle.show();
}
}
}
class Vehicle {
constructor(x, y, target) {
//pass x & y the intial positions of the vehicles
this.position = createVector(x, y);
this.velocity = createVector(0, 0);
this.acceleration = createVector(0, 0);
this.r = 10;
this.maxspeed = 4;
this.maxforce = 0.3;
//make a buffer to move away from target when reach
//this.buffer = 100;
// the 3 target the vehicles will seek
this.target = target;
// //change the size as an indecator if its going away or towards target
// this.size= 15;
}
update() {
this.velocity.add(this.acceleration);
this.velocity.limit(this.maxspeed);
this.position.add(this.velocity);
//check edges for flee x
if (this.position.x > width) {
this.position.x = 0;
} else if (this.position.x < 0) {
this.position.x = width;
}
//check edges for flee y
if (this.position.y > height) {
this.position.y = 0;
} else if (this.position.y < 0) {
this.position.y = height;
}
// reset acc
this.acceleration.mult(0);
}
applyForce(force) {
this.acceleration.add(force);
}
applyForce(force) {
//effects speed and directio
this.acceleration.add(force);
}
seek(target) {
let desired = p5.Vector.sub(target, this.position);
desired.setMag(this.maxspeed);
let steer = p5.Vector.sub(desired, this.velocity);
steer.limit(this.maxforce);
this.applyForce(steer);
}
flee(target) {
// Flee away when reaching buffer
let desired = p5.Vector.sub(this.position, target); // Reverse direction
desired.setMag(this.maxspeed);
let steer = p5.Vector.sub(desired, this.velocity);
steer.limit(this.maxforce);
this.applyForce(steer);
}
show() {
//dirc
let angle = this.velocity.heading();
fill(255,random(0,100),0);
stroke(255,random(0,100),random(0,8));
strokeWeight(5);
push();
translate(this.position.x, this.position.y);
rotate(angle);
beginShape();
point(-this.r*4 , -this.r*2);
point(this.r*4 , 0);
vertex(-this.r * 4, this.r*2);
endShape(CLOSE);
pop();
}
}
Sketch:
Future work:
I think there is a lot that can be done with this code, like adding more vocals and targets and experimenting with the different shapes and styles that can be implemented. Also, giving users more agency over the program where they can play around more and interrupt the decisions of the system. I also think it would be nice if I could try it out with the pursue and evade where I can make a buffer area to either evade or pursue the target.
Resources:
The Coding Train. “5.2 Seeking A Target – the Nature of Code.” YouTube, 16 June 2021, www.youtube.com/watch?v=p1Ws1ZhG36g.
For this assignment, I wanted to implement something rather simple, but entertaining to play with. So, to come with some ideas, I reflected upon the material given in class and had a sudden realization that the autonomous agents concepts and examples looked like how ants behave.
So with this realization in mind, I decided to code ants trying to reach their home, while they try to avoid the flashlights.
Randomized movement across the Canva with the help of a flowfield.
At the beginning of the program, an ant will spawn, which will make the final destination the nest. After the ant has moved a certain distance, another ant will spawn. Although, the ants can have some troubles getting into the nest if the flashlights are switched on, since they would have to avoid them. Likewise, to avoid a monotonous movement, the concept of flow fields (using Perlin Noise to help with the random generation) was added into the Canva to let ants move more naturally; their movements are more unpredictable.
Highlight of some code that I am particularly proud of
The hardest part, at least for this code, was to find a way to properly toggle ON and OFF the flashlights, as well as moving them across the Canva. While this seemed like an easy task at first, I forgot that sometimes coding can be hard for no reason. For example, the first flashlight would work, but the second not. Although, if you moved the second flashlight closer to the left side of the canvas, suddenly it worked!
It was confusing, but at the end, this was the code that worked:
First part of the logic (mouse is being pressed on a flashlight to move it):
for (let i = 0; i < flashlights.length; i++) {
flashlights[i].display();
if(mouseIsPressed == true){
if (
mouseX > flashlights[i].position.x &&
mouseX < flashlights[i].position.x + flashlights[i].r + 60 &&
mouseY > flashlights[i].position.y &&
mouseY < flashlights[i].position.y + flashlights[i].r + 180
) {
//Check just in case if clicked element exists.
let index = flashlights.indexOf(flashlights[i]);
if (index > -1) {
flashlights[index].position.x = mouseX-30;
flashlights[index].position.y = mouseY-95;
}
}
}
}
Second part of the logic (mouse is pressed over a flashlight to turn ON or OFF the light):
function mousePressed() {
for (let i = 0; i < flashlights.length; i++) {
if (
mouseX > flashlights[i].position.x &&
mouseX < flashlights[i].position.x + flashlights[i].r + 60 &&
mouseY > flashlights[i].position.y &&
mouseY < flashlights[i].position.y + flashlights[i].r + 180
) {
//Check just in case if clicked element exists.
let index = flashlights.indexOf(flashlights[i]);
if (index > -1) {
if (flashlights[index].state == 0) {
flashlights[index].state = 1;
} else if (flashlights[index].state == 1) {
flashlights[index].state = 0;
}
flashlight_button_sound.play();
}
}
}
}
Reflection and ideas for future work or improvements
I feel that there could be more original ideas and experimentation implemented in this program, since I feel that I only use class material in a very copy and paste fashion. Not to imply that I feel that I did not put effort into this assignment, rather, how I should have gone more outside the box.
Now, regarding the code itself, some improvements are:
Ants spawn in different points in the Y axis.
Ants avoid the flashlights in a more believable manner.
Flashlights can not be overlapped between each other.
In this project, each vehicle acts as an independent agent with three key behaviors:
Cohesion: Vehicles steer toward the average position of nearby flockmates.
Separation: To avoid collisions, vehicles steer away from those that come too close.
Alignment: Vehicles match their direction and speed with nearby flockmates, creating a collective flow.
These behaviors produce an emergent, cohesive movement across the system as vehicles move together as a group while avoiding overcrowding and maintaining some alignment. We also added a wander function with Perlin noise to generate more organic, smooth changes in direction, which softened the rigidness of purely algorithmic steering.
Challenges and Discoveries
One challenge was balancing the behaviors, as the vehicles could either clump together too tightly or spread out too widely. Adjusting the perception radii and scaling each behavior’s force was key in achieving the desired balance, allowing each vehicle to respond naturally to its neighbors while still participating in a larger, collective pattern.
I also experimented with Perlin noise to influence movement subtly. By introducing this form of randomness, each vehicle gained a more fluid, organic quality, reminiscent of natural systems where movement isn’t perfectly uniform. This approach has inspired further experimentation, such as adding color shifts or trails to visualize the vehicles’ paths.
The guest artist lecture this week was enjoyable and inspiring. Their work emphasized that designing with nature often means embracing imperfections and patterns that emerge over time, rather than controlling every element. This philosophy connects well with our coding assignments where we learn that emergent behaviors often arise from simple rules rather than strict control.
From a different perspective, I truly liked how Mujo created a serene yet stimulating environment. I felt as though I could directly relate to an abstract idea like impermanence because of the piece’s tactile quality. I felt as like I was beginning something new each time I touched the surface, but I knew it wouldn’t last, which gave the entire experience a subtle sense of fragility.
This time, I found the process to be really soothing. A contemplative environment was produced by the combination of the ambient sound and the gentle pictures. It helped me realise that interactive art doesn’t have to be overpowering or high-energy; it can be delicate and captivate you gradually, which is precisely its strongest point. Mujo seemed to be encouraging me to take a moment to consider how change, even when it seems gradual, is always there. Since it made the experience more personal and reflective for me, I appreciate that feeling of calm and would love to apply it to my own work.
This project visualizes my travel itineraries between home (Bucharest) and NYU Abu Dhabi. Since I’ve taken this journey multiple times, I wanted to find a way to represent these flights visually, capturing the routes on a map and animating the flights in a way that reflects their random and varied nature. Using p5.js, I created a sketch where autonomous planes travel between Bucharest and Abu Dhabi, tracing their paths as they move. The project reflects my personal connection to these two cities and the significant journeys I make between them.
CODE HIGHLIGHT
One part of the code I’m particularly proud of is the way I handled the autonomous flight paths. The planes move between two coordinates (Bucharest and Abu Dhabi) with a subtle randomness. They don’t all fly in the same direction at the same time, which mimics the variability of real-life flight schedules. I also implemented a smooth transition using p5.Vector.lerp, and each flight leaves behind a trace as it travels, symbolizing the many paths I’ve taken.
Here’s a snippet of the code that makes the flights smooth and random:
This part of the code ensures that each plane follows its route independently, with a delay that makes the animations feel more organic and less synchronized.
You can see the embedded sketch of the project here. It shows multiple flights moving between Bucharest and Abu Dhabi at random intervals, leaving traces behind them on the map.
This project captures a very personal aspect of my life—the regular trips I take between Bucharest and NYU Abu Dhabi. In future iterations, I’d love to add more cities to the map to reflect layovers or other places I’ve visited. I’m also considering adding more interaction, allowing users to click on a city and start a flight from there or adjust the speed and appearance of the planes.
Additionally, I could incorporate real-time data, like actual flight paths or distances traveled, to make the animation even more realistic. Another idea is to create a log of these trips, showing statistics like the number of flights, total distance traveled, or the amount of time spent in the air, adding an informative layer to the artistic representation.
This space simulation project evolved from the foundation of the vehicle sketch code provided on WordPress for current weekly objective, transforming the basic principles of object movement and forces into a more planetary scale simulation. The original vehicle concept was as inspiration for implementing celestial bodies that respond to gravitational forces. By adapting the core mechanics of velocity and acceleration from the vehicle example, I developed a more complex system that models the behavior of various celestial objects interacting with a central black hole. The simulation aims to create an immersive experience that, while not strictly scientifically accurate, captures the wonder and dynamic nature of cosmic interactions.
Process
The development began with establishing the CelestialBody class as the center of the simulation. This class handles the physics calculations and rendering for all space objects, including planets, stars, comets, and the central black hole. I implemented Newton’s law of universal gravitation to create realistic orbital mechanics, though with modified constants to ensure visually appealing movement within the canvas constraints.
The black hole visualization required special attention to create a convincing representation of its extreme gravitational effects. I developed an accretion disk system using separate particle objects that orbit the black hole, complete with temperature-based coloring to simulate the intense energy of matter approaching the event horizon. The background starfield and nebula effects were added to create depth and atmosphere in the simulation.
The implementation process involved several iterations to fine-tune the visual effects and physics calculations. I spent a lot of time on creation of the particle system for the accretion disk, which needed to balance performance with visual fidelity. The addition of comet trails and star glows helped to create a more dynamic and engaging visual experience.
Challenges
One of the primary challenges was balancing realistic physics with visual appeal. True gravitational forces would result in either extremely slow movement or very quick collisions, so finding the right constants and limits for the simulation required careful tuning. Another significant challenge was creating convincing visual effects for the black hole’s event horizon and gravitational lensing without overwhelming the system’s performance.
The implementation of the accretion disk presented its own challenges, particularly in managing particle behavior and ensuring smooth orbital motion while maintaining good performance with hundreds of particles. Creating a visually striking distortion effect around the black hole without impacting the frame rate was also difficult. I spent a lot of time on gravitiational lensing component but despite this could not get it to work like I imagined. However, that is beyond the scope of weekly assignment, and it could be something I would work for bigger timeframe.
Code I’m Proud Of
The following section creates multiple layers of distortion to simulate gravitational lensing:
for (let i = 20; i > 0; i--) {
let radius = this.radius * (i * 0.7);
let alpha = map(i, 0, 20, 100, 0);
for (let angle = 0; angle < TWO_PI; angle += 0.05) {
let time = frameCount * 0.02;
let xOff = cos(angle + time) * radius;
let yOff = sin(angle + time) * radius;
let distortion1 = noise(xOff * 0.01, yOff * 0.01, time) * 20;
let distortion2 = noise(xOff * 0.02, yOff * 0.02, time + 1000) * 15;
let finalDistortion = distortion1 + distortion2;
let spiralFactor = (sin(angle * 3 + time) * cos(angle * 2 + time * 0.5)) * radius * 0.1;
This code combines Perlin noise with circular motion to create a dynamic, organic-looking distortion field that suggests the warping of space-time around the black hole. The layered approach with varying alpha values creates a sense of depth and intensity that enhances the overall visual effect. The addition of the spiral factor creates a more complex and realistic representation of the gravitational distortion.
Reflection and Future Considerations
The project successfully achieves its goal of creating an engaging and visually impressive space simulation. The interaction between celestial bodies and the central black hole creates emergent behaviors that can be both predictable and surprising, making the simulation entertaining to watch. The visual effects, particularly around the black hole, effectively convey the sense of powerful gravitational forces at work.
For future iterations, several enhancements could be considered. Implementing relativistic effects could make the simulation more scientifically accurate, though this would need to be balanced against performance and visual clarity. Adding user interaction capabilities, such as allowing viewers to create new celestial bodies or adjust gravitational constants in real-time, could make the simulation more engaging and educational.
Another potential improvement would be the addition of collision detection and handling between celestial bodies, which could lead to interesting events like the formation of new bodies or the creation of debris fields. The visual effects could also be enhanced with WebGL shaders to create more sophisticated gravitational lensing and accretion disk effects while potentially improving performance.
The addition of sound effects and music could enhance the immersive experience, perhaps with dynamic audio that responds to the movement and interactions of celestial bodies. A more sophisticated particle system could be implemented to simulate solar winds, cosmic radiation, and other space phenomena, further enriching the visual experience.
Additionally, implementing a system to generate and track interesting events in the simulation could provide educational value, helping viewers understand concepts like orbital mechanics and the behavior of matter around black holes.
Listening to lecture about MUJO, I was quote moved by how this multimedia performance piece explores the concept of impermanence through multiple artistic dimensions. The work masterfully integrates dance, projection mapping, and sound in the desert landscape to create a profound meditation on the lasting nature of existence.
The decision to use desert dunes as both stage and canvas is particularly fascinating. The natural formation and erosion of sand dunes serves as a perfect metaphor for the piece’s central theme of impermanence, mirroring the way human experiences and emotions constantly shift and transform. The digital projections that create abstract dunes over real ones cleverly amplify this concept, creating a dialogue between the natural and the digital.
What makes MUJO especially compelling is its dual existence as both a live desert performance and a multi-channel installation. The installation version demonstrates how site-specific art can be thoughtfully adapted for different contexts while maintaining its core message. The multi-channel approach in the installation allows for a more fragmented and intimate exploration of the body’s relationship with elemental forces.
The collaboration between choreographer Kiori Kawai and multimedia artist Aaron Sherwood shows significant effort. The dancers’ movements, as they climb and descend the dunes, physically embody the struggle with constant change, while the immersive soundscape and visuals reinforce this theme. The technical aspects – from projection mapping to sound design – don’t merely serve as technicalities but actively participate in the narrative.
The work draws fascinating parallels between the impermanence of natural phenomena and human existence. Just as sand particles come together to form dunes only to be reshaped by wind, the piece suggests our bodies and thoughts are similarly temporary mediums. This Buddhist-influenced perspective on impermanence is expressed not just conceptually but through every artistic choice in the performance.
Additionally, having an opportunity to ask questions from their direct experience was very helpful as we were able to see not only the steps taken by them but what kind of hindrances they were challenged with throughout. Overcoming those obstacles, whether they are technological limitations or artistic was very interesting to learn and hear about.
While listening to the lecture, I found myself reflecting on how coding concepts can blend seamlessly with real life. By this I mean that, if we apply some real-life principles to our programs, and then demonstrate them with visual references, the result can be interesting.
One example mentioned in the lecture was the illusion of sand falling in a gradual, realistic way. This is only possible thanks to the knowledge accumulated over the years and a creative approach to projecting it using sand as the medium. This made me realize that coding is not just about solving logical problems; it is also about tackling creative ones.