14주차 2교시 — FFT 주파수 분석 + 이미지/영상 + 기말 프로젝트 안내
강의 아웃라인: week-14-outline 이전 교시: week-14-period1-student (사운드 로드·재생·Amplitude·마이크) 다음 교시: week-14-period3-student (사운드 비주얼 실습 + 기말 구상)
| 항목 | 내용 |
|---|---|
| 대상 | P5.js 입문자 (테크아트 전공, 1교시 Amplitude·마이크 학습 완료) |
| 소요 시간 | 45분 |
| 핵심 도구 | P5.js Web Editor, p5.sound (FFT), loadImage,
createCapture |
-
p5.FFT로 주파수 스펙트럼을 분석하고 이퀄라이저를 만들 수 있습니다 -
loadImage()·createCapture()로 이미지·웹캠을 통합할 수 있습니다 - 사운드+파티클 결합 패턴으로 1교시 도입의 약속을 회수합니다
- 기말 프로젝트의 제출물·일정·평가 기준을 이해할 수 있습니다
1교시 복습
- 사운드 파일을 로드할 때 사용하는 함수는? →
loadSound() - 사운드를
preload()에 넣는 이유는? → 용량이 크니까setup()전에 완전히 로드해야 해서 - 브라우저에서 소리가 안 나올 때 해결 방법은? →
userStartAudio()+ 클릭 amp.getLevel()은 어떤 값을 돌려주나? →0~1사이의 음량
핵심 5번에서 마이크 입력도 다뤘습니다.
setInput() 한 줄로 같은 Amplitude 패턴이
파일/마이크 둘 다 통한다는 점이 핵심입니다.
1교시 → 2교시 연결
1교시는 전체 음량 분석이었습니다. 숫자 하나 — 지금 이 순간 전체적으로 얼마나 큰지. 음악에는 저음(베이스 드럼, 쿵쿵), 중음(보컬, 기타), 고음(심벌, 찌이잉)이 있습니다. 이걸 따로 분석하면 훨씬 풍부한 비주얼이 가능합니다.
2교시는 FFT(주파수 분석) → 이미지·웹캠 통합 → 사운드+파티클 통합 사례 → 기말 프로젝트 안내 순서로 진행합니다.
심화 개념 1 —
p5.FFT로 주파수 스펙트럼 분석
FFT란? (개념만 간단히)
FFT — Fast Fourier Transform. 수학적으로는 복잡하지만, 수학을 몰라도 됩니다. P5.js가 다 해줍니다.
이퀄라이저(EQ) 보신 적 있으면 떠올려 보세요. 음악 앱이나 스피커에 있는 막대 그래프. 저음 막대가 쿵쿵 움직이고, 고음 막대가 찌이잉 움직이고. FFT가 바로 그것입니다. 소리를 주파수별로 분해해서 지금 이 순간 저음·중음·고음이 각각 얼마인지 숫자로 알려줍니다.
1교시 p5.Amplitude |
2교시 p5.FFT |
|---|---|
| 소리의 전체 크기 | 소리의 주파수별 크기 |
숫자 1개 (0~1) |
숫자 1024개 (배열, 각 0~255) |
6주차에서 배운 배열이 여기서 쓰입니다. 1024개의 숫자가 담긴 배열입니다.
기본 사용법 — 이퀄라이저 바 차트
let song;
let fft;
function preload() {
song = loadSound('assets/music.mp3');
}
function setup() {
createCanvas(400, 400);
colorMode(HSB, 255); // 3주차 HSB 색상
fft = new p5.FFT();
}
function draw() {
background(0);
let spectrum = fft.analyze(); // 배열 (길이 1024)
noStroke();
for (let i = 0; i < spectrum.length; i++) {
// 인덱스 → x 위치
let x = map(i, 0, spectrum.length, 0, width);
// 값(0~255) → 막대 높이
let h = map(spectrum[i], 0, 255, 0, height);
// 인덱스 → 색상 (저주파=빨강, 고주파=보라)
fill(map(i, 0, spectrum.length, 0, 255), 255, 255);
rect(x, height - h, width / spectrum.length, h);
}
}
function mousePressed() {
userStartAudio();
song.loop();
}코드 한 줄씩.
| 코드 | 설명 |
|---|---|
colorMode(HSB, 255) |
3주차 HSB 색상 모드 |
fft = new p5.FFT() |
FFT 분석기 생성 |
let spectrum = fft.analyze() |
1024개 주파수 대역 에너지를 배열로 반환 (각 값
0~255) |
map(i, 0, spectrum.length, 0, width) |
배열 인덱스 → x 좌표 |
map(spectrum[i], 0, 255, 0, height) |
에너지 값 → 막대 높이 |
fill(map(i, 0, spectrum.length, 0, 255), 255, 255) |
HSB의 Hue만 변화 → 무지개 색 |
rect(x, height - h, width/spectrum.length, h) |
아래에서 위로 자라는 막대 |
3주차 for문 + 5주차 map() + 2주차
rect() + 3주차 colorMode(HSB). 지금까지 배운
것들이 전부 동원됩니다.
배열의 앞쪽(인덱스 0~100)이 저주파(베이스), 뒤쪽(인덱스 900~1023)이 고주파(심벌)입니다. 그래서 왼쪽 막대가 베이스에 반응하고, 오른쪽이 고음에 반응합니다.
실행하면 이퀄라이저 바 차트가 실시간으로 움직이고, 저음이 강한 구간에서는 왼쪽 빨간 막대가 높아지며, 고음이 강한 구간에서는 오른쪽 보라색 막대가 높아집니다.
에너지 밴드 추출 — getEnergy() (더 직관적인
방법)
1024개를 전부 다루기 부담스러우면, p5.sound가 미리 나눠놓은 밴드를 쓸 수 있습니다.
function draw() {
colorMode(RGB, 255); // 앞선 HSB 예제와 이어 작성할 때 색상 기준 복귀
background(0);
fft.analyze(); // 반드시 먼저 호출!
let bass = fft.getEnergy("bass"); // 저음 (0~255)
let mid = fft.getEnergy("mid"); // 중음 (0~255)
let treble = fft.getEnergy("treble"); // 고음 (0~255)
// 저음 → 빨간 원
let bassSize = map(bass, 0, 255, 10, 200);
fill(255, 0, 0);
noStroke();
ellipse(100, height / 2, bassSize, bassSize);
// 중음 → 초록 사각형
let midSize = map(mid, 0, 255, 10, 150);
fill(0, 255, 0);
rectMode(CENTER);
rect(200, height / 2, midSize, midSize);
// 고음 → 파란 삼각형
let trebleSize = map(treble, 0, 255, 10, 100);
fill(0, 0, 255);
triangle(
300, height / 2 - trebleSize / 2,
300 - trebleSize / 2, height / 2 + trebleSize / 2,
300 + trebleSize / 2, height / 2 + trebleSize / 2
);
}저음 → 큰 빨간 원, 중음 → 초록 사각형, 고음 → 파란 삼각형. 각각 독립적으로 반응합니다.
getEnergy()에 넣을 수 있는 밴드.
| 밴드 | 주파수 범위 | 대응하는 소리 |
|---|---|---|
"bass" |
20-140 Hz | 베이스 드럼, 베이스 기타 |
"lowMid" |
140-400 Hz | 보컬 낮은 음역 |
"mid" |
400-2600 Hz | 보컬, 기타 멜로디 |
"highMid" |
2600-5200 Hz | 보컬 높은 음역 |
"treble" |
5200-14000 Hz | 심벌, 하이햇 |
전부 쓸 필요는 없습니다. 보통 bass, mid,
treble 세 개면 충분합니다.
1교시의 p5.Amplitude와 2교시의 p5.FFT는
어떤 차이가 있나요?
답: Amplitude는 전체 음량 숫자 하나, FFT는 주파수별로 나눠 분석.
심화 개념 2 — 이미지 활용:
loadImage()
사운드 다음으로, 이미지를 P5.js에 가져오는 방법입니다.
let img;
function preload() {
img = loadImage('assets/photo.jpg');
}
function setup() {
createCanvas(400, 400);
}
function draw() {
image(img, 0, 0, width, height);
}11주차 preload() 패턴과 완전히 같습니다.
loadJSON() → loadSound() →
loadImage(). 전부 같은 구조입니다.
image(img, x, y)— 원래 크기로 표시image(img, x, y, w, h)— 지정한 크기로 표시
tint()로 이미지 색조 변경
function draw() {
tint(255, 0, 0); // 빨간 틴트
image(img, 0, 0, width, height);
}function draw() {
tint(255, 128); // 반투명 (알파값)
image(img, 0, 0, width, height);
}사운드 + 이미지 결합 — 음량에 따라 이미지가 펄럭이는 효과
let song, amp, img;
function preload() {
song = loadSound('assets/music.mp3');
img = loadImage('assets/photo.jpg');
}
function setup() {
createCanvas(400, 400);
amp = new p5.Amplitude();
}
function draw() {
background(0);
let level = amp.getLevel();
let s = map(level, 0, 1, 100, 400);
let alpha = map(level, 0, 1, 100, 255);
tint(255, alpha);
image(img, width / 2 - s / 2, height / 2 - s / 2, s, s);
}
function mousePressed() {
userStartAudio();
song.loop();
}음량이 크면 이미지가 커지고 선명해지고, 작으면 작아지고 투명해집니다. 사진이 숨 쉬는 것처럼 펄럭거리는 효과입니다.
13주차 파티클 시스템의 display()에서
ellipse(this.position.x, this.position.y, 12)를
image(img, this.position.x - 8, this.position.y - 8, 16, 16)로
교체하면, 작은 그림이 분수처럼 쏟아지는 이미지 파티클이
됩니다. 13주차 2교시에서 예고했던 그것입니다. 기말에서 별·하트·로고
이미지로 바꾸면 곧장 작품이 됩니다.
심화 개념 3 — 웹캠 활용:
createCapture()
이미지는 정적인 파일이었는데, 실시간 영상도 가져올 수 있습니다. 바로 웹캠입니다.
let video;
function setup() {
createCanvas(400, 400);
video = createCapture(VIDEO);
video.size(400, 400);
video.hide(); // 캔버스 밑에 별도로 표시되는 HTML 요소 숨기기
}
function draw() {
image(video, 0, 0, width, height);
}실행하면 브라우저가 카메라 접근 권한을 묻습니다. ’허용’을 눌러야 동작합니다.
video.hide()가 필요한 이유
createCapture()는 캔버스 외에 HTML
<video> 요소를 하나 더 만듭니다.
video.hide()를 안 넣으면 캔버스 밑에 영상이 하나 더
보입니다. hide()로 숨기고 draw() 안에서
image()로만 표시합니다.
웹캠 + 사운드 결합
function draw() {
image(video, 0, 0, width, height);
let level = amp.getLevel();
let size = map(level, 0, 1, 0, 300);
fill(255, 100);
noStroke();
ellipse(width / 2, height / 2, size, size);
}웹캠 영상 위에 음량에 따라 도형을 오버레이하면 — 자기 모습 위에 사운드 이펙트가 올라가는 인터랙티브 미디어아트가 됩니다.
심화 개념 4 — 사운드+파티클 통합 사례
1교시 도입에서 본 ‘음악에 반응하는 파티클’ 데모, 어떻게 만들어지는지 풀어 봅니다. 13주차 파티클 시스템 + 1교시 Amplitude의 결합입니다.
핵심 아이디어는 Emitter의 addParticle() 호출
빈도를 음량에 비례시키는 것입니다. 13주차 Emitter는
run() 안에서 addParticle()을 매 프레임 한 번
부르는데, 이번 버전에서는 run() 안의 자동 생성 한 줄만
빼고, 외부에서 음량만큼 addParticle()을 호출합니다.
class Emitter {
// ... 13주차와 동일: constructor, addParticle ...
// 13주차 run()에서 this.addParticle() 자동 호출만 제거
run() {
for (let i = this.particles.length - 1; i >= 0; i--) {
this.particles[i].update();
this.particles[i].display();
if (this.particles[i].isDead()) {
this.particles.splice(i, 1);
}
}
}
}draw()는 음량에 비례해 파티클을 방출한 뒤,
run()으로 업데이트·표시·소멸 처리를 이어갑니다.
let song, amp, emitter;
function preload() {
song = loadSound('assets/music.mp3');
}
function setup() {
createCanvas(640, 360);
amp = new p5.Amplitude();
emitter = new Emitter(width / 2, height / 2);
}
function draw() {
background(0, 25); // 잔상 효과
let level = amp.getLevel();
let count = floor(map(level, 0, 0.5, 0, 10)); // 음량 비례 방출량
for (let i = 0; i < count; i++) {
emitter.addParticle();
}
emitter.run();
}
function mousePressed() {
userStartAudio();
song.loop();
}비트가 강할 때 파티클이 폭발하듯 쏟아지고, 조용해지면 잦아드는 화면이 나옵니다. 1교시 도입에서 본 그 데모가 이렇게 만들어진 것입니다.
베이스만 추출해서 파티클 크기·속도에 매핑하면 더
인상적입니다 — fft.getEnergy("bass") (심화 1번에서 다룬
그것).
기말 프로젝트의 가장 강력한 조합 중 하나 — 사운드 리액티브 파티클입니다. 13주차 + 14주차 두 주차 내용 결합의 모범 사례라, 기말 평가 기준의 ’기술 2가지 이상 활용’이 자연스럽게 충족됩니다.
기말 프로젝트 안내
1. 기말 프로젝트 개요
| 항목 | 내용 |
|---|---|
| 과제명 | 인터랙티브 미디어 콘텐츠 제작 |
| 비중 | 40% (학기 전체 성적의 가장 큰 부분) |
| 주제 | 자유 (1~14주차 내용 활용) |
| 15주차 | 프로젝트 작업 + 1:1 피드백 |
| 16주차 | 최종 제출 |
제출물.
- P5.js 소스코드 — Web Editor 링크 또는 파일
- 시연영상 — 30초~1분 길이, 화면 녹화
15주차에 개별 피드백 시간이 있습니다. 완성하고 오지 않아도 됩니다. 진행 중인 상태로 가져오면 같이 방향을 잡고 기술적 도움을 드립니다. 16주차에 최종 제출 — 시연영상과 소스코드 두 개를 제출합니다.
2. 주제 선정 가이드 — 1~14주차 총정리
| 주차 | 핵심 기술 | 기말 활용 예시 |
|---|---|---|
| 1-2 | setup/draw, 도형, 변수 | 기본 캔버스 구성, 움직이는 도형 |
| 3 | 색상(RGB/HSB), for문 | 패턴 생성, 그라데이션, 반복 도형 |
| 4 | 조건문, 마우스/키보드 | 인터랙티브 반응, 상태 전환 |
| 5 | 애니메이션, random, map | 움직임, 랜덤 효과, 값 매핑 |
| 6 | 함수, 배열 | 코드 구조화, 다수 요소 관리 |
| 7 | 클래스, 객체 배열 | 독립 객체 설계, 대량 생성 |
| 9 | 벡터, 삼각함수 | 물리 기반 움직임, 원운동 |
| 10 | 힘, 물리 시뮬레이션 | 중력, 마찰, 바람 효과 |
| 11 | JSON/CSV 데이터 | 데이터 시각화 |
| 12 | Perlin Noise, Flow Field | 유기적·자연스러운 움직임 |
| 13 | 파티클 시스템 | 폭발·비·눈·불꽃 이펙트 |
| 14 | 사운드·이미지·웹캠 | 멀티미디어 결합 |
2가지 이상의 주차 내용을 결합하세요. 하나만 쓰면 실습과제 수준입니다. 두 가지 이상을 조합해야 기말 프로젝트의 깊이가 나옵니다.
주제 방향 예시.
| 방향 | 설명 | 활용 주차 |
|---|---|---|
| A. 사운드 리액티브 비주얼 | 음악에 반응하는 파티클·도형·패턴 | 14 + 13 + 3 |
| B. 인터랙티브 시뮬레이션 | 물리 법칙 기반 놀이·작품 | 9-10 + 4 + 7 |
| C. 데이터 아트 | 데이터를 활용한 시각화 작품 | 11 + 3 + 5 |
| D. 제너러티브 아트 | 노이즈·알고리즘으로 생성되는 작품 | 12 + 5 + 7 |
| E. 자유 주제 | 위 내용을 자유롭게 조합 | 자유 |
A~D는 예시일 뿐입니다. 여기 없는 조합도 얼마든지 가능합니다. 핵심은 본인이 흥미로운 주제를 찾는 것입니다.
3. 평가 기준
| 항목 | 비중 | 세부 내용 |
|---|---|---|
| 기술 구현 | 40% | 수업에서 배운 기법 2가지 이상 활용 |
| 인터랙션 | 20% | 마우스·키보드·사운드 등 사용자 상호작용 포함 |
| 작품성 | 20% | 시각적 완성도, 컨셉의 일관성 |
| 발표 자료 | 20% | 시연영상 품질, 코드 정리 상태 |
기술 구현이 40%로 가장 크지만, ’코드가 완벽해야 한다’는 뜻이 아닙니다. 수업에서 배운 기법을 실제로 활용했는지가 중요합니다.
완벽한 코드보다 본인만의 아이디어가 중요합니다. 기술적으로 완벽하지 않아도, 컨셉이 흥미롭고 직접 시도한 흔적이 보이면 좋은 점수를 받을 수 있습니다.
인터랙션 20% — 반드시 사용자가 무언가를 할 수 있어야 합니다. 단순히 자동으로 돌아가는 영상이 아니라, 클릭·키보드·소리 등으로 작품에 관여할 수 있어야 합니다.
시연영상은 화면 녹화 30초~1분이면 됩니다. 별도 편집은 필요 없고, 작품이 어떻게 동작하는지 보여주면 됩니다.
완성 안 해도 됩니다. 진행 중인 상태로 가져오면 1:1 피드백을 받을 수 있습니다. 방향이 안 잡혔어도 괜찮습니다. 핵심은 다음 주에 무언가를 가져오는 것입니다.
3교시 실습 안내
3교시는 세 부분입니다.
| 파트 | 시간 | 내용 |
|---|---|---|
| Part A | 25분 | 사운드 비주얼 실습 — 1~2교시 내용 직접 구현 |
| Part B | 15분 | 기말 프로젝트 구상 — 주제·방향·기술 선정 |
| 마무리 | 5분 | 저장 + 일정 재확인 + 학기 회고 |
Part A는 세 가지 방향 중 하나를 골라 시작합니다.
- 방향 A: 음량 반응 비주얼
(
p5.Amplitude) — 가장 쉬움, 막막하면 여기서 시작 - 방향 B: 이퀄라이저 바 차트 (
p5.FFT+fft.analyze()) — 중간 난이도 - 방향 C: 저음/중음/고음 분리 비주얼
(
fft.getEnergy()) — 도전
먼저 A부터 시작하고, 잘 되면 B나 C로 넘어갑니다. 스타터 코드는 3교시에서 받습니다. 사운드 파일은 강사가 준비한 샘플을 쓰거나, 자기 곡(MP3, 30초~1분)을 가져와도 됩니다.
Part B는 기말 주제를 구체적으로 구상하는 시간입니다. 종이에 스케치하고, 쓸 기술을 정하고, 여유가 되면 프로토타입을 시작합니다.
Part A에서는 먼저 1교시 완성 코드를 복사해 붙여넣고, 거기서 시작합니다. 코드가 돌아가는 상태에서 수정하는 게 가장 안전합니다.