Media Art ProgrammingWeek 05 · Period 03 Handout

p5.js / Media Art Programming

5주차 3교시 — 학생 주도 실습: 인터랙티브 애니메이션

Motion and Animation 주제의 학생용 강의 자료입니다.

WEEK 05 PERIOD 03 P5.JS MEDIA ART

5주차 3교시 — 학생 주도 실습: 인터랙티브 애니메이션

강의 아웃라인: week-05-outline 이전 교시: week-05-period2-student (random, 유틸리티 함수, 좌표 변환) 다음 주차: week-06-outline (함수와 배열)

항목 내용
대상 P5.js 입문자 (테크아트 전공)
소요 시간 45분
사전 준비 1교시 변수 움직임·경계 처리, 2교시 random·map·좌표 변환
결과물 인터랙티브 애니메이션 스케치 1점 (실습과제 2 기초)
실습 목표
  • 1·2교시의 변수 갱신·random()·map()·좌표 변환·인터랙션을 결합해 인터랙티브 애니메이션을 제작합니다
  • 실습과제 2의 기초를 완성하고, 과제로 발전시킵니다
  • 4주차 인터랙션과 5주차 움직임을 통합해 자기만의 스타일이 드러나는 작품을 만듭니다

완성 기준 안내 + 실습 시작

3교시에서는 직접 인터랙티브 애니메이션을 만듭니다. 세 단계 완성 기준을 보고, 자신이 어느 단계에 있는지 스스로 판단할 수 있습니다.

완성 기준

단계 이름 완성 기준 핵심
기본 움직이는 스케치 도형이 자동으로 움직임 + 경계 처리 + 마우스 또는 키보드 인터랙션 1개 “움직이고, 부딪히고, 반응한다”
표준 화려한 스케치 기본 + random() 또는 map() 활용 + 시각 효과 1개 이상 (색상 변화, 잔상, 크기 변화 중 택1) “보기에도 좋다”
심화 완성된 작품 표준 + 복합 효과 조합 (HSB+잔상+map 등) 또는 좌표 변환(translate/rotate) 활용 “자기만의 스타일이 있다”

기본만 달성해도 오늘 목표는 완료입니다. 까지 가면 충분히 훌륭합니다. 자기 속도에 맞게 진행하세요. 이 스케치가 곧 실습과제 2입니다 — 수업 중에 시작하고, 다음 주까지 발전시켜 제출하면 됩니다. 제출하면 만점입니다.

세 가지 방향

세 방향 중 하나를 골라 시작합니다 (자유롭게 섞어도 됩니다).

방향 설명 핵심 기술
A. 바운싱 볼 아트 바운싱 볼 + 색상/크기 변화 + 잔상 변수 갱신, if문 바운싱, random/map
B. 랜덤 제너러티브 아트 random() 워커 + 누적 그리기 random(), constrain(), 잔상
C. 회전 만다라 translate() + rotate() + for문 push/pop, rotate, frameCount
07 assignment direction preview

무엇을 골라야 할지 모르겠으면 A(바운싱 볼)부터 시작하세요. 1교시에서 이미 만들어봤으니 가장 친숙합니다.

환경 설정 + 스타터 코드

  1. P5.js Web Editor 접속 (로그인 확인)
  2. File → New → 스케치 이름을 week05-animation으로 변경
  3. 아래 세 방향 중 하나를 골라 스타터 코드를 복사해 붙여넣기

방향 A: 바운싱 볼 아트

// === 상태 변수 (draw() 밖에 선언) ===
let x, y;
let speedX, speedY;

// === setup(): 한 번 실행 ===
function setup() {
  createCanvas(400, 400);
  x = width / 2;
  y = height / 2;
  speedX = 3;
  speedY = 2;
}

// === draw(): 매 프레임 실행 (1초에 약 60번) ===
function draw() {
  background(220);

  // 도형 그리기
  fill(100, 150, 255);
  noStroke();
  ellipse(x, y, 50, 50);

  // 위치 갱신
  x += speedX;
  y += speedY;

  // 바운싱
  if (x > width || x < 0) {
    speedX *= -1;
  }
  if (y > height || y < 0) {
    speedY *= -1;
  }
}

// === mousePressed(): 클릭 시 1회 ===
function mousePressed() {
  // 여기에 클릭 이벤트 코드
}

// === keyPressed(): 키 누를 때 1회 ===
function keyPressed() {
  // 여기에 키보드 이벤트 코드
}

방향 B: 랜덤 제너러티브 아트

let x, y;

function setup() {
  createCanvas(400, 400);
  background(255);
  x = width / 2;
  y = height / 2;
}

function draw() {
  // 랜덤 이동
  x += random(-3, 3);
  y += random(-3, 3);

  // 화면 안에 유지
  x = constrain(x, 0, width);
  y = constrain(y, 0, height);

  // 도형 그리기
  noStroke();
  fill(0, 100);
  ellipse(x, y, 10, 10);
}

function mousePressed() {
  // 여기에 클릭 이벤트 코드
}

function keyPressed() {
  // 여기에 키보드 이벤트 코드
}

방향 C: 회전 만다라

function setup() {
  createCanvas(400, 400);
  angleMode(DEGREES);
}

function draw() {
  background(220);

  translate(width / 2, height / 2);
  rotate(frameCount);

  // 회전하는 사각형
  fill(100, 150, 255);
  noStroke();
  rect(-25, -25, 50, 50);
}

function mousePressed() {
  // 여기에 클릭 이벤트 코드
}

function keyPressed() {
  // 여기에 키보드 이벤트 코드
}

실행 버튼()을 눌러보세요. A는 공이 튕기고, B는 점이 돌아다니고, C는 사각형이 회전합니다. 여기서부터 시작합니다.


단계 1 — 기본: “움직이고, 부딪히고, 반응한다”

이 단계가 끝나면: 도형이 자동으로 움직이고, 화면 밖으로 나가지 않으며, 마우스 또는 키보드에 반응한다.

스타터 코드가 실행되면 인터랙션을 추가해보세요. 1교시에서 배운 움직임 + 인터랙션 결합입니다.

방향 A: 바운싱 볼 — 클릭으로 속도 변경 + 키보드 초기화

스타터 코드의 mousePressed()keyPressed()를 채웁니다.

function mousePressed() {
  // 클릭하면 속도 랜덤 변경
  speedX = random(1, 8);
  speedY = random(1, 8);
  // 50% 확률로 음수 방향
  if (random(1) > 0.5) speedX *= -1;
  if (random(1) > 0.5) speedY *= -1;
}

function keyPressed() {
  if (key === 'r') {
    // r키: 중앙으로 초기화
    x = width / 2;
    y = height / 2;
    speedX = 3;
    speedY = 2;
  }
}

이제 클릭하면 속도가 랜덤으로 바뀌고, r을 누르면 처음 상태로 돌아옵니다.

방향 B: 랜덤 워커 — 클릭으로 위치 이동 + 캔버스 초기화

function mousePressed() {
  // 클릭한 위치로 워커 이동
  x = mouseX;
  y = mouseY;
}

function keyPressed() {
  if (key === 'c') {
    background(255);  // 캔버스 초기화
  }
  if (key === 'r') {
    x = width / 2;  // 중앙으로 초기화
    y = height / 2;
  }
}

클릭하면 워커가 그 위치로 순간이동합니다. c로 캔버스를 지우고 새로 시작할 수 있습니다.

방향 C: 회전 만다라 — 클릭으로 회전 속도 변경

draw() 밖에 변수를 추가하고, 스타터 코드를 수정합니다.

let rotSpeed = 1;  //  draw() 밖에 추가

function draw() {
  background(220);
  translate(width / 2, height / 2);
  rotate(frameCount * rotSpeed);  //  frameCount → frameCount * rotSpeed
  fill(100, 150, 255);
  noStroke();
  rect(-25, -25, 50, 50);
}

function mousePressed() {
  rotSpeed *= -1;  // 클릭하면 회전 방향 반전!
}

function keyPressed() {
  if (keyCode === UP_ARROW) {
    rotSpeed += 0.5;    // 빠르게
  } else if (keyCode === DOWN_ARROW) {
    rotSpeed -= 0.5;    // 느리게
  }
}

클릭하면 회전 방향이 반대로 바뀝니다. 방향키로 속도도 조절할 수 있습니다.

달성 체크

여기까지 하면 기본 달성 — “움직이고, 부딪히고, 반응한다”


단계 2 — 표준: “보기에도 좋다”

이 단계가 끝나면: random() 또는 map()을 활용하고, 시각 효과(색상, 잔상, 크기 변화)를 1개 이상 추가하여 보기 좋은 스케치를 만든다.

이 됐으면, 시각적으로 화려하게 만들어보세요. 2교시에서 배운 함수들을 활용할 차례입니다.

아이디어 1: HSB 무지개 색상 순환

// setup()에 추가
colorMode(HSB, 360, 100, 100);

// draw()에서 fill을 이렇게 변경
fill(frameCount % 360, 80, 100);

시간에 따라 색상이 빨강→주황→노랑→…→보라로 순환합니다. 한 줄이면 무지개 효과!

아이디어 2: 잔상 효과

// draw() 맨 위의 background()를 교체
// HSB 모드일 때:
background(0, 0, 10, 30);     // 반투명 어두운 배경 → 잔상 효과
// RGB 모드일 때:
// background(220, 220, 220, 30);

네 번째 값(30)이 투명도입니다. 낮을수록 잔상이 오래 남습니다.

아이디어 3: map()으로 위치에 따른 크기 변화

// 방향 A: 바운싱 볼의 크기가 위치에 따라 변화
let size = map(y, 0, height, 20, 80);
ellipse(x, y, size, size);

위에 있으면 작고 아래에 있으면 큽니다. 원근감 느낌이 납니다.

아이디어 4: 벽에 부딪힐 때 색상 변경

// 방향 A: 바운싱할 때 색 변경
if (x > width || x < 0) {
  speedX *= -1;
  ballColor = color(random(255), random(255), random(255));  //  부딪히면 색 변경!
}
변수 추가 필요

ballColor를 쓰려면 draw() 밖에 let ballColor;를 추가하고, setup()에서 ballColor = color(100, 150, 255);로 초기화하세요. draw()에서는 fill(ballColor);로 씁니다.

아이디어 5: 랜덤 워커 위치 → 색상 매핑

// 방향 B: x좌표에 따라 색상이 변화하는 궤적
// setup()에 colorMode(HSB, 360, 100, 100, 255); 추가
let hue = map(x, 0, width, 0, 360);
fill(hue, 80, 100, 150);

map()으로 x좌표를 색상에 연결했습니다. 왼쪽은 빨강, 오른쪽은 보라. 궤적이 쌓이면서 색상이 섞인 아트워크가 됩니다.

아이디어 6: 회전 만다라 for문 확장

// 방향 C: for문으로 도형을 원형 배치
// setup()에 colorMode(HSB, 360, 100, 100); 추가

function draw() {
  background(0, 0, 10);
  translate(width / 2, height / 2);

  for (let i = 0; i < 8; i++) {
    push();
      rotate(frameCount * rotSpeed + i * 45);  // 45도 간격
      fill(i * 45, 80, 100);
      rect(60, -10, 40, 20);
    pop();
  }
}

for문으로 8개 도형을 45도 간격으로 배치하면 만다라 느낌이 납니다. 3주차 for문 + 2교시 좌표 변환의 조합!

달성 체크

여기까지 하면 표준 달성 — “보기에도 좋다”


단계 3 — 심화: “자기만의 스타일이 있다”

이 단계가 끝나면: 여러 효과를 복합적으로 조합하거나, 좌표 변환을 활용하여 자기만의 개성이 드러나는 작품을 완성한다.

기본 기능이 완성되고 시각적으로도 안정적이면, 조합하고 확장해보세요. 여러 아이디어를 섞으면 자기만의 작품이 됩니다.

조합 예시 1: 바운싱 + 잔상 + 무지개 + 크기 변화

let x, y;
let speedX, speedY;
let ballSize = 50;

function setup() {
  createCanvas(400, 400);
  colorMode(HSB, 360, 100, 100, 255);
  x = width / 2;
  y = height / 2;
  speedX = 3;
  speedY = 2;
}

function draw() {
  background(0, 0, 10, 30);  // 잔상 효과

  // 무지개 색상 + 위치 기반 크기
  let hue = frameCount % 360;
  let size = map(y, 0, height, 20, 80);

  fill(hue, 80, 100);
  noStroke();
  ellipse(x, y, size, size);

  x += speedX;
  y += speedY;

  if (x > width || x < 0) speedX *= -1;
  if (y > height || y < 0) speedY *= -1;
}

function mousePressed() {
  speedX = random(1, 8);
  speedY = random(1, 8);
  if (random(1) > 0.5) speedX *= -1;
  if (random(1) > 0.5) speedY *= -1;
}

function keyPressed() {
  if (key === 'c') {
    background(0, 0, 10);
  }
  if (keyCode === UP_ARROW) {
    ballSize += 5;
  } else if (keyCode === DOWN_ARROW) {
    ballSize -= 5;
    if (ballSize < 10) ballSize = 10;
  }
}

잔상 + 무지개 + map 크기 변화 + 인터랙션. 오늘 배운 것의 종합입니다.

조합 예시 2: 랜덤 워커 + 위치 기반 색상 + 크기 + 잔상

let x, y;

function setup() {
  createCanvas(400, 400);
  colorMode(HSB, 360, 100, 100, 255);
  background(0, 0, 100);
  x = width / 2;
  y = height / 2;
}

function draw() {
  x += random(-4, 4);
  y += random(-4, 4);
  x = constrain(x, 0, width);
  y = constrain(y, 0, height);

  // x → 색상, y → 크기
  let hue = map(x, 0, width, 0, 360);
  let size = map(y, 0, height, 3, 15);

  noStroke();
  fill(hue, 80, 100, 150);
  ellipse(x, y, size, size);
}

function mousePressed() {
  x = mouseX;
  y = mouseY;
}

function keyPressed() {
  if (key === 'c') {
    background(0, 0, 100);
  }
  if (key === 'r') {
    x = width / 2;
    y = height / 2;
  }
}

map() 두 개로 x좌표 → 색상, y좌표 → 크기. 위치가 곧 시각 정보가 되니 궤적이 아트워크가 됩니다.

조합 예시 3: 회전 만다라 + for문 + 마우스 제어

let numShapes = 8;
let rotSpeed = 1;

function setup() {
  createCanvas(400, 400);
  angleMode(DEGREES);
  colorMode(HSB, 360, 100, 100);
}

function draw() {
  background(0, 0, 10);
  translate(width / 2, height / 2);

  let spacing = 360 / numShapes;

  for (let i = 0; i < numShapes; i++) {
    push();
      rotate(frameCount * rotSpeed + i * spacing);
      fill(i * spacing, 80, 100);
      noStroke();
      rect(60, -10, 40, 20);
    pop();
  }

  // 마우스 거리에 따라 중심 원 크기 변화
  let d = dist(mouseX, mouseY, width / 2, height / 2);
  let centerSize = map(d, 0, 300, 80, 10);
  centerSize = constrain(centerSize, 10, 80);
  fill(0, 0, 100);
  ellipse(0, 0, centerSize, centerSize);
}

function mousePressed() {
  numShapes += 2;
  if (numShapes > 24) numShapes = 4;
}

function keyPressed() {
  if (keyCode === UP_ARROW) {
    rotSpeed += 0.5;
  } else if (keyCode === DOWN_ARROW) {
    rotSpeed -= 0.5;
    if (rotSpeed < 0.1) rotSpeed = 0.1;
  }
}

클릭으로 도형 수 변경, 방향키로 회전 속도 조절, 마우스 위치로 중심 원 크기 변경. map() + constrain() + dist() + push()/pop()이 모두 들어 있습니다.

달성 체크

여기까지 하면 심화 달성 — “자기만의 스타일이 있다”


단계 4 — 확장 (선택, 남는 시간)

까지 했다면, 더 도전해볼 수 있는 것들입니다.

확장 A: 바운싱 볼 여러 개

배열은 아직 안 배웠지만, 변수를 복붙하면 여러 개 만들 수 있습니다.

let x1 = 100, y1 = 100, sx1 = 3, sy1 = 2;
let x2 = 300, y2 = 200, sx2 = -2, sy2 = 4;
let x3 = 150, y3 = 300, sx3 = 4, sy3 = -3;

function setup() {
  createCanvas(400, 400);
  colorMode(HSB, 360, 100, 100, 255);
}

function draw() {
  background(0, 0, 10, 30);

  // 볼 1: 빨강
  fill(0, 80, 100);
  ellipse(x1, y1, 30, 30);
  x1 += sx1; y1 += sy1;
  if (x1 > width || x1 < 0) sx1 *= -1;
  if (y1 > height || y1 < 0) sy1 *= -1;

  // 볼 2: 초록
  fill(120, 80, 100);
  ellipse(x2, y2, 30, 30);
  x2 += sx2; y2 += sy2;
  if (x2 > width || x2 < 0) sx2 *= -1;
  if (y2 > height || y2 < 0) sy2 *= -1;

  // 볼 3: 파랑
  fill(240, 80, 100);
  ellipse(x3, y3, 30, 30);
  x3 += sx3; y3 += sy3;
  if (x3 > width || x3 < 0) sx3 *= -1;
  if (y3 > height || y3 < 0) sy3 *= -1;
}

코드가 반복됩니다. 6주차에서 배열을 배우면 같은 구조를 훨씬 짧고 정리된 코드로 만들 수 있습니다.

확장 B: 이중 회전 만다라

function draw() {
  background(0, 0, 10);
  translate(width / 2, height / 2);

  for (let i = 0; i < 12; i++) {
    push();
      rotate(frameCount * 0.5 + i * 30);
      fill(i * 30, 80, 100);
      ellipse(80, 0, 20, 20);        // 바깥 원

      rotate(frameCount * -0.3);      // 역회전!
      rect(120, -5, 30, 10);          // 더 바깥 사각형
    pop();
  }
}

이중 회전입니다. 큰 회전 안에 작은 역회전. 복잡해 보이지만 push()/pop() 패턴만 잘 쓰면 됩니다.

확장 C: 속도 기반 선 굵기

// draw() 안 — 방향 B에서 활용
let speed = dist(x, y, x + random(-3, 3), y + random(-3, 3));
let weight = map(speed, 0, 5, 1, 8);
strokeWeight(weight);
stroke(hue, 80, 100, 150);
point(x, y);

빠르게 움직일수록 두꺼운 점. 궤적에 리듬감이 생깁니다.


마무리

스케치 저장 확인

지금 바로 Ctrl+S (Mac은 Cmd+S)로 저장하세요. 이 스케치가 실습과제 2입니다.

실습과제 2 제출 요건

항목 내용
과제명 인터랙티브 애니메이션
제출 기한 6주차 수업 전까지
제출 방식 P5.js Web Editor 링크 공유
평가 제출 완료 시 만점

완성도를 보는 게 아니라, 직접 시도하고 제출하는 것이 중요합니다.

필수 요구사항 최종 확인

# 요구사항 확인
1 변수를 매 프레임 갱신하여 움직임 구현
2 화면 경계 처리 (바운싱/래핑/constrain)
3 random() 또는 map() 1개 이상 활용
4 마우스 또는 키보드 인터랙션 1개 이상

이상이면 자동으로 다 만족합니다. 다음 주까지 더 발전시켜 제출해도 좋습니다.

완성 기준 최종 확인

단계 완성 기준 체크
기본 움직이고 + 부딪히고 + 반응한다
표준 random/map + 시각 효과 → 보기에도 좋다
심화 복합 효과 + 자기만의 스타일

오늘 배운 것 3줄 정리

3교시 핵심 요약
  1. 변수 갱신 = 움직임: x += speed 한 줄이 Casey Reas의 수천 개 에이전트의 원리
  2. random/map/constrain = 풍부한 표현: 규칙적인 움직임에 변화를 더하는 도구들
  3. 인터랙션 + 움직임 = 인터랙티브 작품: 4주차 + 5주차를 합치면 사용자가 개입하는 살아있는 작품

6주차 예고

오늘 바운싱 볼을 1개 만들었는데, 100개 만들고 싶으면 변수 200개를 선언해야 할까요? 아닙니다. 6주차에서 함수로 코드를 정리하고, 배열로 같은 종류의 데이터를 한 번에 관리하는 법을 배웁니다. 배열을 쓰면 많은 볼도 훨씬 짧고 읽기 쉬운 코드로 만들 수 있습니다.

(선택) 사전 학습 영상

미리 보면 다음 주가 더 쉬워집니다.

영상 내용 시간
The Coding Train “3.4 Random” random() 함수 복습 약 10분
The Coding Train “9.1 Transformations Pt.1” translate, rotate, push/pop 복습 약 22분