art
generative
fxhash
Ephemeral Whirls – Generative Art through Loops and Motion
written by reyrove
07 Oct 2024100 EDITIONS
1 TEZ
The Initial Setup: Creating the Canvas
const e = Math.min(innerWidth, innerHeight);
const canvas = {};
canvas.w = e;
canvas.h = e;
-
Defining the canvas size
: The variable
e
is set to the smaller of the screen's width and height. This ensures that the canvas is always a square, regardless of your browser window's aspect ratio. It becomes the stage where our shapes will dance.
Features and Global Variables
const features = {};
var loopers;
var backgroundColor;
- ``** object**: This is where we’ll store the various properties of the art, such as background color, loop colors, and the number of loopers.
-
Global variables
:
loopers
will store our shapes, whilebackgroundColor
sets the stage for each drawing.
The Setup Function: Laying the Groundwork
function setup() {
createCanvas(e, e);
-
setup()
: This function initializes the canvas, drawing it to the size ofe
(set earlier to the smaller screen dimension), ensuring that the artwork is responsive across different devices.
Choosing Colors: Palette of Pastels and Vibrancy
const backgroundColours = ['#F5F5F5','#9AFEFF','#AAF0D1', ...];
const backgroundNames = ['WhiteSmoke (W3C)','Electric Blue','Magic Mint', ...];
const backgroundIndex = Math.floor($fx.rand() * backgroundColours.length);
features.backgroundColour = backgroundColours[backgroundIndex];
- Background colors : A beautiful palette of pastel colors is prepared to form the canvas’s backdrop. Each time the code runs, a random color from this list is chosen, setting a unique mood for the visual experience.
-
Random background
: The
backgroundIndex
variable selects a random color from the list, ensuring that no two artworks look alike.
const LooperColor = [['#9F21DE','#60DE21'], ['#F20DB2','#0DF24D'], ...];
features.LooperColors = LooperColor[Math.floor($fx.rand() * LooperColor.length)];
-
Looper colors
: Each "looper" (a swirling, moving line) gets its own color scheme. The
LooperColor
array holds multiple color pairs or groups, and just like with the background, a random set is chosen every time.
Building the Loopers: Our Dancing Shapes
features.NumberOfLoopers = Math.floor($fx.rand() * 2 + 23);
features.Time = Math.floor($fx.rand() * 2000 + 2000);
- Number of loopers : Each drawing will feature between 23 and 25 loopers, the swarming entities that will move and paint across the canvas.
-
Duration of loops
: The
Time
feature controls how long the loopers will animate, creating variations in the density and complexity of the final artwork.
loopers = [];
for (let i = 0; i < features.NumberOfLoopers; i++) {
loopers.push(new Looper(0, $fx.rand() * e));
}
- Creating the loopers : The loopers are stored in an array, each one positioned randomly along the canvas’s height. These loopers will later define the motion, each with its own speed, heading, and color.
Drawing the Scene: Orienting and Setting the Stage
if (features.side == 0) {
rotate(Math.PI / 2);
translate(0, -e);
} else if (features.side == 1) {
rotate(-Math.PI / 2);
translate(-e, 0);
}
-
Side rotation
: The canvas is rotated based on a random selection from four possible sides (
up
,down
,left
, orright
). This adds an additional layer of randomness to the composition, tilting the viewer’s perspective.
background(features.backgroundColour);
strokeWeight(e / ($fx.rand() * 200 + 200));
- Setting the background and stroke weight : Once the canvas is oriented, the background color is applied. The stroke weight determines the thickness of the lines drawn by the loopers, varying based on random values to add depth and richness to the composition.
The Magic of Motion: Loopers Take Their Step
for (let i = 0; i < features.Time; i++) {
drawOnce();
}
- Animating the loopers : For the duration defined in the setup, the loopers move and draw. This ensures that the artwork evolves over time, with each frame contributing to the overall image.
for (const looper of loopers) {
looper.step();
looper.draw1();
}
-
Stepping and drawing
: Each looper has its own
step()
anddraw1()
methods, which define their movement and drawing behaviors. As they move, they leave behind a colorful trail, forming intricate patterns.
The Looper Class: The Heart of Motion
class Looper {
constructor(x, y) {
this.x = x;
this.y = y;
this.heading = 0.1;
this.speed = $fx.rand() * (e / 1000);
this.sinOffset = $fx.rand() * 1000;
this.loopingSpeed = $fx.rand() * (0.025, 0.1) * ($fx.rand() < 0.5 ? 1 : -1);
}
-
Creating a looper
: The
Looper
class defines the properties of each looper—its position, speed, heading, and even the "looping speed" that determines how it moves across the canvas. -
Sinusoidal motion
: By incorporating sine functions (
sinOffset
), the looper’s path becomes wavy, creating a natural, organic flow in the artwork.
step() {
if (this.looping) {
this.heading += this.loopingSpeed + sin(this.sinOffset + 100 * 0.1) * 0.05;
this.constrainHeading();
if (this.maybeDoneLooping && (this.heading < TAU * 0.05 || this.heading > TAU * 0.95)) {
this.looping = false;
}
}
- Motion logic : Each looper’s heading (direction) is updated as it moves, with the possibility of entering or exiting a "looping" state. When looping, the looper moves in a curved path, adding complexity to the image.
draw1() {
stroke(this.c);
line(this.prevX, this.prevY, this.x, this.y);
}
-
Drawing the path
: As each looper moves, it leaves a trail by drawing a line between its previous position (
prevX
,prevY
) and its new position (x
,y
). This is where the visual patterns emerge, forming the signature swirls and whirls.
Saving the Canvas: A Moment Captured
function keyPressed() {
if (key === 's' || key === 'S') {
saveCanvas('Ephemeral Whirls', 'png');
}
}
- Saving the artwork : By pressing 'S', the artwork is saved as a PNG file. This ensures that each ephemeral creation, though fleeting in code, can be captured and cherished.
Conclusion
The "Ephemeral Whirls" project is more than just code—it’s a symphony of motion and color, where each looper dances across the screen, leaving behind trails of beauty. The randomness ensures that every execution generates a new, unique work of art, just like the natural unpredictability of life itself. This generative piece celebrates the interplay between control and chaos, inviting you to watch, play, and create in the digital world.
Click here for the full code, and let the art flow. Let’s explore these ephemeral whirls together and share our creations with the world!
*With all our creativity, love, and the code that brings this art to life,*🌸💻
_Frostbond Coders