// Individually designed procedural gestures; no recordings or external samples. // Each factory owns its random/filter/delay state. Names describe an intended // impression, not a claim that a physical object was recorded. const SR=44100,TAU=2*Math.PI; const sine=(f,t)=>Math.sin(TAU*f*t); const gate=(t,a,b)=>t>=a&&tt<0?0:Math.exp(-t/d); const swell=(t,d)=>t<0||t>d?0:Math.sin(Math.PI*t/d)**2; function random(seed){return()=>{seed^=seed<<13;seed^=seed>>>17;seed^=seed<<5;return(seed>>>0)/2147483648-1;};} function noise(seed,alpha){const r=random(seed);let z=0;return()=>{const n=r();z+=alpha*(n-z);return z;};} function modes(t,freqs,times){return t<0?0:freqs.reduce((s,f,i)=>s+sine(f,t)*decay(t,times[i])/(1+i*.55),0);} const designs=[]; function add(group,name,title,duration,method,usage,factory){designs.push({group,name,title,duration,method,usage,factory});} add('foley','paper-crumple','종이 구김 질감',1.55,'불규칙한 길이의 노이즈 조각에 여러 개의 짧은 진폭 봉우리를 배치했습니다.','종이를 쥐거나 구기는 애니메이션에 맞춰 봉우리 위치를 비교하세요.',()=>{const n=noise(301,.8),events=[[.03,.13],[.24,.09],[.48,.22],[.82,.08],[.97,.28],[1.3,.18]];return t=>n()*events.reduce((s,[a,d])=>s+swell(t-a,d),0);}); add('foley','fabric-swipe','천 스침 질감',.85,'저역 통과 노이즈의 세기와 필터 응답을 한 번의 비대칭 마찰 곡선으로 움직였습니다.','옷감이 화면을 스치거나 손으로 천을 닦는 동작에 붙여 보세요.',()=>{const r=random(302);let z=0;return t=>{z+=(.025+.12*t)* (r()-z);return z*(t<.16?t/.16:Math.exp(-(t-.16)*6));};}); add('foley','zipper-pull','지퍼 이빨 연속음',.95,'속도가 달라지는 톱니 펄스에 짧은 노이즈를 곱하고 끝에 낮은 잠금음을 더했습니다.','길게 당기는 지퍼나 작은 톱니 이동의 속도에 맞춰 사용하세요.',()=>{const r=random(303);let p=0;return t=>{p+=(35+90*t)/SR;const tooth=(p%1)<.18?1:0;return r()*tooth*swell(t,.82)*.5+modes(t-.83,[180,870],[.04,.015]);};}); add('foley','sand-shake','작은 입자 흔들기',1.4,'두 번의 흔들기 봉우리 안에 무작위 미세 충돌과 필터 노이즈를 섞었습니다.','작은 용기를 두 번 흔드는 장면이나 입자 이동에 맞춰 보세요.',()=>{const r=random(304),n=noise(3041,.4);let grain=0;return t=>{grain=grain*.73+(r()>.94?r()*.8:0);return(grain+n()*.3)*(swell(t,.5)+swell(t-.72,.57));};}); add('foley','pencil-scratch','연필 긋기 질감',1.2,'가느다란 고역 노이즈를 세 번 끊고 작은 불규칙 진폭 변화를 넣었습니다.','짧은 글자 획이나 밑줄을 긋는 장면에 각 구간을 맞춰 보세요.',()=>{const r=random(305);let previous=0;return t=>{const v=r(),hi=v-previous;previous=v;return hi*(swell(t,.28)+swell(t-.38,.16)+swell(t-.69,.43))*(.32+.1*sine(71,t));};}); add('foley','coin-spin','회전하며 잦아드는 금속음',2.4,'접촉 간격이 좁아지는 충돌열과 고역 비정수 배음을 겹쳤습니다.','동전 회전이 멈추는 연출에서 끝부분의 빠른 떨림을 맞춰 보세요.',()=>{let p=0;return t=>{p+=(4+14*t*t)/SR;const hit=Math.exp(-(p%1)*23);return hit*modes(t,[1280,2093,3371],[2,1.7,1.2])*(1-t/2.4);};}); add('foley','ball-bounces','점차 빨라지는 공 튕김',1.9,'간격과 세기가 줄어드는 다섯 개의 저음 접촉음을 배치했습니다.','공이 여러 번 튄 뒤 멈추는 장면의 접촉 프레임과 비교하세요.',()=>{const events=[[0,1],[.65,.65],[1.1,.43],[1.42,.28],[1.64,.18]];return t=>events.reduce((s,[a,g])=>s+g*modes(t-a,[92,203],[.065,.025]),0);}); add('foley','glass-scatter','유리 조각 흩어짐 연출',1.65,'서로 다른 비정수 배음을 가진 짧은 충돌을 불규칙하게 흩어 놓았습니다.','작은 조각이 바닥에 퍼지는 시각 효과의 여러 접촉점에 활용하세요.',()=>{const events=[[0,1700],[.07,2900],[.19,2100],[.35,3600],[.58,2500],[.93,4100],[1.21,3200]];return t=>events.reduce((s,[a,f],i)=>s+modes(t-a,[f,f*1.47,f*2.19],[.1,.07,.04])/(1+i*.2),0);}); add('nature','water-drop','물방울 한 점 연출',.6,'짧게 올라가는 공명음과 높은 감쇠 배음을 더해 물방울 느낌을 만들었습니다.','물방울이나 한 점이 나타나는 순간에 짧게 사용하세요.',()=>t=>Math.sin(TAU*(610*t-180*.018*(1-Math.exp(-t/.018))))*decay(t,.09)+.3*sine(2300,t)*decay(t,.012)); add('nature','bubble-cluster','불규칙한 거품 연출',1.65,'발생 시각·상승 속도·길이가 다른 낮은 공명음 여섯 개를 겹쳤습니다.','여러 거품이 떠오르는 장면에서 단일 물방울과 비교하세요.',()=>{const ev=[[0,190,.07],[.13,270,.1],[.45,140,.12],[.54,340,.05],[.88,220,.13],[1.19,310,.07]];return t=>ev.reduce((s,[a,f,d])=>{const u=t-a;return s+(u<0?0:Math.sin(TAU*(f*u+220*u*u))*decay(u,d));},0);}); add('nature','rain-on-roof','지붕 빗방울 질감',3,'무작위 간격의 충돌들을 짧은 공명 필터에 통과시키고 낮은 노이즈 바닥을 더했습니다.','짧은 우천 장면의 배경으로 낮게 사용하세요. 자연 현장 녹음이나 무한 루프는 아닙니다.',()=>{const r=random(403),n=noise(4031,.07);let y1=0,y2=0;return t=>{const x=r()>.991?r():0,y=x+1.86*Math.cos(TAU*1950/SR)*y1-.865*y2;y2=y1;y1=y;return(y*.3+n()*.2)*Math.min(1,t*12,(3-t)*12);};}); add('nature','fire-crackle','불꽃 타닥임 질감',2.8,'저음 노이즈 위에 드문 짧은 크랙과 길이가 다른 잔떨림을 배치했습니다.','작은 불꽃 애니메이션의 배경에 쓰고 큰 폭발음과 구분하세요.',()=>{const r=random(404),low=noise(4041,.012);let crack=0;return t=>{crack=crack*.87+(r()>.9984?r()*2:0);return(low()*.65+crack)*Math.min(1,t*20,(2.8-t)*20);};}); add('nature','bird-whistle','두 구절 휘파람 연출',1.45,'두 개의 떨어진 고역 주파수 곡선에 작은 주기적 떨림을 넣었습니다.','캐릭터의 짧은 휘파람이나 새를 연상시키는 그래픽에 사용하세요. 종 식별용 녹음이 아닙니다.',()=>t=>{const a=t,b=t-.76;return(swell(a,.48)*Math.sin(TAU*(2200*a+350*a*a)+.5*sine(24,a)))+(b<0?0:swell(b,.55)*Math.sin(TAU*(3100*b-700*b*b)+.7*sine(19,b)));}); add('nature','cricket-pulses','풀벌레 펄스 연출',2.2,'높은 반송파를 빠른 미세 펄스로 자르고 세 개의 긴 묶음으로 나눴습니다.','밤 장면의 짧은 배경 질감으로 사용하세요. 실제 생물 채집 음원이 아닙니다.',()=>t=>sine(4700,t)*(Math.max(0,sine(43,t))**8)*(swell(t,.45)+swell(t-.76,.45)+swell(t-1.5,.5))); add('machine','clock-escapement','시계 틱톡',2.05,'공명 주파수가 다른 짧은 충격을 일정 간격으로 번갈아 배치했습니다.','시간 경과나 초침 이동 장면에서 네 번의 타격 위치를 맞춰 보세요.',()=>t=>[0,.5,1,1.5].reduce((s,a,i)=>s+modes(t-a,i%2?[1550,2950]:[930,2400],[.012,.006]),0)); add('machine','motor-runup','모터 가속',2.1,'누적 위상을 적분한 회전 기본음·배음과 노이즈의 회전 진폭을 함께 가속했습니다.','회전체가 정지에서 속도를 올리는 장면에 맞춰 사용하세요.',()=>{const n=noise(502,.16);let p=0;return t=>{p+=(28+95*(t/2.1)**2)/SR;return(Math.sin(TAU*p)*.3+Math.sin(TAU*p*6)*.12+n()*(.2+.15*Math.sin(TAU*p)))*Math.min(1,t*8,(2.1-t)*8);};}); add('machine','electric-buzz','간헐 전기 버즈',1.7,'50Hz 기반 톱니파와 빠른 위상 변화를 세 번의 불규칙한 켜짐 구간으로 나눴습니다.','전기 장치가 불안정하게 켜지는 화면 효과에 맞춰 사용하세요.',()=>t=>{const on=gate(t,0,.32)||gate(t,.47,.64)||gate(t,.88,1.57);return on?((t*50)%1-.5)*(.6+.4*sine(93,t))*Math.min(1,t*80,(1.7-t)*40):0;}); add('machine','printer-stepper','프린터 이송 연출',1.6,'세 구간의 톱니형 모터음에 속도가 다른 짧은 이동 펄스와 정지 충격을 더했습니다.','프린터 헤드나 작은 축이 이동하고 멈추는 장면에 활용하세요.',()=>{let p=0;return t=>{const k=t<.4?0:t<.92?1:2;p+=([180,310,130][k])/SR;return((p%1)-.5)*(swell(t,.36)+swell(t-.47,.37)+swell(t-1.02,.48))+.13*modes(t-.87,[500,1700],[.035,.012]);};}); add('machine','hinge-creak','경첩 삐걱임 연출',1.85,'불규칙하게 흔들리는 위상 변조음에 끊겼다 붙는 마찰 진폭을 적용했습니다.','천천히 열리는 경첩이나 뻑뻑한 장치의 움직임과 비교하세요.',()=>{const r=noise(505,.003);return t=>Math.sin(TAU*(180*t+60*t*t)+4*Math.sin(TAU*47*t+r()*20))*(.2+.8*Math.max(0,sine(5.3,t))) *swell(t,1.85);}); add('machine','pneumatic-release','공압 밸브 방출',1.2,'처음의 짧은 밸브 타격 뒤에 고역 노이즈를 넣고 필터와 세기를 함께 낮췄습니다.','압축 공기가 빠지거나 문 잠금이 풀리는 연출에 사용하세요.',()=>{const r=random(506);let z=0;return t=>{const n=r();z+=(.03+.4*Math.exp(-t*5))*(n-z);return(n-z)*decay(t-.035,.22)*(t>.035?1:0)+modes(t,[270,1700],[.02,.006])*.5;};}); add('percussion','sub-kick','서브 킥',.75,'지수적으로 내려가는 저음의 누적 위상과 짧은 어택을 합쳤습니다.','박자 시작이나 낮은 강조에 쓰고 저역이 겹치는 음악과 함께 확인하세요.',()=>{let p=0;return t=>{p+=(43+125*Math.exp(-t*38))/SR;return Math.sin(TAU*p)*decay(t,.16)+.1*sine(3400,t)*decay(t,.003);};}); add('percussion','snare-wire','스네어 와이어 연출',.48,'두 개의 짧은 막 공명과 고역 노이즈의 와이어형 감쇠를 겹쳤습니다.','짧은 리듬 강조나 빠른 컷 전환의 타격 위치에 맞춰 보세요.',()=>{const r=random(602);let z=0;return t=>{const n=r();z+=.14*(n-z);return modes(t,[180,330],[.055,.03])*.35+(n-z)*decay(t,.055);};}); add('percussion','brush-swirl','브러시 원형 마찰',1.35,'대역이 제한된 노이즈의 세기를 회전 주기로 흔들고 시작·끝을 완만하게 감쌌습니다.','둥글게 쓸거나 문지르는 움직임에 맞춰 사용하세요.',()=>{const n=noise(603,.23);let z=0;return t=>{const x=n();z+=.018*(x-z);return(x-z)*swell(t,1.35)*(.35+.65*(.5+.5*sine(4.7,t)));};}); add('percussion','handclap','세 겹 손뼉 연출',.45,'몇 밀리초 간격의 세 노이즈 어택 뒤에 짧은 확산 꼬리를 붙였습니다.','손뼉이나 세 번 겹치는 작은 타격을 표현할 때 영상과 맞춰 보세요.',()=>{const r=random(604);return t=>r()*([0,.012,.026].reduce((s,a)=>s+decay(t-a,.008)*(t>=a?1:0),0)+.25*decay(t-.03,.07)*(t>.03?1:0));}); add('percussion','woodblock','빈 목재 블록 연출',.55,'주파수 비율이 다른 두 짧은 공명과 약한 고역 어택을 조합했습니다.','가벼운 목재 접촉이나 짧은 리듬 점을 표현할 때 사용하세요.',()=>t=>modes(t,[780,1227,2460],[.035,.022,.008])); add('percussion','cymbal-wash','금속성 심벌 잔향',2.6,'서로 비정수 비율인 여섯 오실레이터와 고역 노이즈를 긴 감쇠로 합쳤습니다.','장면 끝이나 음악의 강조 후 남는 고역 잔향으로 사용하세요.',()=>{const r=random(606);let z=0;return t=>{const n=r();z+=.07*(n-z);return([2331,3417,4751,6103,7829,10007].reduce((s,f)=>s+Math.sign(sine(f,t))*.06,0)+(n-z)*.5)*decay(t,.48);};}); add('signal','telephone-key','전화 키패드 한 음',.32,'697Hz와 1209Hz 사인파를 동시에 재생하고 짧은 페이드를 적용했습니다.','전화 키 하나를 누르는 UI에 사용하세요. 실제 통신 기능은 없습니다.',()=>t=>(sine(697,t)+sine(1209,t))*.5*Math.min(1,t*200,(.32-t)*200)); add('signal','telegraph-pattern','짧고 긴 전신 패턴',2.5,'같은 반송파를 짧은 세 펄스·긴 두 펄스로 나누고 경계에 페이드를 넣었습니다.','신호 수신이나 전신을 연상시키는 장면의 리듬으로 사용하세요.',()=>{const events=[[0,.1],[.2,.1],[.4,.1],[.75,.35],[1.3,.35],[1.95,.1]];return t=>events.reduce((s,[a,d])=>s+(gate(t,a,a+d)?sine(820,t)*Math.min(1,(t-a)*300,(a+d-t)*300):0),0);}); add('signal','sonar-echo','소나 지연 반사',2.4,'좁은 대역의 짧은 신호 뒤에 세 번의 더 낮은 레벨 반사를 배치했습니다.','거리 탐지나 물속 탐색 그래픽에 반사 간격을 맞춰 보세요.',()=>t=>[0,.52,1.04,1.56].reduce((s,a,i)=>s+(.52**i)*sine(1180,Math.max(0,t-a))*decay(t-a,.095),0)); add('signal','radio-squelch','무전 스퀠치',.85,'대역 제한 노이즈를 문턱으로 끊고 마지막에 짧은 고역 휘슬을 붙였습니다.','무전 송수신의 시작·종료를 표현하는 연출에 사용하세요. 말소리는 포함하지 않습니다.',()=>{const n=noise(704,.35);let slow=0;return t=>{const x=n();slow+=.025*(x-slow);return gate(t,.02,.6)?(x-slow)*(.5+.5*Math.sign(sine(37,t))):.4*sine(2100,t)*swell(t-.64,.16);};}); add('signal','radio-tuning','라디오 주파수 탐색',1.9,'필터 노이즈 위에 교차하는 두 주파수 곡선과 불규칙한 수신 진폭을 더했습니다.','다이얼을 돌리거나 채널을 찾는 연출에 사용하세요. 방송 녹음은 없습니다.',()=>{const n=noise(705,.18);return t=>(n()*.6+Math.sin(TAU*(150*t+1600*t*t))*.22+Math.sin(TAU*(3100*t-700*t*t))*.12)*swell(t,1.9)*(.4+.6*Math.abs(sine(8,t)));}); add('signal','camera-shutter','카메라 셔터 연출',.38,'짧은 열림 충격과 약간 뒤의 닫힘 충격을 다른 공명·노이즈로 구성했습니다.','사진 촬영이나 작은 슬라이드 잠금의 두 접촉점에 맞춰 보세요.',()=>{const r=random(706);return t=>modes(t,[370,1400],[.012,.007])+.65*modes(t-.075,[220,930],[.025,.01])+r()*(decay(t,.005)+.3*decay(t-.075,.008)*(t>.075?1:0));}); add('motion','elastic-boing','탄성 흔들림',1.15,'진폭이 줄어드는 저주파 위상 변조로 튕긴 뒤 흔들리는 음높이를 만들었습니다.','탄성 있는 캐릭터나 젤리형 오브젝트가 흔들리는 장면에 붙여 보세요.',()=>t=>Math.sin(TAU*180*t+14*Math.sin(TAU*9*t)*Math.exp(-t*3.5))*decay(t,.24)); add('motion','vinyl-stop','회전 급정지',1.3,'기본음과 배음의 누적 위상 속도를 0에 가깝게 낮추고 회전 잡음을 더했습니다.','재생이나 회전체가 서서히 멈추는 화면 연출에 사용하세요.',()=>{let p=0;const n=noise(802,.4);return t=>{const v=Math.max(.02,1-t/1.3)**2;p+=540*v/SR;return(Math.sin(TAU*p)*.5+Math.sin(TAU*p*3)*.15+n()*.12)*Math.min(1,t*50,(1.3-t)*20);};}); add('motion','reverse-slam','역방향 흡입 뒤 충격',1.35,'0.85초의 노이즈 상승을 끊은 뒤 낮은 충격과 짧은 고역 타격을 붙였습니다.','장면이 빨려 들어간 후 닫히는 순간에 충격 위치를 맞춰 보세요.',()=>{const r=random(803);return t=>t<.85?r()*(t/.85)**4*.7:modes(t-.85,[57,121,1730],[.16,.08,.008]);}); add('motion','air-in-out','두 구간 공기 흐름',2.3,'필터 노이즈를 서로 다른 길이의 상승·하강 봉우리로 나누었습니다.','공기 펌프나 부풀었다 줄어드는 오브젝트의 움직임에 맞춰 보세요. 사람의 호흡 녹음이 아닙니다.',()=>{const n=noise(804,.085);return t=>n()*(swell(t,.76)*.6+swell(t-1.02,1.15));}); module.exports={designs,SR};