

์ด ์์ ๋ ๋ง์ฐ์ค ์ ๋ ฅ์ด๋ผ๋ ๊ด๊ฐ์ ์ฐธ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ค์๊ฐ์ผ๋ก ๋ฐ์ ํํ์ ์์์ ๋ณํ์ํค๋ ์ธํฐ๋ํฐ๋ธ ์์ฑ ์ํธ์ ๊ธฐ์ด๋ฅผ ๋ณด์ฌ์ค๋๋ค. ๋ํ, ์๊ฐ ๋ฐ์ดํฐ๋ฅผ ๊ฒฐํฉํ์ฌ ํํ๊ฐ ์ค์ค๋ก ์ง๋ํ๋ฉฐ ๋ฐ์ ํ๋ ์๊ฐ์ฑ ์์คํ ์ ๊ตฌํํฉ๋๋ค.
์ธํฐ๋์ : ๋ง์ฐ์ค์ ์์น์ ํด๋ฆญ ์ํ๋ฅผ ์ค์๊ฐ ์ ๋ ฅ ๋ฐ์ดํฐ๋ก ์ฌ์ฉํฉ๋๋ค.
์๊ฐ์ฑ: frameCount๋ฅผ ์ด์ฉํด ์์คํ
์ ๋ณํ ์๋๋ฅผ ์ ์ดํ๊ณ , millis() (ํ์ฌ ์๊ฐ) ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ ๊ธฐ์ ์ธ ์์ง์์ ๋ง๋ญ๋๋ค.
// ====== ์ ์ญ ๋ณ์ ์ค์ ======
let W = 800;
let H = 600;
let particles = []; // ์
์ ๊ฐ์ฒด๋ค์ ์ ์ฅํ ๋ฐฐ์ด (Array of Objects)
const numParticles = 50; // ์
์์ ๊ฐ์ (๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ๊ณ ๋ ค)
function setup() {
createCanvas(W, H);
background(0);
noStroke();
// 50๊ฐ์ ์
์ ๊ฐ์ฒด๋ฅผ ์์ฑํ์ฌ ๋ฐฐ์ด์ ์ด๊ธฐํํฉ๋๋ค.
for (let i = 0; i < numParticles; i++) {
particles.push(new Particle(random(width), random(height)));
}
}
function draw() {
// 1. ์๊ฐ์ฑ: ๋ฐฐ๊ฒฝ์ ๋ฐํฌ๋ช
ํ๊ฒ ๋ฎ์ด ์์ ํจ๊ณผ (Temporal System)
// ์ด์ ํ๋ ์์ ํ์ ์ ๋จ๊ธฐ๋ฉฐ, ์์ง์์ '์๊ฐ์ฑ'์ ์๊ฐํํฉ๋๋ค.
background(0, 30);
// 2. ์
๋ ฅ ๋ฐ์ดํฐ ๊ณ์ฐ (Interaction Data)
// ๋ง์ฐ์ค์ ์์น๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์์คํ
์ '๋์ด๋น๊น' ์ค์ฌ์ ์ ์ ์ํฉ๋๋ค.
let targetX = mouseX;
let targetY = mouseY;
// ๋ง์ฐ์ค ํด๋ฆญ ์ํ(๋ถ๋ฆฐ ๊ฐ)๋ฅผ 'ํ'์ ๋ฐ์ดํฐ๋ก ๋ณํํฉ๋๋ค.
let attractionStrength = mouseIsPressed ? 0.05 : 0.01;
// 3. ์
์ ์
๋ฐ์ดํธ ๋ฐ ๊ทธ๋ฆฌ๊ธฐ
for (let p of particles) {
// ๋ง์ฐ์ค ์์น(target)๋ฅผ ํฅํด ์
์๋ฅผ ์ด๋์ํค๋ ํ์ ์ ์ฉํฉ๋๋ค.
p.attract(targetX, targetY, attractionStrength);
// ์๊ฐ ํ๋ฆ์ ๋ฐ๋ฅธ ์ง๋ ๋ฐ ์์น ์
๋ฐ์ดํธ
p.update();
// ๋ง์ฐ์ค์ ์
์์ ๊ฑฐ๋ฆฌ์ ๋ฐ๋ผ ์์๊ณผ ํฌ๊ธฐ๋ฅผ ๊ฒฐ์ ํฉ๋๋ค.
p.display(targetX, targetY);
}
}
/**
* ์
์(Particle) ํด๋์ค ์ ์
* - ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ์ ์ฌ์ฉํ์ฌ ์
์๋ค์ ์ํ์ ํ๋์ ๊ด๋ฆฌํฉ๋๋ค.
*/
class Particle {
constructor(x, y) {
this.pos = createVector(x, y); // ํ์ฌ ์์น
this.vel = createVector(0, 0); // ์๋ (0์ผ๋ก ์ด๊ธฐํํ์ฌ ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ฑ ์ ์ง)
this.acc = createVector(0, 0); // ๊ฐ์๋
this.baseSize = 10; // ๊ธฐ๋ณธ ํฌ๊ธฐ
this.noiseOffset = random(1000); // ํผ๋ฆฐ ๋
ธ์ด์ฆ๋ฅผ ์ํ ๋ฌด์์ ์คํ์
(์๊ฐ์ฑ)
}
/**
* ๋ง์ฐ์ค ์์น(target)๋ก ๋์ด๋น๊ธฐ๋ ํ์ ๊ณ์ฐํ์ฌ ๊ฐ์๋์ ์ ์ฉํฉ๋๋ค.
*/
attract(targetX, targetY, strength) {
let target = createVector(targetX, targetY);
// 1. ๋ฐฉํฅ ๋ฒกํฐ ๊ณ์ฐ: target - pos
let force = p5.Vector.sub(target, this.pos);
// 2. ๊ฑฐ๋ฆฌ ์ ํ: ๋๋ฌด ๋ฉ๋ฉด ํ์ ์ค์ด๊ณ , ๋๋ฌด ๊ฐ๊น์ฐ๋ฉด ํ์ 0์ผ๋ก ๋ง๋ญ๋๋ค.
let dist = force.mag();
dist = constrain(dist, 5, 200); // ์ต์ 5, ์ต๋ 200์ผ๋ก ๊ฑฐ๋ฆฌ๊ฐ ์ ํ
// 3. ํ์ ํฌ๊ธฐ ๊ณ์ฐ: (strength) * (1/dist^2)์ ๊ทผ์ฌ์น ์ ์ฉ
force.normalize(); // ๋ฐฉํฅ๋ง ๋จ๊ธฐ๊ณ
force.mult(strength * (1 / (dist * 0.01))); // ํ์ ํฌ๊ธฐ ์ ์ฉ
this.acc.add(force); // ๊ฐ์๋์ ํ์ ๋ํฉ๋๋ค.
}
/**
* ์๋์ ์์น๋ฅผ ์
๋ฐ์ดํธํ๊ณ , ์๊ฐ์ฑ ์ง๋์ ์ถ๊ฐํฉ๋๋ค.
*/
update() {
// 1. ์๊ฐ์ฑ: ํผ๋ฆฐ ๋
ธ์ด์ฆ๋ฅผ ์ด์ฉํ ์ง๋ ์ถ๊ฐ
// ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ(frameCount) ์์น๊ฐ ๋ฏธ์ธํ๊ฒ ์ง๋ํฉ๋๋ค.
let noiseValX = map(noise(this.noiseOffset, frameCount * 0.005), 0, 1, -0.5, 0.5);
let noiseValY = map(noise(this.noiseOffset + 10, frameCount * 0.005), 0, 1, -0.5, 0.5);
this.acc.add(noiseValX * 0.1, noiseValY * 0.1); // ๋ฏธ์ธํ ๋๋ค ๊ฐ์๋ ์ถ๊ฐ
// 2. ๋ฌผ๋ฆฌ ์
๋ฐ์ดํธ
this.vel.add(this.acc);
this.vel.mult(0.95); // ๋ง์ฐฐ๋ ฅ (์๋๋ฅผ 0.95๋ก ์ค์ฌ ์์ ํ)
this.pos.add(this.vel);
this.acc.mult(0); // ๊ฐ์๋๋ฅผ ๋งค ํ๋ ์ ์ด๊ธฐํ (๋งค์ฐ ์ค์)
}
/**
* ์
์๋ฅผ ํ๋ฉด์ ๊ทธ๋ฆฌ๊ณ , ๋ง์ฐ์ค ๊ฑฐ๋ฆฌ์ ๋ฐ๋ฅธ ์์/ํฌ๊ธฐ ๋ณํ๋ฅผ ์ ์ฉํฉ๋๋ค.
*/
display(targetX, targetY) {
// 1. ์ธํฐ๋์
: ๋ง์ฐ์ค์์ ๊ฑฐ๋ฆฌ์ ๋ฐ๋ฅธ ์์/ํฌ๊ธฐ ๋ฐ์ดํฐ ์ ์
let distToMouse = dist(this.pos.x, this.pos.y, targetX, targetY);
// ๊ฑฐ๋ฆฌ์ ๋ฐ๋ผ ํฌ๊ธฐ ๋ณํ (๊ฐ๊น์ธ์๋ก ์ปค์ง)
let size = this.baseSize + map(distToMouse, 0, width, 10, 0);
// ๊ฑฐ๋ฆฌ์ ๋ฐ๋ผ ์์ ๋ณํ (๊ฐ๊น์ธ์๋ก ๋ฐ๊ณ ๋ถ์์)
let hue = map(distToMouse, 0, width, 0, 60); // 0(Red) ~ 60(Yellow)
let brightness = map(distToMouse, 0, width, 100, 50);
// ์์ ๋ชจ๋๋ฅผ HSB๋ก ๋ณ๊ฒฝํ์ฌ ์์ ์ ์ด๋ฅผ ์ฉ์ดํ๊ฒ ํฉ๋๋ค.
colorMode(HSB, 360, 100, 100);
fill(hue, 80, brightness, 80); // ๋ฐํฌ๋ช
๋๋ฅผ 80์ผ๋ก ์ค์
ellipse(this.pos.x, this.pos.y, size, size);
colorMode(RGB, 255); // ๋ค์ ๋๋ก์์ ์ํด ๋ค์ RGB๋ก ๋ณต๊ตฌ
}
}
Particle ํด๋์ค: ์๋ง์ ์ ์์ ์ํ(pos, vel, acc)์ ํ๋(attract, update, display)์ ํ๋์ ๊ฐ์ฒด๋ก ๋ฌถ์ด ๊ด๋ฆฌํฉ๋๋ค. ์ด๋ ์ฝ๋๋ฅผ ๋ชจ๋ํํ๊ณ ์ดํดํ๊ธฐ ์ฝ๊ฒ ๋ง๋ค๋ฉฐ, ๋๊ท๋ชจ ์์คํ ์ ์ค๊ณํ ๋ ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ์ธ ๋ฐฉ๋ฒ์ ๋๋ค.
๋ฒกํฐ ์ฌ์ฉ: ์์น, ์๋, ๊ฐ์๋ ๋ชจ๋ p5.Vector๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๋ฒกํฐ ์ฐ์ฐ(add, sub, mult, normalize)์ ๋ฌผ๋ฆฌํ์ ์์ง์์ ์ง๊ด์ ์ผ๋ก ๊ตฌํํ๋ฉฐ ์ฝ๋๋ฅผ ๊ฐ๊ฒฐํ๊ฒ ํฉ๋๋ค.
์ ๋ ฅ ๋ฐ์ดํฐ ์์ง: mouseX, mouseY, mouseIsPressed๋ผ๋ ๊ด๊ฐ์ ์ค์๊ฐ ์ ๋ ฅ ๋ฐ์ดํฐ๋ฅผ ์ง์ ์ฌ์ฉํฉ๋๋ค.
ํ์ ์ ์ด: attract() ํจ์๊ฐ ์ธํฐ๋์ ์ ํต์ฌ์ ๋๋ค.
์ค์ฌ์ : ๋ง์ฐ์ค ์์น(targetX, targetY)๊ฐ ์ ์๋ค์ ๋์ด๋น๊ธฐ๋ ์ค์ฌ์ ์ญํ ์ ํฉ๋๋ค.
๋ฐ์ดํฐ ๊ธฐ๋ฐ ํ ์กฐ์ : attractionStrength๋ ๋ง์ฐ์ค ํด๋ฆญ ์ํ(mouseIsPressed)๋ผ๋ ๋ถ๋ฆฐ ๋ฐ์ดํฐ๋ฅผ ์์นํํ์ฌ ํ์ ํฌ๊ธฐ๋ฅผ ๋ ๋ฐฐ ์ด์์ผ๋ก ๋ณํ์ํต๋๋ค. (ํด๋ฆญ ์ ๋ ๊ฐํ๊ฒ ๋์ด๋น๊น)
์๊ฐ์ ๋ฐ์: display() ํจ์ ๋ด์์ distToMouse (๋ง์ฐ์ค์ ์ ์์ ๊ฑฐ๋ฆฌ)๋ฅผ ๊ณ์ฐํ์ฌ, ์ด ๊ฑฐ๋ฆฌ์ ๋ฐ๋ผ ์ ์์ ํฌ๊ธฐ(size)์ ์์(hue, brightness)์ ์ค์๊ฐ์ผ๋ก ๋ณํ์ํต๋๋ค. ์ด๋ ๊ด๊ฐ์ ์์น๊ฐ ์ง์ ์ํธ์ํฌ์ ํํ๋ฅผ ๋ณํ์ํค๋ ํจ๊ณผ๋ฅผ ์ค๋๋ค.
ํ๋ ์ ๊ธฐ๋ฐ ์๊ฐ: frameCount๋ ์์คํ ์ด ์์๋ ์ดํ์ ํ๋ ์ ์๋ฅผ ๋ํ๋ด๋ฉฐ, ์ด๋ ์๊ฐ์ ํ๋ฆ์ ๋ํํ๋ ๋ฐ์ดํฐ๋ก ์ฌ์ฉ๋ฉ๋๋ค.
์ ๊ธฐ์ ์ง๋: update() ํจ์์์ frameCount๋ฅผ ํผ๋ฆฐ ๋ ธ์ด์ฆ์ ์ธ์๋ก ์ฌ์ฉํ์ฌ, ์ ์๊ฐ ๋ง์น ์ด์์๋ ๊ฒ์ฒ๋ผ ๋ฏธ์ธํ๊ณ ๋ถ๋๋ฌ์ด ๋ถ๊ท์นํ ์ง๋์ ํ๋๋ก ๊ฐ์๋๋ฅผ ์ถ๊ฐํฉ๋๋ค.
์์ ํจ๊ณผ: background(0, 30) ์ฝ๋๋ ๋งค ํ๋ ์๋ง๋ค ๋ฐฐ๊ฒฝ์ ์์ ํ ์ง์ฐ์ง ์๊ณ ์ ์ ๊ฒ์์์ผ๋ก ๋ง์น ํ์ฌ, ์ด์ ํ๋ ์์ ๋๋ก์์ด ๋ฏธ์ธํ๊ฒ ๋จ์ ์ ์๋ค์ ์์ง์์ ์๊ฐ์ ํ์ (Temporal Trace)์ ๋จ๊น๋๋ค.

frameCount๋ฅผ ์๊ฐ์ ๋ฐ์ดํฐ๋ก ์ฌ์ฉํ์ฌ ํํ๊ฐ ๊ณ์ ์ง๋ํ๊ณ ๋ณํํ๋ ๊ตฌ์กฐ.
let numPoints = 300;
function setup() {
createCanvas(800, 400);
noFill();
stroke(240, 200, 30);
}
function draw() {
background(15);
beginShape();
for (let i = 0; i < numPoints; i++) {
let x = map(i, 0, numPoints, 0, width);
// ์๊ฐ ๊ธฐ๋ฐ ์ง๋
let t = frameCount * 0.02;
let y = height / 2 + sin(i * 0.05 + t) * 120;
vertex(x, y);
}
endShape();
}
๐ ์ค๋ช
frameCount๋ฅผ ์๊ฐ(time) ๋ฐ์ดํฐ์ฒ๋ผ ์ฌ์ฉํด ๊ณ์ ๋ณํํ๋ ํํ์ ์์ฑ.
i * 0.05 + t๊ฐ ๊ฐ์ง๋ ์์ ๋ณํ(phase shift) ๋๋ถ์ ํ๋์ด ํ๋ฌ๊ฐ๋ ๋๋์ ์ค๋ค.
"์๊ฐ์ฑ"์ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ผ๋ก ์๊ฐํํ๋ ์์ .

๋ง์ฐ์ค ์์น๊ฐ โ์ ๋ ฅ ๋ฐ์ดํฐโ๊ฐ ๋์ด ํํฐํด๋ค์ด ์ค์๊ฐ์ผ๋ก ๋ฐ์.
let particles = [];
function setup() {
createCanvas(800, 500);
for (let i = 0; i < 200; i++) {
particles.push({
pos: createVector(random(width), random(height)),
vel: createVector(random(-1, 1), random(-1, 1))
});
}
}
function draw() {
background(15);
for (let p of particles) {
// ๋ง์ฐ์ค ๋ฐฉํฅ์ผ๋ก ๋์ด๋น๊ธฐ๋ ํ
let force = createVector(mouseX - p.pos.x, mouseY - p.pos.y);
force.setMag(0.05);
p.vel.add(force);
p.vel.limit(3);
p.pos.add(p.vel);
fill(240, 200, 30);
noStroke();
circle(p.pos.x, p.pos.y, 6);
}
}

์ฌ์ฉ์๊ฐ ๊ทธ๋ฆฐ ์ ์ โ์ ๋ ฅ ๋ฐ์ดํฐโ๋ก ์ผ์ ์๋์ ์ผ๋ก ๋ณํ๋ ํํ๊ฐ ์์ฑ๋๋ ๋ฐฉ์.
let path = [];
function setup() {
createCanvas(800, 500);
stroke(255);
noFill();
}
function draw() {
background(25);
// ์ฌ์ฉ์๊ฐ ๊ทธ๋ฆฐ ๊ฒฝ๋ก ์๊ฐํ
beginShape();
for (let p of path) {
vertex(p.x, p.y);
}
endShape();
// ๋๋ก์ ๊ธฐ๋ฐ "๋ฐ์ํ ์"
if (path.length > 2) {
let last = path[path.length - 1];
let prev = path[path.length - 2];
let dx = last.x - prev.x;
let dy = last.y - prev.y;
let speed = sqrt(dx * dx + dy * dy);
let size = map(speed, 0, 50, 10, 80);
fill(180, 120, 255);
noStroke();
circle(last.x, last.y, size);
}
}
function mouseDragged() {
path.push(createVector(mouseX, mouseY));
}
๐ ์ค๋ช
mouseDragged()๊ฐ ์ฌ์ฉ์ ์ ์ค์ฒ ๋ฐ์ดํฐ๋ฅผ ์ถ์ ํด path[] ๋ฐฐ์ด์ ๋ง๋ ๋ค.
์ฌ์ฉ์ ์์ง์ + ์๊ฐ ๋ณํ๊ฐ ๊ฒฐํฉ๋ ์์ฑ ์ํธ ๊ตฌ์กฐ.
let nodes = [];
function setup() {
createCanvas(800, 400);
for (let i = 0; i < 400; i++) {
nodes.push({
pos: createVector(random(width), random(height)),
vel: createVector(random(-1, 1), random(-1, 1)),
});
}
}
function draw() {
background(15);
stroke(240, 200, 30, 80);
// ๋ชจ๋ ๋
ธ๋ ์
๋ฐ์ดํธ
for (let n of nodes) {
// ์๊ฐ ๊ธฐ๋ฐ ํ๋ค๋ฆผ
n.pos.x += n.vel.x + sin(frameCount * 0.01 + n.pos.y) * 0.4;
n.pos.y += n.vel.y + cos(frameCount * 0.01 + n.pos.x) * 0.4;
// ๋ง์ฐ์ค ๋ฐ๋ฐ๋ ฅ
let d = dist(mouseX, mouseY, n.pos.x, n.pos.y);
if (d < 150) {
let repel = p5.Vector.sub(n.pos, createVector(mouseX, mouseY));
repel.setMag(0.5);
n.pos.add(repel);
}
// ๋
ธ๋ ๊ทธ๋ฆฌ๊ธฐ
fill(240, 200, 30);
noStroke();
circle(n.pos.x, n.pos.y, 6);
}
// ๋
ธ๋ ๊ฐ ์ฐ๊ฒฐ
stroke(120, 90);
for (let i = 0; i < nodes.length; i++) {
for (let j = i + 1; j < nodes.length; j++) {
let d = dist(
nodes[i].pos.x,
nodes[i].pos.y,
nodes[j].pos.x,
nodes[j].pos.y
);
if (d < 100) {
line(nodes[i].pos.x, nodes[i].pos.y, nodes[j].pos.x, nodes[j].pos.y);
}
}
}
}
๐ ์ค๋ช