Assignment 5 – Afra Binjerais

Midterm progress/ proposal

Concept & Design

For this project, I decided to continue exploring the idea of a moving Starry Night. In Week 3, I was inspired by Van Gogh’s The Starry Night and experimented with recreating some of its swirling motion using movers and attractors. I was also influenced by Batool’s work that week, since she implemented what I had envisioned. This time, however, I want to approach the concept differently, instead of movers and attractors, I am experimenting with: Perlin noise to create organic, natural-looking flow and Sine functions to introduce rhythmic oscillation and temporal movement to create the system

Designing the Code (Functions, Interactivity, Structure)

For this week, I experimented with using Perlin noise to determine the directional flow of the strokes and sine functions to introduce subtle rhythmic movement over time. There are no classes yet, since I decided not to use movers or attractors in this version of the system. For interactivity, I am planning to implement a slider to control movement strength or direction, possibly another slider to adjust speed or oscillation intensity, and a slider that changes the color palette. I am still deciding what additional controls would be meaningful without overcomplicating the system. 

States & Variations

As mentioned, I will have sliders and that will help me to produce different variations/ states to my system. I am thinking of creating variations such as:

  • Different movement speeds
  • Opposite swirl directions
  • Stronger vs softer oscillation
  • More chaotic vs more calm motion

Also, for the printing part, I’m thinking of having an icon on the top, so when I’m satisfied with how it looks, I will click on the icon and it will download as png to later print. 

Most Uncertain Part

The most complex and uncertain part for me was deciding whether to use movers and attractors again. In Week 3/4, I used attractors and movers; however, I remember that when I tried to recreate the motion of Starry Night using movers, it became very complex very quickly. Controlling behavior while maintaining aesthetic control was difficult with attractors and movers; at least for me. To minimize risk here is to either find an alternatives (which is where im headed) or learn new ways to use them. 

I provided below the current starting point, still a skeleton version, no interactions yet but its a vision to where I’m headed. 

Assignment 5

Concept

On brainstorming for my midterm project, thought of mimicking nature with my sketches, as I’ve done for a few of my sketches so far. I was intrigued by particle systems, which I felt like I had actually unknowingly used in my ocean sketch. I wanted to do something with particle systems again, pairing it up with another concept we’ve covered in class. I debated between two ideas: a mycelium system, or a cosmic dust cloud. I settled on the cosmic dust idea because I felt like the prints or images coming out if it could be very painterly.

So the idea is a generative system that simulates the birth, motion, and destruction of cosmic dust. The goal is to create a painterly vacuum: a space that feels filled with fluid-like gas clouds. By combining Particle Systems with Newtonian Forces (Gravity/Drag) and Periodic Oscillation, I want to produce high-resolution prints that capture the shimmer of deep-space photography.

Sketch

Implementation

  • The Particle Engine: I’ve developed a Dust class that handles movement and lifespan. Each particle is aware of its age, allowing the system to constantly recycle itself and stay generative.

  • The Different States: I’ve established three distinct states: NURSERY (Noise-driven movement), SINGULARITY (Point-based gravity), and SUPERNOVA (Radial repulsion).

  • Oscillation: I integrated Sine waves into the strokeWeight of the particles to create a subtle “twinkling” or shimmering effect that mimics starlight.

The Frightening Part & Risk Reduction 

One of the core requirements for the midterm is having multiple distinct operating modes. I handled this by implementing a State Machine – a logic structure that allows the entire physics engine of the sketch to change based on a single variable (state).

In my draw() loop, the particles check the current state of the “universe” every frame, instead of moving randomly. Using an if/else if structure tied to the keyPressed() function, I can switch between NURSERY, SINGULARITY, and SUPERNOVA instantaneously. This was a challenge at first because I had to ensure that the particles didn’t just break when the forces changed; by using p5.Vector and applyForce(), the transition between modes feels fluid, as the particles’ existing momentum carries over into the new force field.

Additionally, one of the most uncertain parts of this project is ensuring that the complex, high-particle-count visuals can be exported at A3 resolution without crashing the browser. If I simply used saveCanvas(), the print would be blurry and pixelated.

I settled on implementing a p5.Graphics buffer (canvasBuffer). This allows me to develop the logic on a small, fast 800 * 600 canvas while maintaining a hidden, high-resolution print-ready canvas in the background. My current save logic triggers on the ‘S’ key:

function saveHighRes() {
  canvasBuffer.background(240, 80, 5);
  // Future implementation:
  // for (let p of particles) { p.drawToBuffer(canvasBuffer); }
  canvasBuffer.save("nebula_output.png");
}

Instead of saving the current frame, I plan to loop over the particles one final time and re-render them onto this larger 1600 * 1131 canvas. This upscaling is a critical risk-reduction step that gives me peace of mind for the final physical print.

Another frightening part is thinking about how I’m going to make this sketch interesting enough to be a final midterm piece. Right now, it’s a skeleton. However, the basic physics – the drag (friction) that makes particles feel like they are moving through thick gas, and the Oscillation (shimmer) that makes them feel like stars – are already providing a strong foundation. I’m trusting the process: once the math is solid, the beauty usually follows in the polishing phase where I’ll play with color gradients and force magnitudes.

Saeed Lootah – Assignment 4

Concept

For this Assignment I was unsure where to start for a long time. I knew I was going to use sound in some way and that of course there would be harmonic motion. I also wanted to make my animation centered around a circle in some way.

Whilst reading through Memo Atken’s work on Simple Harmonic Motion I noticed at the end he mentioned “the fourier series”. For a physics experiment back in high school at one point we used a technique called the Fast Fourier Transform (FFT) to analyze audio by breaking it up into individual sin waves of multiple frequencies which when added together would produce the sound that we were hearing. After some searching I realized that p5.js has the FFT feature built in and so I decided I would use it.

Sketch

Unfortunately there are some small issues: Microphone doesn’t work unless you give permission through your browser, and as for the sound because I had to compress it to be less than 5 MB it doesn’t have the full range of frequencies that it’s supposed to.

Highlight
fft.analyze();
  fftArray = fft.linAverages(resolution);

  let x = width / 2;
  let y = height / 2;
  let radius = 180;

  for (let index = 0; index < resolution; index++) {
    let progress = index / resolution;
    let theta = TWO_PI * progress;

    push();
    let selected = frequencyArray[index];
    translate(x, y);
    rotate(theta);
    noStroke();
    // fill(0, 0, 255);
    selected.show(radius, 0);
    selected.updateFFT(fftArray[index]);
    pop();
  }

This is my personal favorite part of the code. What it does is initialize the

Milestones

This was my initial experiment with using harmonic motion for a rectangle. I created a class called FreqLine (which I ended up using at the end) and all it did was have a sin wave which would change the height of the rectangle accordingly.

This is my second milestone, after getting the rectangles to work I made multiple and turned them into a circle shape. Each rectangle followed a different frequency which created an interesting effect as they rectangles would start in phase of each other and then go out of phase and come back.

At this point I experimented with different rectMode()’s trying CENTER, TOP, BOTTOM. Top and bottom gave the same result (which is what you see in the screenshot) whereas for CENTER it wasn’t as interesting in my opinion.

In the end I decided I would use TOP but I would make the width’s negative so that the rectangles would increase towards the center as I enjoyed that look the most.

Reflection

I wish I didn’t stop myself from starting. In the beginning I wasn’t sure what to do and I felt that if I didn’t have an idea that was good enough I wouldn’t start. Looking back and for upcoming assignments I plan on starting with any idea and being willing to do multiple sketches if I have to. It wasn’t until I had started with the circle idea that I thought to use the FFT and it wasn’t until I started the FFT did I decide to include the option to alternate between a song and a microphone. While they seem simple I think with the midterm coming up its important that I start experimenting.

Assignment 4

Concept

For this assignment, as stated in the assignment prompt, I was inspired by Memo Akten’s Simple Harmonic Motion series, where he uses pure mathematical oscillation to create hypnotic, organic visuals. His work showed me that layering simple sine waves together can produce patterns of unexpected complexity and beauty. My concept, “Ripple Interference,” simulates what happens when you drop multiple stones into a still pond at the same time — each stone creates circular ripples, and where those ripples meet they either amplify or cancel each other out. This interference is pure Simple Harmonic Motion: every circle on the grid is oscillating up and down in size and color based on the sum of sine waves reaching it from multiple sources. The result is a constantly shifting, living pattern that feels organic despite being entirely mathematical.

SKETCH

Code Highlight

The section I’m most proud of is the wave interference calculation at the heart of the sketch:

let waveSum = 0;
 for (let src of waveSources) {
   let d = dist(x, y, src.x, src.y);

   // Each source creates a radial sine wave
   waveSum += sin(d * 0.04 * src.freq - time * src.speed);
 }

 // Normalize so value stays between -1 and 1
 waveSum /= waveSources.length;

This is the core of everything. For each point in the grid, I measure the distance to every wave source, then plug that into a sine function. The distance replaces the linear progress variable from the example we did in class, turning the flat wave into a circular ripple spreading outward. When multiple sources combine, their values add together — this is wave superposition, the same principle that creates interference patterns in real physics. Dividing by the number of sources at the end keeps the value normalized between -1 and 1, which feeds cleanly into the same map(sin(…)) color technique from the class example. What I love about this is that a single line of math creates emergent visual complexity from something simple.

Milestones and Challenges
Milestones

  • Extended the example we did in class from a single 1D wave row to a full 35×35 2D grid of oscillating circles
  • Successfully implemented radial wave propagation (circular ripples instead of flat waves)
  • Combined multiple wave sources using superposition to create interference patterns
  • Retained and extended the class example’s sine-based color mapping into 2D
  • Added interactive wave source placement with click

Challenge 1: Going from 1D to 2D
The example from class maps i linearly across the x-axis to get the wave position. When I first tried expanding this to a grid, I simply ran two nested loops and used the row number j for a second wave — but this just created a grid of identical horizontal waves stacked on top of each other, which looked flat and uninteresting. The breakthrough was switching from using the grid position directly to using the distance from a source point. Replacing width * progress with dist(x, y, src.x, src.y) inside the sin() function was what made the waves actually radiate outward like real ripples.

Challenge 2: Balancing the Interference Pattern
Once multiple wave sources were working, the interference looked chaotic — the colors and sizes were flickering too rapidly with no visual coherence. The problem was that summing multiple sine waves was pushing the total value well beyond -1 to 1, making the map() calls produce extreme values. Dividing waveSum by the number of sources normalized it back to a usable range. This small fix made a dramatic difference — the patterns became smooth, readable, and beautiful instead of noisy.

Reflection and Future Improvements
This assignment taught me how much complexity can emerge from a single mathematical operation repeated in different configurations. The sine function is doing all the real work here — everything else is just deciding where to sample it and what to do with the result. Memo Akten’s work resonates more deeply now because I understand how restraint in the tools you use forces you to be more creative with how you use them.
Below are some ideas I would implement in the future:

  • Moving wave sources – Sources that drift slowly across the canvas, constantly changing the interference pattern
  • Mouse-responsive waves – The mouse position acts as a live wave source that follows your cursor
  • Frequency controls – Sliders to adjust each source’s frequency in real time
  • Different grid shapes – Hexagonal or circular grids instead of square
  • Sound integration – Map wave amplitude to audio frequency for a visual synthesizer

Salem Al Shamsi – Assignment 4

The Conductor — Invisible Hand

Concept & Inspiration :

For this project, I was inspired by Memo Akten’s “Simple Harmonic Motion #12 for 16 Percussionists.” In that piece, 16 performers stand on stage, and each one receives instructions from a computer through their earpiece when to step forward, when to hit their drum, and when to turn on their flashlight. They don’t know what the bigger picture looks like. They just follow their own simple instructions. But when you watch all 16 of them together, something beautiful and complex emerges.

I wanted to translate that idea into a visual sketch. Instead of real performers, I have 16 glowing nodes arranged in a circle. Instead of a computer sending cues through earpieces, I have an invisible point, “the conductor,” that orbits around them. When it gets close to a node, that node lights up. The conductor’s path starts clean and orderly, then gradually becomes chaotic and unpredictable, then returns to order. And the viewer can use their mouse to become a second conductor, creating a tension between human control and machine control.

 

Code I’m Proud Of:

getActivation() method from the Shockwave class:

getActivation(nodeX, nodeY) {
    let d = dist(this.x, this.y, nodeX, nodeY);
    let distFromWave = abs(d - this.radius);

    if (distFromWave < this.activationWidth) {
      return map(distFromWave, 0, this.activationWidth, 1, 0);
    }
    return 0;
  }

What this does is pretty simple: it checks how far a node is from the wave’s current edge. If the node is within the wave’s “thickness” (60 pixels), it gets activated stronger if it’s right on the edge, weaker if it’s at the boundary. If it’s outside that band, nothing happens. So when you click, the ring expands outward and each node lights up only when the wave actually reaches it, not all at once. That one small function is what makes the whole click interaction feel like dropping a stone in water instead of just flipping a switch.

Milestones and Challenges

Stage 1: Circle of 16 Nodes. I placed 16 nodes evenly around a circle using cos() and sin(). The main challenge was getting the spacing right and making the glow effect look soft instead of harsh.

Stage 2: The Invisible Conductor I added an invisible point that orbits the circle and activates nearby nodes based on distance. The tricky part was making the activation smooth without lerp(), the nodes just snapped on and off like a light switch, which looked bad.

Stage 3: Hit Flashes and Ripples. When a node reaches peak activation, it now flashes bright and sends out a ripple ring. The challenge was making sure the hit only fires once per pass, not every single frame while the conductor is nearby. I solved this with a simple flag called wasAboveThreshold.

Stage 4: Order to Chaos to Order. This is where it got interesting. I made the conductor’s path morph between a circle, a figure-8, and random noise over a 35-second cycle. Getting the blending right took some trial and error at first; the transitions were too sudden, and it looked glitchy instead of smooth.

Embedded Sketch

Stage 5:Polish and Mouse Interactivity I added connection lines between nodes, a center glow, film grain, and mouse interactivity. The mouse acts as a second conductor with a smaller reach (so you can only affect a few nodes at a time). Clicking sends a shockwave. The biggest challenge here was combining three different activation sources (machine, mouse, and shockwave) without them conflicting with each other.

Reflection

This project taught me a lot about how simple rules can create complex-looking behavior. Each node only knows one thing: how far it is from the conductor, but when you watch all 16 together, it looks like something alive. That’s basically the same idea behind Akten’s original piece, and I think that’s what makes it so powerful. If I had more time, I’d love to add sound, maybe a soft tone or drum hit when each node gets activated, so it feels more like an actual performance. I’d also like to try it with more nodes (maybe 32 or 64) to see how the patterns change. Another idea is to let the viewer switch between order and chaos manually using a key press, so they have even more control over the composition.

Amal – Assignment 4: Tadpole Anemone

Concept + Inspiration

This project was created in response to the assignment requirement to use Memo Akten’s exploration of Simple Harmonic Motion as a starting point. The goal was to understand how oscillation can function as a generative system rather than just a repetitive mechanical movement.

What I found most interesting in Memo Akten’s work is how simple sine functions can create complexity when layered together. Instead of treating harmonic motion as a physics simulation, I treated it as a compositional structure. I used multiple oscillators with different frequencies and phase shifts so that interference patterns would emerge naturally from the system.

Rather than keeping the study abstract, I translated the oscillatory behavior into a tidepool ecosystem. Each anemone is driven by overlapping harmonic functions that simulate tidal pull, organic sway, and micro-movements within the tentacles. A slower oscillator controls the bloom intensity, which creates a bioluminescent effect that feels ecological instead of mechanical.

Sea Anemones shine with fluorescence! – Mendonoma Sightings

I was mainly inspired by their bioluminescence and the way they sway so beautifully in the water, this is the image the drew the majority of the inspiration and what I’ve tried to mimic. However when I was done, I realized that this looks like the small lights in the new near by gas station “the hub”, makes me wonder if the inspo was more subconscious??

Code I Am Particularly Proud Of

One section of code I am particularly proud of is the multi-layer harmonic interference that drives the tentacle motion:

// I used AI to help achieve this :)
let oscA = sin(this.t * 1.55 + p);
let oscB = sin(this.t * 0.92 + p * 0.7 + this.phase);
let oscC = sin(this.t * 0.45 + p * 1.9);

let sway = (0.75 * oscA + 0.45 * oscB + 0.25 * oscC + this.current) * 0.65;

I used AI to help refine this blending logic. My intention was to create layered oscillation instead of a single sine-driven movement. The AI-assisted refinement helped structure the interference in a way that felt balanced without becoming chaotic.

This section is important to me because it shifts the system from predictable motion to something that feels organic. By combining oscillators with different frequencies and phase relationships, the movement becomes more ecological and less mechanical while still remaining mathematically grounded in Simple Harmonic Motion.

The Sketch

Milestones and Challenges

The first milestone was building a single anemone driven by one primary harmonic function. The focus at this stage was verifying that the tentacle sway behaved consistently and that amplitude, frequency, and phase shifts were functioning correctly.

Challenge: The motion initially felt mechanical and repetitive. With only one oscillator driving the movement, the behavior was too predictable and visually flat. This prototype helped me understand the limitations of a single harmonic source.

[gif to be added but the upload time is sooooo looooooonnnnggg :/]

instead here is a video:

The second milestone introduced multiple oscillators and expanded the system into a grid of anemones. This is where interference patterns began to emerge. I layered harmonic functions with different frequencies and phase offsets to create more complex sway.

Challenge: Balancing complexity without losing control. When too many oscillators were layered equally, the movement became chaotic. I had to adjust weight values so the system felt organic rather than noisy.

This stage also introduced the idea of a global tide and a directional current controlled by mouse movement.

Reflection and Ideas for Future Work

Through this project, I began to understand how Simple Harmonic Motion can function as a structural system rather than just a visual effect. Layering oscillators changed the behavior significantly, and small adjustments in frequency and phase created noticeable shifts in organic quality. The process reinforced how sensitive generative systems are to parameter balance.

One of the main insights was that complexity does not require complicated formulas. It requires intentional relationships between simple ones. When I reduced or reweighted oscillators, the motion became more coherent. When I over-layered them, the system lost clarity. This balance became central to the final result.

For future development, I would like to explore local interaction between anemones instead of having them behave independently. Introducing neighbor-based influence could allow wave propagation across the field. I am also interested in integrating audio input so harmonic frequencies respond dynamically to sound.

Finally, I would experiment with scaling the system for projection or installation, where viewer proximity could influence tide intensity or bloom cycles. Expanding the system spatially would shift it from a screen-based study into an environmental experience.

Assignment 4

Concept

Inspired by Memo Akten, my concept is to simulate a row of searchlights fixed along the bottom of the screen. Each light oscillates back and forth using a sine wave.

The “personality” of the piece comes from the Phase Offset. Because each light starts its swing a fraction of a second after the one next to it, the beams create a harmonic wave effect as they sweep across the sky. I used high-transparency triangles to mimic the look of light passing through fog or haze.

Memo Akten’s Simple Harmonic Motion #6 Performance proof of concept (2011)

The Sketch
  • Watch: The beams move in a synchronized wave.

  • Interact: Move the Mouse to change the “Spread” of the lights. Moving Right makes the wave more chaotic; moving Left brings them into perfect sync.

  • Click: To change the color palette from “Cold Searchlight” to “Warm Amber.”

Code Highlight

I am particularly proud of the display() function for the beams. Instead of drawing a simple line, I used a gradient-like approach with multiple triangles. This creates the “glow” effect where the light is brightest at the source and fades as it stretches into the dark.

display() {
    // We use a loop to draw layers of the beam for a glow effect
    for (let i = 8; i > 0; i--) {
      // As i gets smaller, the beam gets thinner and brighter
      let alpha = map(i, 1, 8, 15, 2); 
      let beamWidth = i * 4;
      
      fill(50, 20, 100, alpha); // Soft white/blue glow
      noStroke();
      
      push();
      translate(this.x, this.y);
      rotate(this.angle);
      
      // Draw the triangular beam of light
      triangle(0, 0, -beamWidth, -height, beamWidth, -height);
      pop();
    }
  }
Milestones

This was my first attempt. I successfully mapped the Sine wave to the rotation of a line, but it felt very mechanical and flat. There was no phase offset yet, so every light moved in perfect, boring unison.

In this version, I moved away from the basic “sticks” and made it actually look like a light. By drawing multiple overlapping triangles with very low opacity, I create the illusion of light scattering through a hazy or smoky sky. All that’s left is the interaction!

Reflection and Future Work

This assignment highlighted how much “mood” can be created with very little code. By using Simple Harmonic Motion to drive rotation and layering simple shapes with transparency, I created a complex, atmospheric environment. Future improvements:

  • Particle Haze: I would like to add tiny “dust” particles that catch the light only when the beam passes over them.

  • Sound-Reactive: Having the beams “grow” or “shrink” in length based on the volume of a music track.

  • 3D Volumetric: Moving this into a 3D space where the beams can rotate on two axes, allowing them to point toward or away from the viewer.

Buernortey – Assignment 4

Concept and Inspiration

This sketch is inspired by my high school physics experiments with simple harmonic motion, especially spring–mass systems. In those experiments, we measured displacement and time as a mass oscillated back and forth, but the motion was mostly understood through formulas and graphs.

For this project, I wanted to recreate that experiment visually and interactively. Instead of only calculating values, my goal was to simulate the motion directly and allow parameters like amplitude, oscillation rate, and damping to be adjusted live. This turns the physics formula into something observable and explorable.

The final result is an interactive spring–mass simulation driven directly by the SHM equation.

Development Stages

Below are the main stages of how the sketch evolved:

Stage 1 — Basic Oscillation

Purpose: Test the SHM formula using a moving dot. This stage ensures the sine-based oscillation behaves as expected.

Stage 2 — Spring–Mass Visualization

Purpose: Represent the physics lab setup visually. A zig-zag spring connects the mass to equilibrium, giving a realistic spring–mass system.

Stage 3 — Interactive SHM with Damping

Purpose: Add interactivity and more realistic physics. Users can control amplitude, oscillation rate, and damping. Motion trail shows past displacement.

Code Highlight

The most important line in the final sketch is:

let x = equilibrium + amplitude * sin(angle) * exp(-damping * time);

This combines harmonic oscillation(sin()), displacement scaling (amplitude), and exponential decay (damping). It directly translates the physics model into animation behavior.

Final Sketch

In the final version, the sketch became fully interactive. Users can adjust sliders to control amplitude, oscillation rate, and damping, and a motion trail shows the displacement history over time. The spring–mass system is visualized clearly, and all variable names are stable to avoid conflicts with p5.js reserved functions.

Challenges Encountered

During development, the first connector looked more like a rope than a spring, so I had to adjust the zig-zag design to make it visually accurate. Balancing the damping values took several tests to ensure the motion was realistic but not too quick to stop. Slider ranges also needed careful tuning to prevent unstable or jittery motion. Additionally, the variable originally named speed conflicted with a reserved p5.js function, requiring a rename to rate. Each challenge was solved through iterative testing and visual adjustments to maintain smooth and accurate motion.

Reflection and Future Improvements

This project helped me understand simple harmonic motion more intuitively than through static formulas. Seeing the oscillation respond to parameter changes in real time made the relationships between amplitude, speed, and damping much clearer. For future improvements, I would add a live sine graph alongside the spring, a toggle to switch to a pendulum mode, multiple coupled springs to simulate more complex systems, and data readouts similar to a virtual lab tool to record measurements and analyze motion quantitatively.

Haris – Assignment 4

Concept

When looking at Memo Akten’s work there was one piece that really took my attention. It was the “Simple Harmonic Motion #12 (2015)”. This although seemingly simple work with people hitting drums with their flashlight really caught my attention by the way it was done with playing with the lights and having all the actors “controlled by the computer”. So I decided that this is the perfect piece to somehow try to recreate in a similar way in p5.

Process

To begin with i first made the “map”. I decided to go with a “stage” looking design to fit the theme so to create something that fits what I imagined I made a completely black background with gray boxes to indicate stages where the people could move.

After it was time to create the performers. I created a performer class where I could create each of the human like performers that would move around the screen. I also decided to give them hands to give them some personality.

At first I experimented with random timing for movements, but this did not feel intentional or rhythmic. The animation looked chaotic rather than composed. To fix this, I replaced random triggers with sine wave oscillators, meaning that every action was driven by harmonic motion instead of randomness.

let g = (sin(frameCount * 0.02 + this.pid * 0.7) + 1) * 0.5;
this.onStage = g > 0.65;

let a = (sin(frameCount * 0.05 + this.pid) + 1) * 0.5;
this.armTarget = a > 0.75 ? 1 : 0;

After motion was working, I implemented the spotlight effect. Rather than drawing a single circle of light, I layered multiple translucent ellipses. This creates a gradient glow that visually resembles stage lighting and feels more organic than a flat shape.

The final step was adding sound. Each time a performer raises their arms while in the spotlight, a drum sound is triggered. I detect this moment by checking for a transition from arms-down to arms-up. This event-based logic ensures the sound only plays at meaningful moments instead of continuously.

Code Highlight

The proudest part of my project was definitely the movement.

let g = (sin(frameCount * 0.02 + this.pid * 0.7) + 1) * 0.5;
this.onStage = g > 0.65;

let a = (sin(frameCount * 0.05 + this.pid) + 1) * 0.5;
this.armTarget = a > 0.75 ? 1 : 0;

I am particularly proud of this part because it captures the entire conceptual idea in just a few lines of code. Instead of manually controlling each performer or assigning scripted movements, each performer is moved by a sine wave with a slightly different phase offset. This means every performer follows the same rule but behaves differently over time.

Future Improvements

If I were to continue working on this project I would maybe like to add some interactivity into it. At this point in time I am not sure how interactivity would work on this project and in what sense it could be implemented so I would love some suggestions. I would also maybe work a bit on visuals, but overall I am really happy with the final project.

Assignment 4: Harmonic Motion

My inspiration comes from Memo Akten’s Simple Harmonic Motion 8-9.  I wanted to build something that felt alive on screen. Waves have always fascinated me, specifically the way two simple oscillations combine into something unexpectedly complex. I used the principles of adding waves together such that: peak + peak = higher peak, trough + trough = lower trough, and peak + trough cancel each other.

Please move mouse around and click around the sketch for special effects.

I started with the simplest possible thing: a single sine wave drawn as dots across the canvas.

I added a second wave with a different frequency and a phase offset of PI, so it starts on the opposite side of the cycle from wave one. The two waves drift in and out of sync as time moves forward. Wave two has a slightly higher frequency and a smaller amplitude, so it has its own distinct character.

This is where things got interesting. I added a third wave that is the sum of the first two. When the two source waves push in the same direction, the result amplifies. When they oppose each other, they cancel out. The yellow interference wave is the visual record of that conversation between the two.

let y3 = y1 + y2;
stroke(255, 220, 80);
strokeWeight(5);
point(x, height / 2 + y3);

 

I replaced the solid background() call with a semi-transparent rectangle drawn each frame. The old frames fade slowly instead of disappearing instantly.

fill(10, 10, 20, 25);
noStroke();
rect(0, 0, width, height);

I replaced fixed amps with values that oscillate slowly over time using a second, slower sine function.

let amp1 = map(sin(t * 0.3), -1, 1, 40, 130);
let amp2 = map(sin(t * 0.17 + PI), -1, 1, 30, 110);

The two amplitudes breathe at different rates, so they are never in sync with each other. The interference wave gets dramatically more expressive as a result, going nearly flat at times and spiking wide at others.

 

I switched to HSB color mode and tied the hue and brightness of each point to its position in the wave cycle.

let hue1 = map(sin(x * 0.01 + t * 0.5), -1, 1, 160, 220);
let bright1 = map(abs(y1), 0, amp1, 60, 100);

I expanded from two base waves to four, each with its own frequency, speed, phase, and breathing amplitude. I also added two partial interference sums alongside the full sum of all four.

The frequency ratios across the four waves are close to harmonic but slightly off, so the pattern never fully repeats. There is always something new happening somewhere on the canvas.

let yA    = ys[0] + ys[1];
let yB    = ys[2] + ys[3];
let yFull = ys[0] + ys[1] + ys[2] + ys[3];

 

At this point the draw() loop was getting hard to read. I pulled the repeating logic into dedicated functions. All wave parameters moved into a waveDefs array of objects.  I also moved the array definition inside setup().

 

function waveY(x, def, amp) { ... }
function breathingAmp(i) { ... }
function driftHue(x, hueMin, hueMax, freqX, freqT) { ... }
function drawWavePoint(x, y, centerY, hue, sat, maxAmp, weight, alpha) { ... }
function drawSumPoint(x, y, centerY, maxDisplace, hueMin, hueMax, sat, weight, alpha) { ... }
 This is the highlight of code im proud of.
  ampPhases = [0, PI, HALF_PI, PI / 3];

  waveDefs = [
    { freqX: 0.020, freqT: 1.0, phase: 0,        ampMin: 40, ampMax: 130, hueMin: 160, hueMax: 220, weight: 3 },
    { freqX: 0.035, freqT: 1.5, phase: PI,        ampMin: 30, ampMax: 110, hueMin: 260, hueMax: 320, weight: 3 },
    { freqX: 0.055, freqT: 0.7, phase: HALF_PI,   ampMin: 20, ampMax:  80, hueMin: 100, hueMax: 160, weight: 2 },
    { freqX: 0.013, freqT: 2.0, phase: PI / 4,    ampMin: 15, ampMax:  60, hueMin:  20, hueMax:  60, weight: 2 },
  ];
}

function waveY(x, def, amp) {
  return sin(x * def.freqX + t * def.freqT + def.phase) * amp;
}

function breathingAmp(i) {
  return map(sin(t * ampSpeeds[i] + ampPhases[i]), -1, 1, waveDefs[i].ampMin, waveDefs[i].ampMax);
}

function driftHue(x, hueMin, hueMax, freqX, freqT) {
  return map(sin(x * freqX + t * freqT), -1, 1, hueMin, hueMax);
}

function drawWavePoint(x, y, hue, sat, maxAmp, weight, alpha) {
  let bright = map(abs(y), 0, maxAmp, 55, 100);
  stroke(hue, sat, bright, alpha);
  strokeWeight(weight);
  point(x, height / 2 + y);
}

function drawSumPoint(x, y, maxDisplace, hueMin, hueMax, sat, weight, alpha) {
  let hue = map(y, -maxDisplace, maxDisplace, hueMin, hueMax);
  let bright = map(abs(y), 0, maxDisplace, 60, 100);
  stroke(hue, sat, bright, alpha);
  strokeWeight(weight);
  point(x, height / 2 + y);
}

I added a mousePull() function that bends each wave point toward the cursor. The pull strength falls off with distance, reaching zero at 200 pixels away. Moving the mouse slowly across the canvas bends the waves toward it in a way that feels physical. The effect fades naturally so it never feels abrupt. The final step was a Ripple class. Clicking spawns a ring that expands outward from the click position and displaces any wave point it passes through. The ring has a bandwidth of 40 pixels. Points inside that band get displaced, everything outside it is untouched. The ripple fades as it expands and gets removed from the array once it exceeds its maximum radius. Multiple ripples can coexist and their displacements stack on top of each other.

function mousePull(x, baseY, centerY) {
  let dist = sqrt(dx * dx + dy * dy);
  let influence = constrain(map(dist, 0, 200, 60, 0), 0, 60);
  return dy * (influence / max(dist, 1)) * -1;
}


class Ripple {
  constructor(x, y) {
    this.radius = 0;
    this.speed  = 4;
    this.maxRadius = 300;
    this.strength  = 55;
  }

  influence(px, py) {
    let ring = abs(dist - this.radius);
    if (ring > 40) return 0;
    let fade    = map(this.radius, 0, this.maxRadius, 1, 0);
    let falloff = map(ring, 0, 40, 1, 0);
    return sin(ring * 0.3) * this.strength * fade * falloff;
  }
}

 

For future improvements, I want to make the waves responsive to music/notes. I think this can be done by using Fast Fourier Transform which I’ve used and written a paper about before in one of my INTRO TO IM projects. it would work by processing the whole audio file once then storing the data in a queue and mapping each frequency band to an intersection between waves. I really wanna try this but it seems a little bit challenging so I didnt implement it in this sketch.