While looking for ideas for generative art I stumbled upon coding train’s playlist by Daniel Shiffman. It was super interesting to see just how many things you can create using simple idea of randomness and how you add different variables to generate seemingly organic patterns. The last video in the playlist was Polar Perlin Noise loops which intrigued me the most. I followed the tutorial and executed the default code which I started messing with to get some interesting results. For example, I changed z_offset and phase values to make the shape spin faster or with greater amplitude, additionally there is a slider to increase the randomness which visually translates to increasing speed. Once I saw the outline I was interesting in not just the changing of the shape but what would happen if background did not override it every time? That way a figure would slowly come to life as we let the code play out. Adding transparency to that aspect made the animation seem smoother and more organic. I changed the color to red and saw how it morphed into circle with spikes after changing amplitude to 10*(cos a + phase). It instantly reminded me of Eye of the Sauron from the Lord of the Rings. Thus I had a picture in mind and a rough plan to execute it. I added more shapes operated by same Perlin noise logic but with different parameters. For example, different phase, color, or shape entirely (latter being very important for the black vertical pupil in the middle of the eye). I then added arcs to imitate the Dark Tower.
I decided to change color (transparency) of the eye, as well as increase its shape if the volume is loud enough. After 6.8 seconds, the camera also begins to work and 0.4 seconds before audio ends, the inner pupil starts to expand. There is also transparency aspect (commented in code) which makes eye more transparent as it comes closer to the subject, the camera overlay is also semitransparent which gives it sort of slow motion effect using tint function.
I followed articles on web to get the capture feed. I had some difficulty with getting camera to work in sync with audio but in the end, simple boolean checks did the trick. Values for audio were just trial and error, as it was for eyesize scaling and volume levels. I am really happy with how it turned out, especially how the audio dialogue matches what is actually happening on screen. (Camera = seen, and death = engulfed in darkness by expanding inner pupil gradually).
I changed few colors and parameters to make it look like the source material and in the end got exactly what I wanted. Perhaps, this is most impressive part of whole code because this is where perlin noise eye animation takes space:
// draw outer shape stroke(255, 10, 0, alphaValue); noFill(); beginShape(); for (let a = 0; a < TWO_PI; a += 0.1) { let xoff = map(10 * cos(a + phase), -1, 1, 0, noiseMax); let yoff = map(sin(a + phase), -1, 1, 0, noiseMax); let r = map(noise(xoff, yoff, zoff), 0, 1, 100, 220) * (eyeSize / 20); // scale based on eyeSize let x = r * cos(a); let y = r * sin(a); vertex(x, y); } endShape(CLOSE); // orange glow for the first outer shape fill(255, orange, 0, alphaValue * 0.5); // lower transparency beginShape(); for (let a = 0; a < TWO_PI; a += 0.1) { let xoff = map(8 * cos(a + phase), -1, 1, 0, noiseMax); let yoff = map(8 * sin(a + phase), -1, 1, 0, noiseMax); let r = map(noise(xoff, yoff, zoff), 0, 1, 0, size_t) * (eyeSize / 20); // Scale based on eyeSize let x = r * cos(a); let y = r * sin(a); vertex(x, y); } endShape(CLOSE); // second glow fill(255, 165, 0, alphaValue * 0.5); beginShape(); for (let a = 0; a < TWO_PI; a += 0.1) { let xoff = map(10 * cos(a + phase + 1), -1, 1, 0, noiseMax); // different phase let yoff = map(10 * sin(a + phase + 1), -1, 1, 0, noiseMax); let r = map(noise(xoff, yoff, zoff), 0, 1, 50, 220) * (eyeSize / 20); // Scale based on eyeSize let x = r * cos(a); let y = r * sin(a); vertex(x, y); } endShape(CLOSE); // inner pupil black which is a vertical ellipse fill(0); // black beginShape(); for (let a = 0; a < TWO_PI; a += 0.1) { let xoff = map(5 * cos(a + phase), -1, 1, 0, noiseMax); let yoff = map(5 * sin(a + phase), -1, 1, 0, noiseMax); let rx = map(noise(xoff, yoff, zoff), 0, 1, 5, 20) * (eyeSize / 20); // Scale based on eyeSize let ry = map(noise(yoff, xoff, zoff), 0, 1, 50, 120) * (eyeSize / 20); // Scale based on eyeSize let x = rx * cos(a); let y = ry * sin(a); vertex(x, y); } endShape(CLOSE); // update zoff and phase zoff += 0.008; phase += 0.008;
All in all, I had lots of fun working on this project and I am very happy with the results. Hope you like it too! Here is the final version: (I would highly suggest opening it on actual browser and giving it camera/microphone permissions for the full animation- https://editor.p5js.org/dt2307/full/krd4mZZqJ)