I’m working on a Racing Eggs! animation, wherein four Egg objects race across the canvas from left to right. They each speed up and slow down independently of each other. When an Egg reaches the right edge of the canvas, it wraps around to appear again at the left edge. Each traverse of the canvas is considered a lap. The race continues indefinitely.
Each Egg object is labeled with its name (a greek letter, namely α, β, γ, and δ), and a number representing its current lap. Clicking the window toggles the animation on and off.
Question: Which Egg is currently in first place?
(answer at bottom of post)
The rate of progress of each Egg is governed by values from Perlin noise. These values are fetched from nearly parallel tracks in Perlin space.
This animation is purely for amusement, so the following concern is by no means of grave importance. However, it has me wondering whether Perlin noise is at all suitable for simulations that might be developed for research purposes.
For this animation, my worry is that the progress of the race is rendered cyclical due to the periodicity of Perlin noise, when the actual intention was to use that noise to confer randomness with a natural aspect to it. The same concern might apply to more serious research that involves performing statistical analysis to simulations of natural phenomena.
Here’s the p5.js code:
/* Racing Eggs Sketch */
// javagar
// posted July 12, 2021
let x_noise_distance;
let y_noise_distance;
let alpha;
let velocity_factor;
function setup() {
let canvas = createCanvas(640, 320);
canvas.parent('sketch_div');
// distances of points in noise space
// increment in x dimension per frame
// higher -> more abrupt velocity changes
x_noise_distance = 0.01;
// separation in y space domension for each Egg
// higher -> more independence of motion
y_noise_distance = 1.0;
velocity_factor = 4.0;
alpha = new Egg("α", 100, 55, 100, 60);
beta = new Egg("β", 100, 125, 100, 60);
gamma = new Egg("γ", 100, 195, 100, 60);
delta = new Egg("δ", 100, 265, 100, 60);
eggs = [alpha, beta, gamma, delta];
background(255, 239, 223);
for (let i = 0; i < eggs.length; i += 1) {
// Use randomGaussian() to reduce effect of periodicity of Perlin noise ...
// ... but does that solve the problem???
eggs[i].move_by(noise(frameCount * x_noise_distance, i * y_noise_distance + x_noise_distance * randomGaussian()) * velocity_factor, 0);
eggs[i].display();
}
}
function draw() {
if (anim) {
background(255, 239, 223);
for (let i = 0; i < eggs.length; i += 1) {
// Use randomGaussian() to reduce effect of periodicity of Perlin noise ...
// ... but does that solve the problem???
eggs[i].move_by(noise(frameCount * x_noise_distance, i * y_noise_distance + x_noise_distance * randomGaussian()) * velocity_factor, 0);
eggs[i].display();
}
}
}
class Egg {
constructor(id, x, y, w, h) {
this.id = id
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.lap = 1;
}
display() {
push();
textAlign(CENTER, CENTER);
textSize(min(this.w, this.h) / 3);
strokeWeight(2);
fill(255, 255, 255);
ellipse(this.x, this.y, this.w, this.h);
strokeWeight(1);
fill(255, 255, 191);
ellipse(this.x, this.y, this.w - 20, this.h - 20);
fill(0);
text(this.id + ": " + this.lap, this.x, this.y);
pop();
}
move_to(x, y) {
this.x = x;
this.y = y;
}
move_by(dx, dy) {
this.x += dx;
this.y += dy;
if (this.x >= width) {
this.x = this.x % width;
this.lap += 1;
}
}
}
// Mouse clicks toggle animation off and on.
let anim = false;
document.addEventListener('click', toggleAnimation, true);
function toggleAnimation() {
anim = !anim;
if (anim) {
loop();
} else {
noLoop();
}
}
Here’s the HTML:
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- PLEASE NO CHANGES BELOW THIS LINE (UNTIL I SAY SO) -->
<script language="javascript" type="text/javascript" src="libraries/p5.min.js"></script>
<script language="javascript" type="text/javascript" src="racing_eggs.js"></script>
<!-- OK, YOU CAN MAKE CHANGES BELOW THIS LINE AGAIN -->
<!-- This line removes any default padding and style.
You might only need one of these values set. -->
<style> body { padding: 0; margin: 10; background-color: #eeeeee; font-family: Verdana, Arial, Helvetica, sans-serif;} </style>
</head>
<body>
<h2>Racing Eggs!</h2>
<div id="sketch_div">
</div>
<p>Click the page to start or stop the animation.</p>
</body>
</html>
Note this in the draw()
function:
eggs[i].move_by(noise(frameCount * x_noise_distance, i * y_noise_distance + x_noise_distance * randomGaussian()) * velocity_factor, 0);
In that statement, a call to randomGaussian()
is used to reduce, slightly, the periodicity that may result from following absolutely parallel tracks in Perlin space to fetch data. Is this sufficient? Can anyone suggest effective means of overcoming the problem of periodicity in Perlin space, or as suggested in the discussion Periodicity of perlin noise, should we give up, and use open simplex noise or something else, instead?
Answer to question regarding which Egg is winning the race in the illustration:
It is α (alpha), which is in lap 31, while the others are still in earlier laps.
EDITED 2x on July 12, 2021 to make revisions to the p5.js code.