markburn commited on
Commit
8e70b2b
·
verified ·
1 Parent(s): 0632963

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +815 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Neon Noice Monitor
3
- emoji: 🐨
4
- colorFrom: red
5
- colorTo: red
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: neon-noice-monitor
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: blue
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,815 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Neon Noise Monitor</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js"></script>
10
+ <style>
11
+ @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&family=Roboto:wght@300;400;500&display=swap');
12
+
13
+ :root {
14
+ --primary: #00f0ff;
15
+ --secondary: #ff00f0;
16
+ --bg-dark: #0a0a1a;
17
+ --bg-light: #1a1a2e;
18
+ }
19
+
20
+ body {
21
+ font-family: 'Roboto', sans-serif;
22
+ background-color: var(--bg-dark);
23
+ color: white;
24
+ overflow-x: hidden;
25
+ }
26
+
27
+ .orbitron {
28
+ font-family: 'Orbitron', sans-serif;
29
+ }
30
+
31
+ .glow {
32
+ text-shadow: 0 0 10px var(--primary), 0 0 20px var(--primary);
33
+ }
34
+
35
+ .glow-secondary {
36
+ text-shadow: 0 0 10px var(--secondary), 0 0 20px var(--secondary);
37
+ }
38
+
39
+ .btn-glow {
40
+ box-shadow: 0 0 15px var(--primary);
41
+ }
42
+
43
+ .btn-glow:hover {
44
+ box-shadow: 0 0 25px var(--primary);
45
+ }
46
+
47
+ .btn-glow-secondary {
48
+ box-shadow: 0 0 15px var(--secondary);
49
+ }
50
+
51
+ .btn-glow-secondary:hover {
52
+ box-shadow: 0 0 25px var(--secondary);
53
+ }
54
+
55
+ .border-glow {
56
+ border: 1px solid var(--primary);
57
+ box-shadow: 0 0 10px var(--primary), inset 0 0 10px var(--primary);
58
+ }
59
+
60
+ .pulse {
61
+ animation: pulse 2s infinite;
62
+ }
63
+
64
+ @keyframes pulse {
65
+ 0% { opacity: 0.7; }
66
+ 50% { opacity: 1; }
67
+ 100% { opacity: 0.7; }
68
+ }
69
+
70
+ .visualizer-bar {
71
+ background: linear-gradient(to top, var(--primary), var(--secondary));
72
+ width: 4px;
73
+ margin: 0 2px;
74
+ border-radius: 2px;
75
+ transition: height 0.1s ease-out;
76
+ }
77
+
78
+ .noise-level-indicator {
79
+ height: 10px;
80
+ background: linear-gradient(to right, #00ff00, #ffff00, #ff0000);
81
+ border-radius: 5px;
82
+ position: relative;
83
+ overflow: hidden;
84
+ }
85
+
86
+ .noise-level-indicator::after {
87
+ content: '';
88
+ position: absolute;
89
+ top: 0;
90
+ left: 0;
91
+ right: 0;
92
+ bottom: 0;
93
+ background: linear-gradient(90deg,
94
+ rgba(0, 255, 0, 0.6) 0%,
95
+ rgba(255, 255, 0, 0.6) 50%,
96
+ rgba(255, 0, 0, 0.6) 100%);
97
+ animation: shine 3s infinite linear;
98
+ }
99
+
100
+ @keyframes shine {
101
+ 0% { transform: translateX(-100%); }
102
+ 100% { transform: translateX(100%); }
103
+ }
104
+
105
+ .theme-selector input[type="radio"]:checked + label {
106
+ border-color: var(--primary);
107
+ box-shadow: 0 0 10px var(--primary);
108
+ }
109
+
110
+ .peak-marker {
111
+ position: absolute;
112
+ width: 2px;
113
+ background-color: var(--secondary);
114
+ top: 0;
115
+ bottom: 0;
116
+ z-index: 10;
117
+ }
118
+
119
+ .peak-value {
120
+ position: absolute;
121
+ top: -20px;
122
+ transform: translateX(-50%);
123
+ font-size: 10px;
124
+ color: var(--secondary);
125
+ }
126
+ </style>
127
+ </head>
128
+ <body class="min-h-screen flex flex-col">
129
+ <header class="container mx-auto px-4 py-6">
130
+ <h1 class="orbitron text-4xl md:text-5xl font-bold text-center mb-2 glow">NEON NOISE MONITOR</h1>
131
+ <p class="text-center text-gray-400">Real-time ambient noise level visualization</p>
132
+ </header>
133
+
134
+ <main class="flex-grow container mx-auto px-4 py-6">
135
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
136
+ <!-- Left Panel -->
137
+ <div class="lg:col-span-1 space-y-6">
138
+ <!-- Microphone Status -->
139
+ <div class="bg-gray-900 bg-opacity-50 rounded-xl p-6 border-glow">
140
+ <h2 class="orbitron text-xl mb-4 flex items-center">
141
+ <i class="fas fa-microphone mr-3 text-purple-500"></i>
142
+ <span>MICROPHONE STATUS</span>
143
+ </h2>
144
+ <div class="flex items-center justify-between">
145
+ <span id="micStatus" class="text-gray-400">Click start to begin</span>
146
+ <div id="micStatusIndicator" class="w-4 h-4 rounded-full bg-gray-600"></div>
147
+ </div>
148
+ </div>
149
+
150
+ <!-- Current Session -->
151
+ <div class="bg-gray-900 bg-opacity-50 rounded-xl p-6 border-glow">
152
+ <h2 class="orbitron text-xl mb-4 flex items-center">
153
+ <i class="fas fa-clock mr-3 text-blue-400"></i>
154
+ <span>CURRENT SESSION</span>
155
+ </h2>
156
+ <div class="text-center">
157
+ <div id="sessionTimer" class="orbitron text-3xl mb-2 glow">00:00:00</div>
158
+ <div class="text-sm text-gray-400">Monitoring duration</div>
159
+ </div>
160
+ </div>
161
+
162
+ <!-- Controls -->
163
+ <div class="bg-gray-900 bg-opacity-50 rounded-xl p-6 border-glow">
164
+ <h2 class="orbitron text-xl mb-4 flex items-center">
165
+ <i class="fas fa-sliders-h mr-3 text-green-400"></i>
166
+ <span>CONTROLS</span>
167
+ </h2>
168
+ <div class="flex flex-col space-y-4">
169
+ <button id="startBtn" class="btn-glow orbitron py-3 px-6 bg-blue-600 hover:bg-blue-700 rounded-lg font-bold transition-all">
170
+ <i class="fas fa-play mr-2"></i> START MONITORING
171
+ </button>
172
+ <button id="stopBtn" disabled class="btn-glow-secondary orbitron py-3 px-6 bg-purple-600 hover:bg-purple-700 rounded-lg font-bold transition-all">
173
+ <i class="fas fa-stop mr-2"></i> STOP
174
+ </button>
175
+ <button id="resetBtn" class="orbitron py-3 px-6 bg-gray-700 hover:bg-gray-600 rounded-lg font-bold transition-all">
176
+ <i class="fas fa-redo mr-2"></i> RESET DATA
177
+ </button>
178
+ </div>
179
+ </div>
180
+
181
+ <!-- Threshold Settings -->
182
+ <div class="bg-gray-900 bg-opacity-50 rounded-xl p-6 border-glow">
183
+ <h2 class="orbitron text-xl mb-4 flex items-center">
184
+ <i class="fas fa-bell mr-3 text-yellow-400"></i>
185
+ <span>NOISE THRESHOLD</span>
186
+ </h2>
187
+ <div class="space-y-4">
188
+ <div>
189
+ <label for="thresholdSlider" class="block text-sm text-gray-400 mb-2">Alert Level (0-100)</label>
190
+ <input type="range" id="thresholdSlider" min="0" max="100" value="70" class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer">
191
+ <div class="flex justify-between text-xs text-gray-400 mt-1">
192
+ <span>0</span>
193
+ <span id="thresholdValue">70</span>
194
+ <span>100</span>
195
+ </div>
196
+ </div>
197
+ <div class="flex items-center">
198
+ <input type="checkbox" id="enableThreshold" class="mr-2" checked>
199
+ <label for="enableThreshold" class="text-sm">Enable threshold alerts</label>
200
+ </div>
201
+ </div>
202
+ </div>
203
+ </div>
204
+
205
+ <!-- Main Panel -->
206
+ <div class="lg:col-span-2 space-y-6">
207
+ <!-- Real-time Noise Level -->
208
+ <div class="bg-gray-900 bg-opacity-50 rounded-xl p-6 border-glow">
209
+ <h2 class="orbitron text-xl mb-4 flex items-center">
210
+ <i class="fas fa-volume-up mr-3 text-red-400"></i>
211
+ <span>REAL-TIME NOISE LEVEL</span>
212
+ </h2>
213
+ <div class="text-center mb-6">
214
+ <div id="noiseLevel" class="orbitron text-6xl md:text-8xl font-bold mb-2 glow pulse">--</div>
215
+ <div id="noiseDescription" class="text-xl text-gray-400">Microphone inactive</div>
216
+ </div>
217
+
218
+ <!-- Visualizer -->
219
+ <div class="flex justify-center items-end h-32 mb-4" id="audioVisualizer">
220
+ <!-- Bars will be added dynamically -->
221
+ </div>
222
+
223
+ <!-- Noise Level Indicator -->
224
+ <div class="noise-level-indicator mb-2">
225
+ <div id="noiseLevelBar" class="h-full bg-green-500 rounded-lg" style="width: 0%"></div>
226
+ </div>
227
+ <div class="flex justify-between text-xs text-gray-400">
228
+ <span>Quiet</span>
229
+ <span>Moderate</span>
230
+ <span>Loud</span>
231
+ </div>
232
+
233
+ <!-- Stats -->
234
+ <div class="grid grid-cols-2 gap-4 mt-6">
235
+ <div class="bg-gray-800 bg-opacity-50 rounded-lg p-4">
236
+ <div class="text-sm text-gray-400 mb-1">PEAK LEVEL</div>
237
+ <div id="peakLevel" class="orbitron text-2xl glow-secondary">--</div>
238
+ </div>
239
+ <div class="bg-gray-800 bg-opacity-50 rounded-lg p-4">
240
+ <div class="text-sm text-gray-400 mb-1">AVERAGE</div>
241
+ <div id="averageLevel" class="orbitron text-2xl">--</div>
242
+ </div>
243
+ </div>
244
+ </div>
245
+
246
+ <!-- Historical Data -->
247
+ <div class="bg-gray-900 bg-opacity-50 rounded-xl p-6 border-glow">
248
+ <h2 class="orbitron text-xl mb-4 flex items-center">
249
+ <i class="fas fa-chart-line mr-3 text-green-400"></i>
250
+ <span>HISTORICAL DATA</span>
251
+ </h2>
252
+ <div class="relative h-64">
253
+ <canvas id="noiseChart"></canvas>
254
+ </div>
255
+ <div class="flex justify-between mt-4">
256
+ <div class="text-sm text-gray-400">Last 4 hours of data</div>
257
+ <div class="flex space-x-2">
258
+ <button id="smoothBtn" class="text-xs px-3 py-1 bg-gray-700 rounded hover:bg-gray-600">
259
+ <i class="fas fa-wave-square mr-1"></i> Smooth
260
+ </button>
261
+ <button id="rawBtn" class="text-xs px-3 py-1 bg-gray-700 rounded hover:bg-gray-600">
262
+ <i class="fas fa-chart-bar mr-1"></i> Raw
263
+ </button>
264
+ </div>
265
+ </div>
266
+ </div>
267
+
268
+ <!-- Peak Values Dashboard -->
269
+ <div class="bg-gray-900 bg-opacity-50 rounded-xl p-6 border-glow">
270
+ <h2 class="orbitron text-xl mb-4 flex items-center">
271
+ <i class="fas fa-mountain mr-3 text-purple-400"></i>
272
+ <span>PEAK VALUES (LAST 4 HOURS)</span>
273
+ </h2>
274
+ <div class="relative h-64">
275
+ <canvas id="peakChart"></canvas>
276
+ </div>
277
+ <div class="grid grid-cols-3 gap-4 mt-4">
278
+ <div class="bg-gray-800 bg-opacity-50 rounded-lg p-3">
279
+ <div class="text-sm text-gray-400 mb-1">HIGHEST PEAK</div>
280
+ <div id="highestPeak" class="orbitron text-xl glow-secondary">--</div>
281
+ </div>
282
+ <div class="bg-gray-800 bg-opacity-50 rounded-lg p-3">
283
+ <div class="text-sm text-gray-400 mb-1">AVG PEAKS</div>
284
+ <div id="avgPeaks" class="orbitron text-xl">--</div>
285
+ </div>
286
+ <div class="bg-gray-800 bg-opacity-50 rounded-lg p-3">
287
+ <div class="text-sm text-gray-400 mb-1">THRESHOLD BREACHES</div>
288
+ <div id="thresholdBreaches" class="orbitron text-xl">--</div>
289
+ </div>
290
+ </div>
291
+ </div>
292
+ </div>
293
+ </div>
294
+ </main>
295
+
296
+ <footer class="container mx-auto px-4 py-6 text-center text-gray-500 text-sm">
297
+ <p>Neon Noise Monitor v1.0 | Uses Web Audio API | Data stays in your browser</p>
298
+ </footer>
299
+
300
+ <script>
301
+ // DOM Elements
302
+ const startBtn = document.getElementById('startBtn');
303
+ const stopBtn = document.getElementById('stopBtn');
304
+ const resetBtn = document.getElementById('resetBtn');
305
+ const noiseLevel = document.getElementById('noiseLevel');
306
+ const noiseDescription = document.getElementById('noiseDescription');
307
+ const noiseLevelBar = document.getElementById('noiseLevelBar');
308
+ const micStatus = document.getElementById('micStatus');
309
+ const micStatusIndicator = document.getElementById('micStatusIndicator');
310
+ const sessionTimer = document.getElementById('sessionTimer');
311
+ const peakLevel = document.getElementById('peakLevel');
312
+ const averageLevel = document.getElementById('averageLevel');
313
+ const audioVisualizer = document.getElementById('audioVisualizer');
314
+ const thresholdSlider = document.getElementById('thresholdSlider');
315
+ const thresholdValue = document.getElementById('thresholdValue');
316
+ const enableThreshold = document.getElementById('enableThreshold');
317
+ const smoothBtn = document.getElementById('smoothBtn');
318
+ const rawBtn = document.getElementById('rawBtn');
319
+ const highestPeak = document.getElementById('highestPeak');
320
+ const avgPeaks = document.getElementById('avgPeaks');
321
+ const thresholdBreaches = document.getElementById('thresholdBreaches');
322
+
323
+ // Audio variables
324
+ let audioContext;
325
+ let analyser;
326
+ let microphone;
327
+ let javascriptNode;
328
+ let isMonitoring = false;
329
+ let sessionStartTime = 0;
330
+ let timerInterval;
331
+
332
+ // Data variables
333
+ let noiseData = [];
334
+ let peakNoise = 0;
335
+ let sumNoise = 0;
336
+ let countNoise = 0;
337
+ let chart;
338
+ let peakChart;
339
+ let chartType = 'line'; // 'line' or 'bar'
340
+ let threshold = 70;
341
+ let thresholdEnabled = true;
342
+ let historicalPeaks = [];
343
+ let thresholdBreachCount = 0;
344
+
345
+ // Initialize Charts
346
+ function initChart() {
347
+ const ctx = document.getElementById('noiseChart').getContext('2d');
348
+
349
+ chart = new Chart(ctx, {
350
+ type: chartType,
351
+ data: {
352
+ labels: Array(60).fill('').map((_, i) => `${i*4}m`).reverse(),
353
+ datasets: [{
354
+ label: 'Noise Level',
355
+ data: Array(60).fill(0),
356
+ borderColor: '#00f0ff',
357
+ backgroundColor: 'rgba(0, 240, 255, 0.2)',
358
+ borderWidth: 2,
359
+ pointRadius: 0,
360
+ tension: 0.1,
361
+ fill: true
362
+ }]
363
+ },
364
+ options: {
365
+ responsive: true,
366
+ maintainAspectRatio: false,
367
+ scales: {
368
+ y: {
369
+ beginAtZero: true,
370
+ max: 100,
371
+ grid: {
372
+ color: 'rgba(255, 255, 255, 0.1)'
373
+ },
374
+ ticks: {
375
+ color: 'rgba(255, 255, 255, 0.7)'
376
+ }
377
+ },
378
+ x: {
379
+ grid: {
380
+ color: 'rgba(255, 255, 255, 0.1)'
381
+ },
382
+ ticks: {
383
+ color: 'rgba(255, 255, 255, 0.7)'
384
+ }
385
+ }
386
+ },
387
+ plugins: {
388
+ legend: {
389
+ display: false
390
+ },
391
+ tooltip: {
392
+ enabled: true,
393
+ mode: 'index',
394
+ intersect: false,
395
+ callbacks: {
396
+ label: function(context) {
397
+ return `Noise: ${context.parsed.y}%`;
398
+ }
399
+ }
400
+ }
401
+ },
402
+ animation: {
403
+ duration: 0
404
+ }
405
+ }
406
+ });
407
+ }
408
+
409
+ function initPeakChart() {
410
+ const ctx = document.getElementById('peakChart').getContext('2d');
411
+
412
+ peakChart = new Chart(ctx, {
413
+ type: 'bar',
414
+ data: {
415
+ labels: Array(12).fill('').map((_, i) => `${i*20}m`).reverse(),
416
+ datasets: [{
417
+ label: 'Peak Values',
418
+ data: Array(12).fill(0),
419
+ backgroundColor: 'rgba(255, 0, 240, 0.6)',
420
+ borderColor: 'rgba(255, 0, 240, 1)',
421
+ borderWidth: 1
422
+ }]
423
+ },
424
+ options: {
425
+ responsive: true,
426
+ maintainAspectRatio: false,
427
+ scales: {
428
+ y: {
429
+ beginAtZero: true,
430
+ max: 100,
431
+ grid: {
432
+ color: 'rgba(255, 255, 255, 0.1)'
433
+ },
434
+ ticks: {
435
+ color: 'rgba(255, 255, 255, 0.7)'
436
+ }
437
+ },
438
+ x: {
439
+ grid: {
440
+ color: 'rgba(255, 255, 255, 0.1)'
441
+ },
442
+ ticks: {
443
+ color: 'rgba(255, 255, 255, 0.7)'
444
+ }
445
+ }
446
+ },
447
+ plugins: {
448
+ legend: {
449
+ display: false
450
+ },
451
+ tooltip: {
452
+ callbacks: {
453
+ label: function(context) {
454
+ return `Peak: ${context.parsed.y}%`;
455
+ }
456
+ }
457
+ }
458
+ }
459
+ }
460
+ });
461
+ }
462
+
463
+ // Initialize Audio Visualizer Bars
464
+ function initVisualizer() {
465
+ audioVisualizer.innerHTML = '';
466
+ for (let i = 0; i < 32; i++) {
467
+ const bar = document.createElement('div');
468
+ bar.className = 'visualizer-bar';
469
+ bar.style.height = '2px';
470
+ audioVisualizer.appendChild(bar);
471
+ }
472
+ }
473
+
474
+ // Update Chart with new data
475
+ function updateChart(value) {
476
+ if (chart) {
477
+ // Shift all data left and add new value
478
+ const data = chart.data.datasets[0].data;
479
+ data.shift();
480
+ data.push(value);
481
+
482
+ // Update chart
483
+ chart.update();
484
+ }
485
+ }
486
+
487
+ // Update Peak Chart with historical data
488
+ function updatePeakChart() {
489
+ if (!peakChart) return;
490
+
491
+ // Get peaks from localStorage
492
+ const storedPeaks = JSON.parse(localStorage.getItem('noiseMonitorPeaks')) || [];
493
+ const recentPeaks = storedPeaks.slice(-12); // Last 12 peaks (4 hours)
494
+
495
+ // Fill with zeros if not enough data
496
+ while (recentPeaks.length < 12) {
497
+ recentPeaks.unshift(0);
498
+ }
499
+
500
+ // Update chart data
501
+ peakChart.data.datasets[0].data = recentPeaks;
502
+ peakChart.update();
503
+
504
+ // Update stats
505
+ if (recentPeaks.length > 0) {
506
+ const maxPeak = Math.max(...recentPeaks.filter(p => p > 0));
507
+ const avgPeak = recentPeaks.filter(p => p > 0).reduce((a, b) => a + b, 0) /
508
+ recentPeaks.filter(p => p > 0).length || 0;
509
+
510
+ highestPeak.textContent = maxPeak > 0 ? maxPeak : '--';
511
+ avgPeaks.textContent = avgPeak > 0 ? Math.round(avgPeak) : '--';
512
+
513
+ // Update threshold breaches
514
+ const breaches = storedPeaks.filter(p => p > threshold).length;
515
+ thresholdBreaches.textContent = breaches;
516
+ }
517
+ }
518
+
519
+ // Store peak value
520
+ function storePeakValue(value) {
521
+ if (value < 10) return; // Don't store very low values
522
+
523
+ // Get existing peaks from localStorage
524
+ const storedPeaks = JSON.parse(localStorage.getItem('noiseMonitorPeaks')) || [];
525
+
526
+ // Add new peak (one per minute)
527
+ const now = new Date();
528
+ const lastPeakTime = storedPeaks.length > 0 ?
529
+ new Date(storedPeaks[storedPeaks.length - 1].timestamp) : null;
530
+
531
+ if (!lastPeakTime || (now - lastPeakTime) > 60000) { // 1 minute
532
+ storedPeaks.push({
533
+ value: value,
534
+ timestamp: now.toISOString()
535
+ });
536
+
537
+ // Keep only last 24 hours of data (1440 minutes)
538
+ if (storedPeaks.length > 240) { // 4 hours
539
+ storedPeaks.shift();
540
+ }
541
+
542
+ localStorage.setItem('noiseMonitorPeaks', JSON.stringify(storedPeaks));
543
+ updatePeakChart();
544
+ }
545
+ }
546
+
547
+ // Update Visualizer
548
+ function updateVisualizer(frequencyData) {
549
+ const bars = audioVisualizer.querySelectorAll('.visualizer-bar');
550
+ bars.forEach((bar, i) => {
551
+ const value = frequencyData[i] / 255;
552
+ const height = value * 100;
553
+ bar.style.height = `${height}px`;
554
+ bar.style.opacity = 0.1 + (value * 0.9);
555
+ });
556
+ }
557
+
558
+ // Get noise description based on level
559
+ function getNoiseDescription(level) {
560
+ if (level < 20) return 'Silent';
561
+ if (level < 40) return 'Very Quiet';
562
+ if (level < 60) return 'Moderate';
563
+ if (level < 80) return 'Loud';
564
+ return 'Very Loud';
565
+ }
566
+
567
+ // Get color based on noise level
568
+ function getNoiseColor(level) {
569
+ if (level < 30) return '#00ff00'; // Green
570
+ if (level < 60) return '#ffff00'; // Yellow
571
+ if (level < 80) return '#ff9900'; // Orange
572
+ return '#ff0000'; // Red
573
+ }
574
+
575
+ // Start monitoring
576
+ async function startMonitoring() {
577
+ try {
578
+ // Request microphone access
579
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
580
+
581
+ // Create audio context
582
+ audioContext = new (window.AudioContext || window.webkitAudioContext)();
583
+ analyser = audioContext.createAnalyser();
584
+ analyser.fftSize = 64;
585
+
586
+ // Create microphone source
587
+ microphone = audioContext.createMediaStreamSource(stream);
588
+ microphone.connect(analyser);
589
+
590
+ // Create script processor for audio processing
591
+ javascriptNode = audioContext.createScriptProcessor(2048, 1, 1);
592
+ analyser.connect(javascriptNode);
593
+ javascriptNode.connect(audioContext.destination);
594
+
595
+ // Set up audio processing
596
+ javascriptNode.onaudioprocess = function() {
597
+ if (!isMonitoring) return;
598
+
599
+ // Get frequency data for visualizer
600
+ const frequencyData = new Uint8Array(analyser.frequencyBinCount);
601
+ analyser.getByteFrequencyData(frequencyData);
602
+
603
+ // Calculate overall volume level
604
+ const array = new Uint8Array(analyser.frequencyBinCount);
605
+ analyser.getByteTimeDomainData(array);
606
+
607
+ let values = 0;
608
+ for (let i = 0; i < array.length; i++) {
609
+ values += Math.abs(array[i] - 128);
610
+ }
611
+
612
+ const average = values / array.length;
613
+ const level = Math.min(Math.round(average * 2), 100); // Scale to 0-100
614
+
615
+ // Update UI
616
+ requestAnimationFrame(() => {
617
+ noiseLevel.textContent = level;
618
+ noiseLevel.style.color = getNoiseColor(level);
619
+ noiseDescription.textContent = getNoiseDescription(level);
620
+ noiseLevelBar.style.width = `${level}%`;
621
+ noiseLevelBar.style.backgroundColor = getNoiseColor(level);
622
+
623
+ // Update visualizer
624
+ updateVisualizer(frequencyData);
625
+
626
+ // Update stats
627
+ if (level > peakNoise) {
628
+ peakNoise = level;
629
+ peakLevel.textContent = peakNoise;
630
+ peakLevel.style.color = getNoiseColor(peakNoise);
631
+ storePeakValue(peakNoise);
632
+ }
633
+
634
+ sumNoise += level;
635
+ countNoise++;
636
+ const avg = Math.round(sumNoise / countNoise);
637
+ averageLevel.textContent = avg;
638
+ averageLevel.style.color = getNoiseColor(avg);
639
+
640
+ // Store data
641
+ noiseData.push({
642
+ time: Date.now(),
643
+ level: level
644
+ });
645
+
646
+ // Keep only last 4 hours of data (approx)
647
+ if (noiseData.length > 240) { // 4 hours at 1 sample per minute
648
+ noiseData.shift();
649
+ }
650
+
651
+ // Update chart every second
652
+ updateChart(level);
653
+
654
+ // Check threshold
655
+ if (thresholdEnabled && level > threshold) {
656
+ noiseLevel.classList.add('animate-pulse');
657
+ setTimeout(() => {
658
+ noiseLevel.classList.remove('animate-pulse');
659
+ }, 1000);
660
+
661
+ // Count threshold breaches
662
+ thresholdBreachCount++;
663
+ thresholdBreaches.textContent = thresholdBreachCount;
664
+ }
665
+ });
666
+ };
667
+
668
+ // Update UI
669
+ isMonitoring = true;
670
+ startBtn.disabled = true;
671
+ stopBtn.disabled = false;
672
+ micStatus.textContent = 'Microphone active';
673
+ micStatusIndicator.className = 'w-4 h-4 rounded-full bg-green-500 pulse';
674
+
675
+ // Start session timer
676
+ sessionStartTime = Date.now();
677
+ updateTimer();
678
+ timerInterval = setInterval(updateTimer, 1000);
679
+
680
+ // Initialize visualizer bars
681
+ initVisualizer();
682
+
683
+ } catch (error) {
684
+ console.error('Error accessing microphone:', error);
685
+ micStatus.textContent = 'Microphone access denied';
686
+ micStatusIndicator.className = 'w-4 h-4 rounded-full bg-red-500';
687
+ }
688
+ }
689
+
690
+ // Stop monitoring
691
+ function stopMonitoring() {
692
+ if (javascriptNode) {
693
+ javascriptNode.disconnect();
694
+ javascriptNode = null;
695
+ }
696
+
697
+ if (microphone) {
698
+ microphone.disconnect();
699
+ microphone = null;
700
+ }
701
+
702
+ if (analyser) {
703
+ analyser.disconnect();
704
+ analyser = null;
705
+ }
706
+
707
+ if (audioContext) {
708
+ audioContext.close();
709
+ audioContext = null;
710
+ }
711
+
712
+ // Update UI
713
+ isMonitoring = false;
714
+ startBtn.disabled = false;
715
+ stopBtn.disabled = true;
716
+ micStatus.textContent = 'Microphone inactive';
717
+ micStatusIndicator.className = 'w-4 h-4 rounded-full bg-gray-600';
718
+
719
+ // Stop timer
720
+ clearInterval(timerInterval);
721
+ }
722
+
723
+ // Reset data
724
+ function resetData() {
725
+ noiseData = [];
726
+ peakNoise = 0;
727
+ sumNoise = 0;
728
+ countNoise = 0;
729
+ thresholdBreachCount = 0;
730
+
731
+ peakLevel.textContent = '--';
732
+ averageLevel.textContent = '--';
733
+ thresholdBreaches.textContent = '--';
734
+
735
+ // Reset charts
736
+ if (chart) {
737
+ chart.data.datasets[0].data = Array(60).fill(0);
738
+ chart.update();
739
+ }
740
+
741
+ if (peakChart) {
742
+ peakChart.data.datasets[0].data = Array(12).fill(0);
743
+ peakChart.update();
744
+ }
745
+
746
+ // Clear localStorage peaks
747
+ localStorage.removeItem('noiseMonitorPeaks');
748
+
749
+ // Reset stats
750
+ highestPeak.textContent = '--';
751
+ avgPeaks.textContent = '--';
752
+
753
+ // Reset timer if not monitoring
754
+ if (!isMonitoring) {
755
+ sessionTimer.textContent = '00:00:00';
756
+ }
757
+ }
758
+
759
+ // Update session timer
760
+ function updateTimer() {
761
+ const elapsed = Date.now() - sessionStartTime;
762
+ const seconds = Math.floor(elapsed / 1000) % 60;
763
+ const minutes = Math.floor(elapsed / (1000 * 60)) % 60;
764
+ const hours = Math.floor(elapsed / (1000 * 60 * 60));
765
+
766
+ sessionTimer.textContent =
767
+ `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
768
+ }
769
+
770
+ // Event Listeners
771
+ startBtn.addEventListener('click', startMonitoring);
772
+ stopBtn.addEventListener('click', stopMonitoring);
773
+ resetBtn.addEventListener('click', resetData);
774
+
775
+ thresholdSlider.addEventListener('input', function() {
776
+ threshold = parseInt(this.value);
777
+ thresholdValue.textContent = threshold;
778
+ });
779
+
780
+ enableThreshold.addEventListener('change', function() {
781
+ thresholdEnabled = this.checked;
782
+ });
783
+
784
+ smoothBtn.addEventListener('click', function() {
785
+ chartType = 'line';
786
+ if (chart) {
787
+ chart.config.type = 'line';
788
+ chart.data.datasets[0].tension = 0.1;
789
+ chart.update();
790
+ }
791
+ });
792
+
793
+ rawBtn.addEventListener('click', function() {
794
+ chartType = 'bar';
795
+ if (chart) {
796
+ chart.config.type = 'bar';
797
+ chart.update();
798
+ }
799
+ });
800
+
801
+ // Initialize
802
+ document.addEventListener('DOMContentLoaded', function() {
803
+ initChart();
804
+ initPeakChart();
805
+ initVisualizer();
806
+
807
+ // Set threshold value
808
+ thresholdValue.textContent = thresholdSlider.value;
809
+
810
+ // Load historical peaks
811
+ updatePeakChart();
812
+ });
813
+ </script>
814
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=markburn/neon-noice-monitor" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
815
+ </html>