홈 › 읽을거리 › 칼럼

효과음 181개를 코드로 만든 기록: 볼륨과 템포 조정기

칼럼 · 2026-10-08 · GamePool 편집팀

GamePool의 소리는 녹음 파일이 아닙니다. public/games/_shared/audio.js 파일 하나(64,609바이트)가 브라우저의 Web Audio로 효과음 181개와 배경음악 테마 50개를 그때그때 계산해서 냅니다. 이 글은 일반적인 게임 사운드 이론이 아니라, 그 파일 안의 숫자와 개발 이력에서 확인되는 사실만 정리한 것입니다. 어떤 값을 왜 그렇게 두었는지, 나중에 어떤 값을 고쳤는지를 적었습니다.

소리 파일 대신 계산식을 고른 구조

엔진의 바닥에는 두 함수가 있습니다. tone은 사각파·삼각파·톱니파·사인파 중 하나를 정해진 높이와 길이로 울리고, noise는 미리 만들어 둔 1초짜리 잡음 버퍼를 필터에 통과시켜 바람·타격·폭발 같은 소리를 만듭니다. 효과음 181개는 이 두 함수를 몇 줄씩 조합한 것입니다. 예를 들어 click은 700Hz 사각파를 0.04초, 볼륨 0.12로 한 번 울리는 한 줄이고, 폭발음 explode는 1600Hz에서 80Hz로 떨어지는 잡음 0.5초(볼륨 0.5)에 110Hz에서 35Hz로 내려가는 사인파 0.4초를 겹친 두 줄입니다.

소리가 모이는 길도 정해져 있습니다. 배경음악은 볼륨 0.15의 음악 버스, 효과음은 0.5의 효과음 버스로 들어가고, 둘이 합쳐진 뒤 마스터(0.9)를 지나 컴프레서(임계값 -14dB, 비율 5:1)를 거쳐 나갑니다. 음악을 효과음의 약 3분의 1 크기로 깔아 둔 것은, 게임 중 플레이어가 반응해야 하는 정보는 효과음이라고 우리가 판단했기 때문입니다. 마지막 단계의 컴프레서는 군단 점령전처럼 효과음이 한꺼번에 겹치는 순간에 가장 큰 소리를 눌러 주는 역할을 합니다. 파일을 쓰지 않으니 소리 때문에 내려받는 용량은 0이고, 대신 모든 음색을 코드로 흉내 내야 한다는 한계가 따라옵니다. 실제 악기 음원처럼 풍부한 소리는 나오지 않습니다.

볼륨 숫자에 담긴 의도

효과음 목록을 훑어보면 볼륨 값이 용도에 따라 뚜렷하게 갈립니다. 매 프레임 가까이 반복되는 소리는 작습니다. 이동 move는 260Hz에 0.03초, 볼륨 0.07이고, 발소리 step은 0.07, 시계 소리 tick은 0.06입니다. 반대로 한 번 나오면 판의 분위기를 바꾸는 소리는 큽니다. 저음 폭음 boom은 50Hz에서 22Hz로 내려가는 사인파를 0.9초, 볼륨 0.8로 울리고, 내려치기 slam은 62Hz 사인파에 0.75를 줍니다. 짧고 잦은 소리는 작게, 드물고 무거운 소리는 크게라는 단순한 규칙이지만, 이 규칙을 어기면 곧바로 티가 났습니다.

블록월드 전용 소리 묶음은 같은 동작도 재질별로 따로 만든 사례입니다. 돌을 캘 때(bw_hit_stone)는 3200Hz 대역 잡음에 1250Hz 사인파를 얹은 쨍한 소리, 흙(bw_hit_dirt)은 750Hz 이하의 둔탁한 잡음에 120Hz 사인파, 유리(bw_hit_glass)는 2400Hz와 3300Hz 두 음입니다. 블록을 설치하는 소리(bw_put_*)도 돌·나무·흙·모래·유리·천·물 일곱 가지가 따로 있습니다. 한 가지 소리를 높이만 바꿔 쓰지 않은 이유는 단순합니다. 화면을 보지 않고도 지금 무슨 블록을 다뤘는지 구분되기를 원했기 때문입니다.

알까기: 충돌 세기에 볼륨을 묶고, 겹치는 소리는 버리기

알까기의 돌 소리(stone)는 2600Hz 대역의 짧은 잡음 0.04초에 1000Hz에서 700Hz로 내려가는 사인파를 얹은 소리입니다. 이 소리를 부를 때 게임은 충돌 속도에 따라 볼륨을 0.25에서 1.0까지 바꾸고(0.25 + 충돌 속도 ÷ 900, 최대 1), 높이는 매번 0.75~1.15배 사이에서 무작위로 흔듭니다. 살짝 닿은 충돌과 세게 부딪힌 충돌이 같은 크기로 들리면 어색했고, 같은 높이가 반복되면 기계적으로 들렸기 때문입니다.

돌이 한꺼번에 여러 개 부딪히는 장면에서는 소리가 뭉개지는 문제가 있었습니다. 그래서 마지막 충돌음 이후 45밀리초가 지나지 않았으면 새 충돌음은 아예 재생하지 않습니다. 45밀리초 안에 겹친 두 소리는 어차피 구분이 잘 되지 않는다고 판단해, 뒤의 소리를 버리는 쪽을 골랐습니다. 같은 알까기에는 소리와 관련한 실수도 있었습니다. 게임이 끝난 뒤에도 승패 처리 함수 settle이 프레임마다 다시 불려 승리·패배 효과음이 계속 반복되고 승리 횟수까지 올라가는 오류였습니다. 원인은 진행 단계 값이 sim으로 남아 있어서 매 프레임 같은 처리가 되풀이된 것이었고, 함수 맨 앞에 이미 끝났으면 돌아가라는 한 줄과 단계를 idle로 바꾸는 한 줄로 고쳤습니다(2026년 10월 8일 커밋). 소리가 이상하게 반복된다는 증상이 로직 버그의 가장 먼저 눈에 띄는 신호였던 셈입니다.

군단 점령전: 발사음과 충돌음을 키운 변경

군단 점령전(mobpush)은 파란 병사 수백 명과 보라색 병사 수백 명이 부딪히는 게임이라, 모든 충돌에 소리를 붙이면 소음이 되고 하나도 붙이지 않으면 전투가 조용해집니다. 그래서 충돌 카운터를 두고 N번에 한 번만 소리를 냅니다. 10월 8일 커밋에서는 이 카운터 간격과 볼륨을 같이 손봤습니다. 변경 전후 값은 다음과 같습니다.

  • 대포 발사음(pop): 이전에는 40% 확률로 볼륨 0.14, 변경 후에는 발사할 때마다 볼륨 0.42에 높이 1.0~1.4배. 약 3배 키우고 매번 들리게 했습니다. 일반 발사에서는 대장 게이지 카운트가 짝수일 때 묵직한 저음 thock(0.3)을 한 겹 더 얹습니다.
  • 병사끼리 충돌(pop): 10번에 한 번 0.18에서 6번에 한 번 0.3으로. 소리 간격을 줄이면서 볼륨도 키웠습니다.
  • 배수 문 통과음(select)과 병사 소멸 문(wrong): 둘 다 0.12에서 0.2로. 적 기지 피격(hit): 0.2에서 0.3으로. 거대 병사가 죽을 때(explode): 0.35에서 0.5로.
  • 대장 출격(powerup): 0.35에서 0.5로 올리고 blast(0.35)를 겹침. 새로 생긴 부서지는 벽에는 break(0.6)와 crumble(0.35), 숨은 황금 문이 열릴 때 powerup(0.4)을 붙였습니다.

이 변경의 기준은 숫자 몇 개로 정리됩니다. 정보가 담긴 소리는 반드시 들려야 한다는 것, 그리고 잦은 소리는 간격을 벌리고 대신 크기를 키워 존재감을 유지한다는 것입니다. 발사음은 플레이어가 직접 누르는 동작의 확인 소리이므로 확률을 없애 100% 들리게 했습니다. 반면 병사끼리 부딪히는 소리는 초당 수십 번 일어나는 일이라 6번에 한 번으로도 전투가 진행 중이라는 느낌을 주기에 충분했습니다. 커밋 메시지에는 키운다고만 적혀 있지만, 코드의 차이를 보면 키운 것과 솎은 것이 함께 있었습니다.

배경음악은 작곡이 아니라 규칙입니다

배경음악 테마는 음표가 아닌 설정값 한 줄로 정의됩니다. 박자(bpm), 기준 음(root), 음계(scale), 코드 진행(prog), 선율·베이스 악기, 드럼 종류, 그리고 난수 씨앗(seed)입니다. compose 함수가 이 값으로 곡을 만듭니다. 한 마디는 8스텝(8분음표)이고 코드 진행 4개를 두 번 돌려 8마디를 만들며, 두 마디 길이의 선율 모양 A·B·C 세 가지를 만들어 반복합니다. 강박에는 85%, 약박에는 45% 확률로 음을 놓고, 강박 음은 코드의 근음·3음·5음 중에서 고릅니다. 마디 배치는 A와 B를 번갈아 쓰다가 마지막 마디에서 C로 끝냅니다. 씨앗이 고정되어 있어서 같은 테마는 언제나 같은 곡이 나옵니다.

이 방식에서는 곡의 길이가 박자에서 바로 계산됩니다. 8마디 × 8스텝 = 64스텝이므로 장기·오목·알까기가 쓰는 oriental(88bpm)은 약 21.8초, 기억력 게임의 calm(84bpm)은 약 22.9초, 블록 낙하의 arcade(140bpm)는 약 13.7초, 레오니스 보스의 lw_boss(172bpm)는 약 11.2초마다 처음으로 돌아갑니다. 블록월드 동굴의 mc_cave(48bpm)는 40초입니다. 짧은 곡이 반복되는 구조라서 템포가 빠른 곡일수록 한 바퀴가 짧아 금방 지겨워질 위험이 있습니다. 이를 줄이려고 벽돌깨기는 bk_a·bk_b·bk_c 세 곡을 레벨마다 번갈아 재생합니다.

템포로 게임의 성격을 나눈 실제 표

테마 값을 모아 보면 박자가 게임의 성격을 그대로 말해 줍니다. 장기·오목·알까기는 모두 oriental(88bpm, 5음 음계, 드럼 없음)을 쓰고, 기억력 게임은 calm(84bpm), 블록 낙하는 arcade(140bpm, 단조, 록 드럼), 두더지 잡기는 happy(126bpm, 장조)입니다. 느린 박자에 드럼이 없는 조합은 한 수를 오래 생각하는 게임에 쓰고, 빠른 박자에 킥·스네어가 있는 조합은 반사 신경이 필요한 게임에 씁니다.

챔피언 방어전은 상황마다 전용 곡을 갈아 끼웁니다. 월드맵의 cd_map은 100bpm, 낮 전투 cd_day 118, 설원 cd_frost 108, 밤 cd_night 126, 공성 cd_siege 138, 보스 cd_boss 152입니다. 모두 금관과 북(taiko)을 중심으로 하되 박자만 올라갑니다. 보스가 없는 스테이지의 마지막 웨이브에서는 사이렌과 함께 cd_siege로, 보스가 나타나는 순간에는 cd_boss로 바뀌어 화면 설명 없이도 상황이 달라졌음을 알 수 있게 했습니다. 레오니스는 챕터 1부터 8까지 lw_1(138bpm)에서 lw_8(156bpm)까지 챕터별 곡이 따로 있고, 챕터 6 곡은 140bpm으로 오히려 내려가는 등 단순히 올라가기만 하지는 않습니다. 챕터 보스 곡 lw_boss는 172bpm입니다.

레오니스 위기 연출: 같은 곡을 40% 빠르게

레오니스에는 요새 체력이 30%(RAGE_AT = 0.3) 아래로 떨어지면 터지는 위기 연출이 있습니다. 2026년 10월 7일 커밋에서 적 전군이 각성해 능력치가 1.3배가 되고 붉은 기운이 도는 효과와 함께 배경음악 템포를 올리는 장치를 붙였습니다. 별도의 위기 곡을 새로 만든 것이 아니라 엔진에 긴장도(tension, 0~1)라는 값을 추가해, 지금 재생 중인 곡의 스텝 길이를 1 + 0.4 × 긴장도로 나눕니다. 긴장도 1이면 같은 곡이 40% 빨라집니다. 계산하면 1챕터 곡 lw_1은 138bpm에서 약 193bpm으로, 보스 곡 lw_boss는 172bpm에서 약 241bpm으로 올라갑니다.

템포만 올리면 단순히 빨라진 곡으로 들리기 때문에 두 가지를 더 얹었습니다. 하나는 곡의 기준 음에서 두 옥타브 아래 톱니파와 거기서 증4도 위의 톱니파를 겹친 낮은 불협화음 드론입니다. 다른 하나는 긴장도 0.3을 넘으면 두 스텝마다 120Hz에서 48Hz로 떨어지는 심장 박동 같은 저음이 들어가고, 0.5를 넘으면 마디 끝에 고음 잡음이 한 번 더 붙는 것입니다. 긴장도는 25밀리초마다 목표와의 차이의 3%씩 따라가므로, 코드 주석대로 1~2초에 걸쳐 서서히 올라갑니다. 갑자기 템포가 튀면 곡이 고장 난 것처럼 들리기 때문에 일부러 천천히 변하게 했습니다.

관리해야 할 곳도 생겼습니다. 긴장도는 한 번 올리면 저절로 내려가지 않기 때문에, 전투를 시작할 때와 전투 결과를 정리할 때 두 곳에서 GameAudio.tension(0)을 호출해 다음 판이 빠른 템포로 시작하는 일이 없게 해 두었습니다. 위기 연출의 효과음도 같은 자리에서 정해집니다. 경보(alarm, 0.35)와 포효(roar, 0.4, 높이 0.7배)를 겹치고 화면을 흔드는 동작이 한 번에 일어납니다.

엔진이 알아서 해 주는 것과 한계

브라우저는 사용자가 한 번 누르거나 키를 누르기 전에는 소리를 허용하지 않습니다. 그래서 엔진은 pointerdown·keydown·touchstart를 가장 먼저 받아 소리 시스템을 깨우고, 그 전에 요청된 배경음악은 기억해 두었다가 깨어난 직후 시작합니다. 단, 효과음은 기억하지 않습니다. 첫 입력 이전에 요청된 효과음은 재생되지 않고 버려집니다. 탭을 숨기면 소리를 멈추고 돌아오면 다시 켜며, 보상형 광고가 재생되는 동안에는 pauseAll로 모든 소리를 멈춥니다. 소리 설정은 소리 켬(음악+효과음), 효과음만, 끔 세 단계를 둥근 버튼이 순서대로 돌고 gp:sound 키로 저장하지만, 저장소가 막힌 환경에서는 저장만 건너뛰고 소리는 정상 동작합니다.

이 구조의 약점도 분명합니다. 소리가 모두 코드이므로 이름이 181개로 늘어나면서 어떤 게임이 어떤 이름을 쓰는지 한눈에 보기 어렵습니다. 기기 성능이 낮으면 합성 음이 겹칠 때 지연이 생길 수 있어서, 효과음을 줄여 달라는 판단이 들면 위의 군단 점령전처럼 간격을 벌려야 합니다. 합성음은 어디까지나 칩튠에 가까운 소리라는 한계도 인정해야 합니다. 대신 파일 용량 없이, 박자와 음계와 볼륨 숫자 몇 개로 게임마다 다른 분위기를 만들 수 있다는 점이 지금 사이트에 맞는 선택이었습니다.

소리를 직접 확인하는 방법

  • 알까기에서 돌을 약하게 밀었을 때와 최대로 당겼을 때의 충돌음을 비교해 보세요. 볼륨 0.25와 1.0의 차이가 들립니다.
  • 레오니스에서 요새 체력이 30% 아래로 떨어지는 순간 배경음악이 같은 곡인 채 빨라지는지, 낮은 드론이 깔리는지 들어 보세요.
  • 군단 점령전에서 대포를 계속 쏴 보세요. 발사음은 매번 들리고, 병사 충돌음은 몇 번에 한 번만 섞입니다.
  • 소리 버튼을 한 번 눌러 효과음만 켠 상태로 해 보면 음악 없이도 정보가 전달되는지 확인할 수 있습니다.

소리는 게임의 진행 정보와 분위기를 동시에 전달하지만, 이 사이트의 소리는 모두 몇 줄의 계산식입니다. 그래서 고치는 일도 숫자를 바꾸는 일로 끝납니다. 군단 점령전의 볼륨 0.14가 0.42가 되었고, 레오니스의 곡이 40% 빨라졌다는 것이 그 증거입니다. 앞으로 새 게임을 추가할 때도 같은 규칙을 지킬 계획입니다. 자주 나는 소리는 작게, 드문 소리는 크게 두고, 곡의 박자는 게임의 속도감에 맞춥니다.

관련 게임 플레이하기

다른 읽을거리

칼럼장기·오목·알까기를 화면으로 옮기며 내린 결정들장기 AI의 읽는 수 1·3·5, 오목에 금수를 넣지 않은 선택과 6목 처리, 알까기 마찰·반발 값과 AI 후보 수까지. 소스에서 확인되는 구현 사실로 풀어 썼습니다.공략2048 모서리 전략: 봇 1,000판으로 확인한 차이이 사이트 2048의 소스 규칙(새 타일 확률, 합치기 순서, 점수)과 봇 1,000판 결과로 모서리 고정과 정렬이 왜 효과가 있는지 풀어 썼습니다.입문스도쿠 입문: 봇이 푼 난이도별 필요 기법이 사이트 스도쿠가 난이도별로 주는 단서 수와, 같은 규칙으로 만든 판을 논리 풀이 봇으로 풀어 본 결과, 힌트·되돌리기의 실제 동작을 정리한 입문 글입니다.입문오목 입문: 컴퓨터 점수표를 읽고 삼삼으로 이기기이 사이트 오목 컴퓨터의 점수표와 난이도별 차이를 소스에서 읽고, 삼삼 준비 모양과 봇 대전 결과로 초반 배치·수비 우선순위·무르기 활용을 정리했습니다.공략블록 퍼즐 습관: 점수표와 봇 실험으로 본 판 관리블록 낙하와 블록 채우기의 점수표·속도·블록 출현 확률을 소스에서 읽고, 봇으로 확인한 결과로 구멍을 만들지 않는 습관과 한 번에 지우는 판단을 정리했습니다.가이드GamePool 이 내 브라우저에 두는 것과 서버에 보내는 것저장 키, 랭킹 닉네임 규칙, 서버 검증과 IP 처리, 지우는 법, 복제 사이트를 가려내는 방법을 GamePool 소스 기준으로 정리했습니다.