Interactive NFT by @andriibakulin
<code>
let gNullColor;
let gPoints = [];
let gLines = [];
let gDistanceRange, gDistanceRangeSqr;
class EntityPoint
{
constructor(index)
{
this.index = index;
this.x = Math.random() * window.innerWidth;
this.y = Math.random() * window.innerHeight;
this.x_offset = 0;
this.y_offset = 0;
this.x_last_draw = this.x;
this.y_last_draw = this.y;
const c = Math.random();
this.color_base = color(c * 255, 255, 255);
this.color_line = color(c * 255, 255, 255);
this.color_back = color(c * 255, 255, 255, 111);
this.offset = Math.random() * 123;
this.speed = 0.5 + Math.random() * 4.5 * (Math.random() < 0.5 ? -1 : +1);
this.radius = 1.0 + Math.random() * 29;
this.activeForce = 0;
}
getX()
{
return this.x + this.x_offset;
}
getY()
{
return this.y + this.y_offset;
}
next(dt)
{
this.offset += dt * this.speed * (1 + this.activeForce);
this.x_offset = Math.sin(this.offset) * this.radius;
this.y_offset = Math.cos(this.offset) * this.radius;
}
distanceSqr(x, y)
{
return (x - this.getX())**2 + (y - this.getY())**2;
}
}
class EntityLine
{
constructor(p1, p2)
{
this.p1 = p1;
this.p2 = p2;
this.w = Math.random() < 0.5 ? 1 : 2;
this.v = 0;
this.t = Math.random() * 3 + 1;
}
next(dt)
{
this.v += dt;
return this.v < this.t;
}
render()
{
let t0 = this.v/this.t;
let t1 = t0 * 2;
if (t1 > 1)
t1 = 2 - t1;
let col = lerpColor(this.p1.color_line, this.p2.color_line, t0);
strokeWeight(this.w);
stroke(lerpColor(gNullColor, col, t1));
line(
this.p1.x_last_draw, this.p1.y_last_draw,
this.p2.x_last_draw, this.p2.y_last_draw);
}
}
function setup()
{
gNullColor = color(0,0,0,0);
createCanvas(window.innerWidth, window.innerHeight)
pixelDensity(1);
frameRate(60);
angleMode(DEGREES);
setupWorkingAreaAndData();
}
function windowResized()
{
resizeCanvas(window.innerWidth, window.innerHeight);
setupWorkingAreaAndData();
}
function setupWorkingAreaAndData()
{
gDistanceRange = Math.min(window.innerWidth, window.innerHeight)*0.33;
gDistanceRangeSqr = gDistanceRange ** 2;
background(0);
gPoints = [];
gLines = [];
colorMode(HSB, 255);
for (let index=0; index<30; index++)
{
gPoints.push(new EntityPoint(index));
}
colorMode(RGB, 255);
}
function draw()
{
const dt = deltaTime/1000;
background(0);
if (gLines.length < 16)
{
const p1 = gPoints[floor(Math.random()*gPoints.length)];
const p2 = gPoints[floor(Math.random()*gPoints.length)];
gLines.push(new EntityLine(p1, p2));
}
for (const idx in gLines)
{
const line = gLines[idx];
if (line.next(dt))
line.render();
else
gLines.splice(idx, 1);
}
for (const idx in gPoints)
{
const point = gPoints[idx];
point.next(dt);
let point_x = point.getX();
let point_y = point.getY();
let point_s = 5;
if (mouseIsPressed)
{
const entDistanceSqr = point.distanceSqr(mouseX, mouseY);
if (entDistanceSqr < gDistanceRangeSqr)
{
const entDistance = Math.sqrt(entDistanceSqr);
const dst = entDistance / gDistanceRange;
const frc = 1 - dst;
point.activeForce = frc;
point_x = lerp(point_x, mouseX, frc);
point_y = lerp(point_y, mouseY, frc);
const dx = point_x - mouseX;
const dy = point_y - mouseY;
noStroke();
fill(lerpColor(point.color_back, color(0,0,0,0), dst));
circle(
point_x - dx * dst * 3,
point_y - dy * dst * 3,
gDistanceRange * (point.radius/10) * frc);
point_s *= (frc + 1)**2;
}
}
else
{
point.activeForce = 0;
}
noStroke();
fill(point.color_base);
circle(point_x, point_y, point_s);
point.x_last_draw = point_x;
point.y_last_draw = point_y;
}
}
</code>