<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Interactive Circle Collision NFT</title>
<script src="
cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<style>
body { margin: 0; padding: 0; }
canvas { display: block; }
</style>
</head>
<body>
<script>
let balls = [];
const numBalls = 7;
let bgColor1, bgColor2, targetBgColor1, targetBgColor2;
const bgColorChangeSpeed = 0.005;
let draggedBall = null;
class Ball {
constructor(x, y, r) {
this.position = createVector(x, y);
this.velocity = p5.Vector.random2D().mult(3);
this.r = r;
this.m = r * 0.1;
this.color = color(random(255), random(255), random(255));
this.targetColor = color(random(255), random(255), random(255));
this.colorChangeSpeed = 0.01;
this.isDragging = false;
}
update() {
if (!this.isDragging) {
this.position.add(this.velocity);
}
this.updateColor();
}
updateColor() {
this.color = lerpColor(this.color, this.targetColor, this.colorChangeSpeed);
if (this.color.toString() === this.targetColor.toString()) {
this.targetColor = color(random(255), random(255), random(255));
}
}
checkBoundaryCollision() {
if (this.position.x > width - this.r || this.position.x < this.r) {
this.velocity.x *= -1;
this.position.x = constrain(this.position.x, this.r, width - this.r);
}
if (this.position.y > height - this.r || this.position.y < this.r) {
this.velocity.y *= -1;
this.position.y = constrain(this.position.y, this.r, height - this.r);
}
}
checkCollision(other) {
let distanceVect = p5.Vector.sub(other.position, this.position);
let distanceVectMag = distanceVect.mag();
let minDistance = this.r + other.r;
if (distanceVectMag < minDistance) {
let distanceCorrection = (minDistance - distanceVectMag) / 2.0;
let correctionVector = distanceVect.copy().normalize().mult(distanceCorrection);
other.position.add(correctionVector);
this.position.sub(correctionVector);
let theta = distanceVect.heading();
let sine = sin(theta);
let cosine = cos(theta);
let bTemp = [createVector(), createVector()];
bTemp[1].x = cosine * distanceVect.x + sine * distanceVect.y;
bTemp[1].y = cosine * distanceVect.y - sine * distanceVect.x;
let vTemp = [createVector(), createVector()];
vTemp[0].x = cosine * this.velocity.x + sine * this.velocity.y;
vTemp[0].y = cosine * this.velocity.y - sine * this.velocity.x;
vTemp[1].x = cosine * other.velocity.x + sine * other.velocity.y;
vTemp[1].y = cosine * other.velocity.y - sine * other.velocity.x;
let vFinal = [createVector(), createVector()];
vFinal[0].x = ((this.m - other.m) * vTemp[0].x + 2 * other.m * vTemp[1].x) / (this.m + other.m);
vFinal[0].y = vTemp[0].y;
vFinal[1].x = ((other.m - this.m) * vTemp[1].x + 2 * this.m * vTemp[0].x) / (this.m + other.m);
vFinal[1].y = vTemp[1].y;
bTemp[0].x += vFinal[0].x;
bTemp[1].x += vFinal[1].x;
let bFinal = [createVector(), createVector()];
bFinal[0].x = cosine * bTemp[0].x - sine * bTemp[0].y;
bFinal[0].y = cosine * bTemp[0].y + sine * bTemp[0].x;
bFinal[1].x = cosine * bTemp[1].x - sine * bTemp[1].y;
bFinal[1].y = cosine * bTemp[1].y + sine * bTemp[1].x;
other.position.x = this.position.x + bFinal[1].x;
other.position.y = this.position.y + bFinal[1].y;
this.position.add(bFinal[0]);
this.velocity.x = cosine * vFinal[0].x - sine * vFinal[0].y;
this.velocity.y = cosine * vFinal[0].y + sine * vFinal[0].x;
other.velocity.x = cosine * vFinal[1].x - sine * vFinal[1].y;
other.velocity.y = cosine * vFinal[1].y + sine * vFinal[1].x;
}
}
display() {
noStroke();
fill(this.color);
ellipse(this.position.x, this.position.y, this.r * 2);
}
contains(px, py) {
let d = dist(px, py, this.position.x, this.position.y);
return d < this.r;
}
drag(px, py) {
this.position.x = px;
this.position.y = py;
}
}
function setup() {
createCanvas(710, 400);
for (let i = 0; i < numBalls; i++) {
let x = random(width);
let y = random(height);
let r = random(20, 40);
balls.push(new Ball(x, y, r));
}
bgColor1 = color(random(255), random(255), random(255));
bgColor2 = color(random(255), random(255), random(255));
targetBgColor1 = color(random(255), random(255), random(255));
targetBgColor2 = color(random(255), random(255), random(255));
}
function draw() {
bgColor1 = lerpColor(bgColor1, targetBgColor1, bgColorChangeSpeed);
bgColor2 = lerpColor(bgColor2, targetBgColor2, bgColorChangeSpeed);
if (bgColor1.toString() === targetBgColor1.toString()) {
targetBgColor1 = color(random(255), random(255), random(255));
}
if (bgColor2.toString() === targetBgColor2.toString()) {
targetBgColor2 = color(random(255), random(255), random(255));
}
for(let y = 0; y < height; y++){
let inter = map(y, 0, height, 0, 1);
let c = lerpColor(bgColor1, bgColor2, inter);
stroke(c);
line(0, y, width, y);
}
for (let i = 0; i < balls.length; i++) {
let b = balls[i];
b.update();
b.display();
b.checkBoundaryCollision();
for (let j = i + 1; j < balls.length; j++) {
b.checkCollision(balls[j]);
}
}
}
function mousePressed() {
for (let i = balls.length - 1; i >= 0; i--) {
if (balls[i].contains(mouseX, mouseY)) {
draggedBall = balls[i];
draggedBall.isDragging = true;
break;
}
}
}
function mouseDragged() {
if (draggedBall) {
draggedBall.drag(mouseX, mouseY);
}
}
function mouseReleased() {
if (draggedBall) {
draggedBall.isDragging = false;
draggedBall = null;
}
}
</script>
</body>
</html>
perfect loop blue
COLOR SHAPE BLOB (SPACEBAR)
pure heatmap flow three
pure heatmap flow two
pure heatmap flow
Four Loko
CLEAN CIRCUITS
same interactivity (maybe only on desktop)
CIRCUIT WAVES
Mouse to control 🐁
spacebar to randomize ⌨️
Bitcoin
DROP IN
metalwerk3
metalwerk1
brutal merc
also bigger
brutal guy
BALL UP TOP (INTERACTIVE!!!)
BALL UP TOP
WATCHER
knot derivative
star array
RE-ZORB - interior study
WOODGRAIN
MATERIA
crystallized mako. “Meteor Fall” 333333 $enjoy
Splotch
BRYCE-One
top
SEAGLASS ONE