Persona Stage
페르소나 스테이지
An Incredibox-style interactive music tool built into this portfolio. 6 personas, 11 voices, a 16-step sequencer, swing, combo FX — all synthesized live in the browser via the Web Audio API with no audio assets.
이 포트폴리오 안에 직접 만든 Incredibox 스타일 인터랙티브 음악 도구. 6 페르소나·11 보이스·16스텝 시퀀서·스윙·콤보 FX를 브라우저에서 Web Audio API로 실시간 합성 — 오디오 파일 0개.
Problem
/ 문제Most browser music tools land somewhere awkward: either they ship megabytes of sample audio (slow load, licensing concerns, fixed timbres) or they stay so simple they feel like a toy. The goal for Persona Stage was to sit at the top of the Development page as something playable on the first tap, sample-free, and still useful for someone who wants to dig in.
브라우저 음악 도구는 보통 어색한 지점에 머문다 — 수 MB 샘플 오디오를 싣거나(로딩 느림·라이선스 부담·음색 고정), 너무 단순해서 장난감처럼 느껴지거나. Persona Stage의 목표는 Development 페이지 상단에서 첫 탭에 바로 재생되고, 샘플을 안 쓰면서도, 깊이 파고드는 사람한테도 쓸모가 있는 것이었다.
Architecture
/ 아키텍처Key Decisions
/ 핵심 결정No audio files — every voice synthesized live
오디오 파일 0개 — 모든 보이스를 실시간 합성
Each of the 11 voices is built from oscillators, noise buffers, envelopes, and filters in a Web Audio graph. A pre-allocated AnalyserNode per slot drives the level visualization. The cost: every drum, pluck, and pad had to be designed by ear from primitives. The benefit: 0 KB of audio assets shipped, no licensing, plenty of room to tune each voice, and the entire instrument fits inside the bundle.
11개 보이스 각각을 오실레이터·노이즈 버퍼·엔벨로프·필터로 Web Audio 그래프 안에서 직접 만든다. 슬롯마다 AnalyserNode를 미리 할당해 레벨 시각화에 쓴다. 비용: 모든 드럼·플럭·패드를 프리미티브로부터 귀로 디자인해야 했다. 이득: 오디오 에셋 0 KB, 라이선스 없음, 보이스마다 튜닝 여지가 넓고, 악기 전체가 번들 안에 들어간다.
Persona-first UX, sequencer behind Nerd Mode
페르소나 우선 UX, 시퀀서는 Nerd Mode 뒤로
A 16-step grid is intimidating for a first-time visitor. So the default surface is 6 personas — pick a sound, click a face, hear a loop. The full per-step pattern editor only appears when the user opts into Nerd Mode. Floor lowered, ceiling kept: two clicks gets a first tap making sound, and power users still have the grid.
16스텝 그리드는 처음 방문한 사람한테 위압적이다. 그래서 기본 화면은 6 페르소나 — 사운드 고르고, 얼굴 클릭, 루프 재생. 풀 스텝 단위 패턴 에디터는 사용자가 Nerd Mode를 켤 때만 나타난다. 천장은 그대로 두고 바닥만 낮춘 구조 — 처음 들른 사람도 두 번 클릭으로 소리를 내고, 파워 유저는 여전히 그리드를 쓸 수 있다.
Lookahead scheduler, not setInterval
Lookahead 스케줄러, setInterval 아님
The naive way to drive a beat is setInterval(tick, ms). It drifts under tab throttling and skips notes. Persona Stage uses the lookahead pattern: a low-frequency timer (every LOOKAHEAD_MS) pre-schedules every note that will fire in the next SCHEDULE_AHEAD window using AudioContext.currentTime. The audio thread plays them at the exact requested time. Timing stays stable even when the tab is backgrounded.
비트를 돌리는 단순한 방법은 setInterval(tick, ms)이다. 탭 throttling에 드리프트가 생기고 노트를 빠뜨린다. Persona Stage는 lookahead 패턴을 쓴다 — 낮은 주기 타이머(LOOKAHEAD_MS마다)가 향후 SCHEDULE_AHEAD 윈도우 안에 발생할 모든 노트를 AudioContext.currentTime 기반으로 미리 예약한다. 오디오 스레드가 정확한 시간에 재생한다. 탭이 백그라운드 상태여도 타이밍이 흔들리지 않는다.
URL-encoded mix state — no backend, links last
URL 인코딩 믹스 상태 — 백엔드 없음, 링크 영구
The entire mix (slot assignments, BPM, swing, time signature, pattern overrides) encodes into the URL hash via share.ts. No database, no auth, no cleanup — paste a link and the recipient hears the same mix. The constraint: the encoding has to stay backwards-compatible, so adding a new voice means deciding upfront what older URLs should fall back to.
믹스 전체(슬롯 할당, BPM, 스윙, 박자, 패턴 오버라이드)를 share.ts가 URL 해시에 인코딩한다. DB 없음, 인증 없음, 청소 없음 — 링크 붙여넣으면 받는 쪽도 같은 믹스를 듣는다. 제약: 인코딩이 하위 호환을 유지해야 해서, 새 보이스 추가할 때마다 옛 URL이 어디로 fallback할지 미리 정해야 한다.
Outcome
/ 결과Sits at the top of /work/development as the page's hero. Loads in under a second, plays without any user setup, and takes the place of the static screenshot most dev portfolios put there. The entire instrument — synthesis, scheduler, sequencer, share encoding, animation — ships in one Next.js bundle with zero audio assets.
/work/development 최상단에 페이지 hero로 들어간다. 1초 안에 로드되고 별도 설정 없이 재생되며, 보통 개발자 포트폴리오가 그 자리에 두는 정적 스크린샷을 대신한다. 합성·스케줄러·시퀀서·공유 인코딩·애니메이션까지 악기 전체가 오디오 에셋 0개로 Next.js 번들 하나에 담긴다.