온라인 메트로놈 (탭 템포)
샘플 단위로 정확한 타이밍, 조절 가능한 박자 수, 강조되는 시각적 펄스, 귀로 BPM을 찾을 수 있는 탭 템포 버튼을 갖춘 무료 온라인 메트로놈입니다.
조회수 230회
리듬에 맞춰 탭하세요 — 최소 2번 탭이 필요합니다
정확한 타이밍을 위해 Web Audio API의 look-ahead 스케줄러(AudioContext.currentTime)를 사용합니다 — 단순한 setInterval이 아니므로 페이지 부하가 커도 시간이 어긋나지 않습니다.
대부분의 브라우저 메트로놈이 어긋나는 이유
JavaScript로 메트로놈을 만드는 가장 직관적인 방법은 setInterval(playClick, 60000 / bpm)입니다. 몇 초 동안은 잘 작동하지만 — 곧 어긋나기 시작합니다. setInterval이 보장하는 것은 콜백이 요청한 지연 시간보다 더 일찍 실행되지 않는다는 것뿐이며, 실제로 얼마나 더 늦게 실행될 수 있는지는 전혀 보장하지 않습니다. JS 메인 스레드는 페이지가 수행하는 모든 작업 — DOM 업데이트, 레이아웃 계산, 가비지 컬렉션 — 과 공유되며, 이 중 어느 것이든 대기 중인 타이머를 수십 밀리초 지연시킬 수 있습니다. 대부분의 UI 코드에서는 이것이 눈에 띄지 않지만, 메트로놈에서는 치명적입니다. 이번 박자에서 40밀리초 늦고 다음 박자에서 10밀리초 늦는 클릭음은 단순히 어긋난 정도가 아니라 귀로 들릴 만큼 흔들리며, 이를 기준으로 연습하는 연주자를 적극적으로 헷갈리게 만듭니다.
연주자가 메트로놈에 의존하는 이유는 정확히, 외부 기준 없이는 일정한 시간에 대한 인간의 지각이 신뢰할 수 없기 때문입니다 — 이는 1815년 요한 멜첼(Johann Maelzel)이 태엽식 버전을 특허 등록한 이래로 이 장치가 존재하는 이유 자체입니다. 스스로 어긋나는 소프트웨어 메트로놈은 그 목적 자체를 무너뜨리며, 해결책은 더 영리한 타이머가 아닙니다. requestAnimationFrame을 포함한 모든 JS 타이머는 동일한 메인 스레드 병목을 공유합니다. 진짜 해결책은 JavaScript에게 정확한 순간에 소리를 내달라고 요청하는 것 자체를 그만두는 것입니다.
룩어헤드 스케줄링: 이 메트로놈이 정확함을 유지하는 방법
이 도구는 과거 구글 크롬 Web Audio 팀에 몸담았던 크리스 윌슨(Chris Wilson)이 널리 인용되는 글 "A Tale of Two Clocks"에서 널리 알린 패턴을 사용합니다. 스케줄링 로직과 사운드 타이밍을 완전히 분리하는 방식입니다. Web Audio API의 AudioContext는 샘플 단위로 정확한 시계를 가진 자체 오디오 스레드에서 동작하며, 이는 JavaScript에 AudioContext.currentTime으로 노출됩니다. 그 시계 기준으로 미래 시각을 지정해 osc.start(t)를 호출하면, 오디오 엔진은 그 순간 메인 스레드가 무엇을 하고 있든 상관없이 정확히 t 시점에 소리를 재생합니다 — 정확한 박자에 맞춰 "깨어날" 필요가 전혀 없었던 것입니다.
메인 스레드가 하는 일은 오직 장부 기록뿐입니다. 약 25밀리초마다 스케줄러가 앞으로 100밀리초 안에 있을 박자를 확인하고, 각 박자에 대해 정확한 미래의 currentTime 값으로 osc.start()를 호출합니다. 이 루프는 대략적으로만 제시간에 맞으면 되기 때문에 일반적인 지터(jitter)는 들리는 문제를 전혀 일으키지 않습니다 — 소리 자체는 여전히 샘플 단위로 정확하게 예약되어 있기 때문입니다. 각 클릭음은 약 30밀리초 길이의 오실레이터 파열음이며, 각 마디의 첫 박자에서는 더 높은 음으로 재생되어 귀로 들리는 강세를 만들고, 대응하는 시각적 펄스와 동기화됩니다.
탭 템포는 다른 문제를 해결합니다 — 읽을 악보가 없는 음악의 BPM을 찾는 것입니다. 박자에 맞춰 탭하면 도구가 탭 사이의 간격을 측정하고 60000 / averageIntervalMs로 변환합니다. 최소 두 번의 탭이 필요하며, 최근 탭들의 평균을 내면 작은 타이밍 오차가 완화됩니다. 2초를 넘는 간격이 생기면 시퀀스가 초기화되는데, 이는 대개 박자가 크게 느려졌다기보다 놓쳤음을 의미하기 때문입니다.
자주 묻는 질문
단순한 setInterval 메트로놈은 왜 시간이 지날수록 불규칙하게 들리나요?
setInterval은 콜백이 요청한 지연 시간보다 일찍 실행되지 않는다는 것만 약속할 뿐입니다 — 메인 스레드가 레이아웃 계산, 가비지 컬렉션, 또는 다른 탭의 활동으로 바쁠 때마다 더 늦게 실행될 수 있습니다. 이런 작고 불규칙한 지연이 박자마다 누적되면서 클릭음이 일정한 템포에 고정되어 있지 못하고 귀로 들릴 만큼 어긋나게 됩니다. 이 도구는 JS 타이머가 정확한 순간에 실행되기를 기대하는 대신, 실제 소리의 타이밍을 Web Audio API 자체의 오디오 스레드 시계에 맡겨 이 문제를 피합니다.
"룩어헤드 스케줄링"이란 무엇이고 왜 어긋남을 해결하나요?
JavaScript가 소리를 내야 할 바로 그 순간에 재생을 시도하지 않는 패턴입니다. 루프가 약 25밀리초마다 앞으로 100밀리초 안에 박자가 있는지 확인하고, AudioContext.currentTime에 정확한 오프셋을 더한 값을 사용해 osc.start(정확한미래시각) 방식으로 각 박자의 오실레이터를 미리 예약합니다. 오디오 엔진은 메인 스레드와 독립적인 자체 스레드에서 그 오실레이터를 샘플 단위로 정확하게 실행합니다 — 그래서 스케줄링 루프는 다소 부정확해도 실제 박자는 정확하게 유지됩니다.
탭 템포는 BPM을 어떻게 계산하나요?
탭 템포 버튼을 클릭할 때마다 시각이 기록됩니다. 두 번째 탭부터 도구는 이전 탭과의 간격을 측정하고, 최근 간격들의 짧은 이력을 유지하며, 그 평균을 밀리초 단위 평균 간격으로 60000을 나누어 템포로 변환합니다. 최소 두 번의 탭이 필요하며, 2초를 넘는 간격이 생기면 시퀀스가 초기화됩니다. 이는 거의 항상 극도로 느린 템포로 계속 탭했다기보다 탭이 멈췄음을 의미하기 때문입니다.
왜 각 마디의 첫 박자만 음높이가 다른가요?
이것은 기계식 메트로놈과 디지털 메트로놈 모두에서 표준으로 쓰이는 강세입니다. 마디의 첫 박자(다운비트)는 나머지 박자보다 더 높은 주파수로 재생되며, 다른 색상의 시각적 펄스와 함께 표시됩니다. 이를 통해 3/4박자나 6/8박자 같은 패턴에서 "하나"가 어디에 오는지 한눈에, 또는 귀로 바로 알 수 있습니다. 이는 특히 그렇지 않으면 모든 박자가 똑같이 들리는 박자표에서 마디를 정확히 세는 데 중요합니다.
이 메트로놈은 어떤 BPM 범위를 지원하며, BPM은 무엇을 의미하나요?
BPM은 분당 박자 수(beats per minute)를 뜻하며, 60초 동안 몇 번의 박이 발생하는지를 나타냅니다. 이 도구는 30에서 300 BPM까지 지원하여 악보에서 쓰이는 거의 모든 범위를 아우릅니다. 그라베(Grave) 표기는 대략 25-45 BPM, 걷는 속도의 안단테(Andante)는 대략 76-108, 프레스토(Presto) 악절은 200 BPM을 넘을 수 있습니다. 120 BPM이 기본값인 이유는 흔하고 편안한 연습 템포이면서 동시에 고전적인 모데라토(Moderato) 표기이기 때문입니다.
비슷한 도구
문제 신고하기
온라인 메트로놈 (탭 템포)
댓글
아직 댓글이 없습니다 — 첫 댓글을 남겨보세요!