← DEVLOG
인터랙티브2025.11.085 min read

Harmony — 케플러의 꿈, 행성이 연주하는 음악

태양계 8개 행성의 공전 주기를 음높이로 변환한 인터랙티브 오케스트라 — Web Audio 합성, 음계 퀀타이즈, 녹음/재생

web-audiomusickeplerorbital-mechanicsrecording

시작 — Musica Universalis

1619년 요하네스 케플러는 Harmonices Mundi에서 행성의 공전 속도가 음악적 비율을 이룬다고 주장했습니다. 빠른 궤도의 행성은 높은 음, 느린 궤도의 행성은 낮은 음.

이 페이지는 그 아이디어를 코드로 실현합니다. 수성부터 해왕성까지, 실제 공전 주기 비율을 Hz로 변환해 각 행성에 음을 할당했습니다.


공전 주기에서 주파수로

행성의 공전 주기가 짧을수록 높은 음을 배정합니다. 수성(88일)은 C5(523Hz), 해왕성(60,190일)은 C2(65Hz).

// 실제 공전 주기 → 주파수 매핑 (역비례 스케일링)
const PLANETS = [
  { id: 'mercury', orbitalPeriodDays: 88, baseFrequency: 523.25, waveform: 'triangle' },
  { id: 'venus', orbitalPeriodDays: 225, baseFrequency: 392.0, waveform: 'triangle' },
  { id: 'earth', orbitalPeriodDays: 365, baseFrequency: 329.63, waveform: 'triangle' },
  // ...
  { id: 'neptune', orbitalPeriodDays: 60190, baseFrequency: 65.41, waveform: 'sine' },
];

내행성(수성화성)은 triangle 파형으로 밝고 선명한 음색, 외행성(목성해왕성)은 sine 파형으로 깊고 부드러운 음색을 부여했습니다. 크기가 큰 행성일수록 더 근본적인 진동이라는 느낌을 주려는 의도입니다.


음계 퀀타이즈

행성의 원래 주파수를 그대로 재생하면 불협화음이 됩니다. quantizeToScale 함수가 가장 가까운 음계 음에 스냅합니다.

function quantizeToScale(freq: number, scale: ScaleConfig): number {
  const semitonesFromA4 = 12 * Math.log2(freq / 440);
  let relativeSemitone = ((Math.round(semitonesFromA4) % 12) + 12) % 12;

  // 스케일에서 가장 가까운 음정 찾기
  let bestInterval = 0;
  let bestDist = Infinity;
  for (const interval of scale.intervals) {
    const dist = Math.abs(relativeSemitone - (interval % 12));
    if (dist < bestDist) {
      bestDist = dist;
      bestInterval = interval % 12;
    }
  }

  return 440 * Math.pow(2, (octaveBase + bestInterval) / 12);
}

장조, 단조, 펜타토닉 세 가지 음계를 제공합니다. 같은 행성 조합이라도 음계를 바꾸면 전혀 다른 분위기가 됩니다. 장조는 밝고 희망적, 단조는 깊고 몽환적, 펜타토닉은 동양적 선율이 느껴집니다.


녹음과 재생

행성을 켜고 끄는 순서, 음계 전환 시점을 타임라인 이벤트로 기록합니다.

interface CompositionEvent {
  time: number; // 녹음 시작 기준 ms
  type: 'planet' | 'scale';
  planetId?: string;
  action?: 'on' | 'off';
  scaleIdx?: number;
}

재생 시에는 각 이벤트를 setTimeout으로 스케줄링하고, requestAnimationFrame으로 타임라인 프로그레스를 실시간 업데이트합니다. 작곡은 최대 3개까지 localStorage에 저장됩니다.


궤도 뷰

중앙의 태양에서 동심원으로 8개 궤도가 펼쳐집니다. 각 행성은 궤도 위의 점으로 표현되고, 활성화되면 궤도선이 행성 고유 색상으로 빛납니다.

.orbit {
  border: 1px solid rgba(var(--js2-primary), 0.12);
  transition:
    border-color 0.4s,
    box-shadow 0.4s;
}
.orbitActive {
  border-color: var(--orbit-color);
  box-shadow:
    0 0 8px color-mix(in srgb, var(--orbit-color) 30%, transparent),
    inset 0 0 8px color-mix(in srgb, var(--orbit-color) 15%, transparent);
}

궤도 간격은 가장 큰 행성(목성, 50px)이 인접 궤도선을 침범하지 않도록 계산했습니다. minR=40, maxR=250으로 30px 간격을 확보합니다.


마치며

케플러는 행성의 음악을 들을 수 없었습니다. 17세기에는 우주의 소리를 재현할 방법이 없었으니까요.

400년이 지난 지금, 브라우저의 Web Audio API로 그의 상상을 실현할 수 있게 되었습니다. 수성의 높은 트라이앵글 파형과 해왕성의 깊은 사인파가 동시에 울릴 때, 그것은 과학과 음악의 경계에 있는 어떤 것입니다.

행성을 하나씩 켜보세요. 우주가 연주하기 시작합니다.

이 포스트와 연결된 콘텐츠

직접 체험하기