44 < meta charset ="utf-8 ">
55 < title > Hear the Traffic (2025)</ title >
66 < style >
7-
7+ body {
8+ font-family : system-ui, sans-serif;
9+ }
10+ # guage {
11+ margin-block : 1rem ;
12+ display : block;
13+ padding : 0 ;
14+ inline-size : 100% ;
15+ }
16+ # toggle-sound {
17+ font : inherit;
18+ }
819 </ style >
920 </ head >
1021 < body >
1122 < h1 > Hear the Traffic</ h1 >
1223 < p > By 真空 (2025)</ p >
1324 < button id ="toggle-sound "> Unmute/Mute</ button >
25+ < meter id ="guage " min ="0 " max ="255 "> </ meter >
1426 < script >
1527 const toggleButton = document . querySelector ( '#toggle-sound' ) ;
28+ const guage = document . querySelector ( '#guage' ) ;
1629 let sound = false ;
1730
18- let realData = new Array ( 120 ) . fill ( 0 ) ;
31+ let absData = new Array ( 120 ) . fill ( 0 ) ;
1932
2033 toggleButton . addEventListener ( 'click' , ( ) => {
2134 if ( sound ) {
@@ -29,21 +42,33 @@ <h1>Hear the Traffic</h1>
2942
3043 let ac = null ;
3144 let osc = null ;
32- let freq = 30 ;
45+ let dynamicRange = 3 ;
46+ let worklet = null ;
47+ let gain = null ;
48+ let gainGain = null ;
49+ let analyser = null ;
50+
51+ let analyserArray = null ;
3352
3453 const update = ( ) => {
3554 fetch ( 'https://metrics.nc.menhera.org/_traffic.json' ) . then ( async ( res ) => {
3655 if ( ! res . ok ) return ;
3756 const data = await res . json ( ) ;
38- freq = data [ 0 ] * 2 ;
39- realData = [ 0 , ... data . slice ( 7 , 126 ) ] ;
57+ dynamicRange = data [ 0 ] ;
58+ absData = [ 0 , ... data . slice ( 1 ) ] ;
4059
4160 if ( osc ) {
42- let len = realData . length ;
61+ let len = absData . length ;
62+ const realData = new Array ( len ) . fill ( 0 ) ;
4363 const imagData = new Array ( len ) . fill ( 0 ) ;
64+ for ( let i = 0 ; i < len ; i ++ ) {
65+ let phase = Math . random ( ) * 2 * Math . PI ;
66+ realData [ i ] = absData [ i ] * Math . cos ( phase ) ;
67+ imagData [ i ] = absData [ i ] * Math . sin ( phase ) ;
68+ }
4469 const periodicWave = ac . createPeriodicWave ( realData , imagData ) ;
45- osc . frequency . value = freq ;
4670 osc . setPeriodicWave ( periodicWave ) ;
71+ gain . gain . value = 33 * dynamicRange ;
4772 }
4873 } ) . catch ( ( e ) => {
4974 console . error ( e ) ;
@@ -54,25 +79,67 @@ <h1>Hear the Traffic</h1>
5479
5580 function sound_start ( ) {
5681 ac = new AudioContext ( ) ;
82+ analyser = ac . createAnalyser ( ) ;
83+ analyser . fftSize = 8192 ;
84+ const bufferLength = analyser . frequencyBinCount ;
85+ analyserArray = new Uint8Array ( bufferLength ) ;
5786 osc = ac . createOscillator ( ) ;
58- osc . frequency . value = freq ;
59- let len = realData . length ;
87+ osc . frequency . value = 0.0667 ;
88+ let len = absData . length ;
89+ const realData = new Array ( len ) . fill ( 0 ) ;
6090 const imagData = new Array ( len ) . fill ( 0 ) ;
91+ for ( let i = 0 ; i < len ; i ++ ) {
92+ let phase = Math . random ( ) * 2 * Math . PI ;
93+ realData [ i ] = absData [ i ] * Math . cos ( phase ) ;
94+ imagData [ i ] = absData [ i ] * Math . sin ( phase ) ;
95+ }
6196 const periodicWave = ac . createPeriodicWave ( realData , imagData ) ;
6297 osc . setPeriodicWave ( periodicWave ) ;
63- osc . connect ( ac . destination ) ;
64- ac . resume ( ) ;
65- osc . start ( ) ;
98+ osc . connect ( analyser ) ;
99+ ac . audioWorklet . addModule ( 'fm-processor.js' ) . then ( ( ) => {
100+ worklet = new AudioWorkletNode ( ac , 'fm-processor' ) ;
101+ const freqParam = worklet . parameters . get ( 'frequency' ) ;
102+ const gainParam = worklet . parameters . get ( 'gain' ) ;
103+ gain = new GainNode ( ac ) ;
104+ gainGain = new GainNode ( ac ) ;
105+ gainGain . gain . value = 0.2 ;
106+ freqParam . value = 440 ;
107+ osc . connect ( gain ) ;
108+ osc . connect ( gainGain ) ;
109+ gain . connect ( freqParam ) ;
110+ gainParam . value = - 0.5 ;
111+ gainGain . connect ( gainParam ) ;
112+ gain . gain . value = dynamicRange * 33 ;
113+ worklet . connect ( ac . destination ) ;
114+ ac . resume ( ) ;
115+ osc . start ( ) ;
116+ } ) ;
66117 }
67118
68119 function sound_stop ( ) {
69120 if ( ! ac ) return ;
70121 ac . close ( ) ;
71122 ac = null ;
72123 osc = null ;
124+ analyser = null ;
125+ }
126+
127+ function draw ( ) {
128+ requestAnimationFrame ( draw ) ;
129+
130+ if ( ! analyser ) return ;
131+ analyser . getByteTimeDomainData ( analyserArray ) ;
132+ let sum = 0 ;
133+ analyserArray . forEach ( v => {
134+ sum += v ;
135+ } ) ;
136+ let value = sum / analyserArray . length ;
137+ guage . value = value ;
73138 }
74139
75140 update ( ) ;
141+
142+ draw ( ) ;
76143 </ script >
77144 </ body >
78145</ html >
0 commit comments