ameenmarashi commited on
Commit
348ac8c
·
verified ·
1 Parent(s): 7ced021

Create index.html

Browse files
Files changed (1) hide show
  1. index.html +645 -0
index.html ADDED
@@ -0,0 +1,645 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Voice Lie Detector</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
+ min-height: 100vh;
18
+ display: flex;
19
+ justify-content: center;
20
+ align-items: center;
21
+ padding: 20px;
22
+ }
23
+
24
+ .container {
25
+ background: white;
26
+ border-radius: 20px;
27
+ padding: 40px;
28
+ max-width: 500px;
29
+ width: 100%;
30
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
31
+ }
32
+
33
+ h1 {
34
+ text-align: center;
35
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
36
+ -webkit-background-clip: text;
37
+ -webkit-text-fill-color: transparent;
38
+ background-clip: text;
39
+ margin-bottom: 30px;
40
+ font-size: 32px;
41
+ }
42
+
43
+ .info-box {
44
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
45
+ color: white;
46
+ padding: 15px;
47
+ border-radius: 10px;
48
+ margin-bottom: 30px;
49
+ font-size: 14px;
50
+ text-align: center;
51
+ }
52
+
53
+ .control-section {
54
+ margin-bottom: 30px;
55
+ text-align: center;
56
+ }
57
+
58
+ button {
59
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
60
+ color: white;
61
+ border: none;
62
+ padding: 15px 40px;
63
+ border-radius: 50px;
64
+ font-size: 16px;
65
+ font-weight: bold;
66
+ cursor: pointer;
67
+ transition: all 0.3s ease;
68
+ margin: 10px;
69
+ box-shadow: 0 10px 25px rgba(102, 126, 234, 0.4);
70
+ }
71
+
72
+ button:hover {
73
+ transform: translateY(-2px);
74
+ box-shadow: 0 15px 35px rgba(102, 126, 234, 0.6);
75
+ }
76
+
77
+ button:active {
78
+ transform: translateY(0);
79
+ }
80
+
81
+ button:disabled {
82
+ opacity: 0.5;
83
+ cursor: not-allowed;
84
+ }
85
+
86
+ .stop-btn {
87
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
88
+ box-shadow: 0 10px 25px rgba(245, 87, 108, 0.4);
89
+ }
90
+
91
+ .stop-btn:hover {
92
+ box-shadow: 0 15px 35px rgba(245, 87, 108, 0.6);
93
+ }
94
+
95
+ .sensitivity-control {
96
+ margin-bottom: 20px;
97
+ padding: 15px;
98
+ background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%);
99
+ border-radius: 10px;
100
+ }
101
+
102
+ .sensitivity-label {
103
+ font-size: 14px;
104
+ font-weight: bold;
105
+ color: #333;
106
+ margin-bottom: 8px;
107
+ }
108
+
109
+ .sensitivity-slider {
110
+ width: 100%;
111
+ height: 6px;
112
+ border-radius: 3px;
113
+ background: #ddd;
114
+ outline: none;
115
+ -webkit-appearance: none;
116
+ }
117
+
118
+ .sensitivity-slider::-webkit-slider-thumb {
119
+ -webkit-appearance: none;
120
+ appearance: none;
121
+ width: 20px;
122
+ height: 20px;
123
+ border-radius: 50%;
124
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
125
+ cursor: pointer;
126
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
127
+ }
128
+
129
+ .sensitivity-slider::-moz-range-thumb {
130
+ width: 20px;
131
+ height: 20px;
132
+ border-radius: 50%;
133
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
134
+ cursor: pointer;
135
+ border: none;
136
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
137
+ }
138
+
139
+ .sensitivity-value {
140
+ text-align: center;
141
+ font-size: 12px;
142
+ color: #666;
143
+ margin-top: 5px;
144
+ }
145
+
146
+ .visualizer {
147
+ display: flex;
148
+ align-items: flex-end;
149
+ justify-content: center;
150
+ gap: 4px;
151
+ height: 150px;
152
+ margin: 30px 0;
153
+ padding: 20px;
154
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
155
+ border-radius: 15px;
156
+ min-height: 200px;
157
+ }
158
+
159
+ .bar {
160
+ width: 8px;
161
+ background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
162
+ border-radius: 4px;
163
+ transition: height 0.05s ease;
164
+ min-height: 5px;
165
+ }
166
+
167
+ .stats {
168
+ display: grid;
169
+ grid-template-columns: 1fr 1fr;
170
+ gap: 15px;
171
+ margin: 30px 0;
172
+ }
173
+
174
+ .stat-card {
175
+ background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
176
+ padding: 20px;
177
+ border-radius: 10px;
178
+ text-align: center;
179
+ color: white;
180
+ font-weight: bold;
181
+ }
182
+
183
+ .stat-card.pitch {
184
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
185
+ }
186
+
187
+ .stat-card.intensity {
188
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
189
+ }
190
+
191
+ .stat-card.stability {
192
+ background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
193
+ }
194
+
195
+ .stat-card.verdict {
196
+ background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
197
+ grid-column: 1 / -1;
198
+ font-size: 18px;
199
+ }
200
+
201
+ .stat-label {
202
+ font-size: 12px;
203
+ opacity: 0.9;
204
+ margin-bottom: 5px;
205
+ }
206
+
207
+ .stat-value {
208
+ font-size: 24px;
209
+ }
210
+
211
+ .verdict-text {
212
+ font-size: 28px;
213
+ }
214
+
215
+ .confidence {
216
+ font-size: 14px;
217
+ margin-top: 10px;
218
+ opacity: 0.9;
219
+ }
220
+
221
+ .meter {
222
+ width: 100%;
223
+ height: 10px;
224
+ background: rgba(255, 255, 255, 0.3);
225
+ border-radius: 5px;
226
+ margin-top: 10px;
227
+ overflow: hidden;
228
+ }
229
+
230
+ .meter-fill {
231
+ height: 100%;
232
+ background: linear-gradient(90deg, #43e97b 0%, #38f9d7 100%);
233
+ width: 0%;
234
+ transition: width 0.3s ease;
235
+ }
236
+
237
+ .status-message {
238
+ text-align: center;
239
+ margin-top: 20px;
240
+ padding: 15px;
241
+ border-radius: 10px;
242
+ background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%);
243
+ color: #333;
244
+ font-weight: bold;
245
+ min-height: 40px;
246
+ display: flex;
247
+ align-items: center;
248
+ justify-content: center;
249
+ }
250
+
251
+ .status-message.recording {
252
+ background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
253
+ color: white;
254
+ animation: pulse 1s infinite;
255
+ }
256
+
257
+ @keyframes pulse {
258
+ 0%, 100% { opacity: 1; }
259
+ 50% { opacity: 0.7; }
260
+ }
261
+
262
+ .recording-indicator {
263
+ display: inline-block;
264
+ width: 10px;
265
+ height: 10px;
266
+ background: white;
267
+ border-radius: 50%;
268
+ margin-right: 10px;
269
+ animation: blink 1s infinite;
270
+ }
271
+
272
+ @keyframes blink {
273
+ 0%, 100% { opacity: 1; }
274
+ 50% { opacity: 0.3; }
275
+ }
276
+
277
+ @media (max-width: 600px) {
278
+ .container {
279
+ padding: 20px;
280
+ }
281
+
282
+ h1 {
283
+ font-size: 24px;
284
+ }
285
+
286
+ button {
287
+ padding: 12px 30px;
288
+ font-size: 14px;
289
+ }
290
+
291
+ .visualizer {
292
+ min-height: 150px;
293
+ }
294
+ }
295
+ </style>
296
+ </head>
297
+ <body>
298
+ <div class="container">
299
+ <h1>🎤 Voice Lie Detector</h1>
300
+
301
+ <div class="info-box">
302
+ ⚡ Real-time voice analysis - Analyzes tone, pitch stability & stress
303
+ </div>
304
+
305
+ <div class="sensitivity-control">
306
+ <div class="sensitivity-label">Sensitivity: <span id="sensitivityText">5</span></div>
307
+ <input type="range" id="sensitivity" class="sensitivity-slider" min="1" max="10" value="5">
308
+ <div class="sensitivity-value">Low ← → High</div>
309
+ </div>
310
+
311
+ <div class="control-section">
312
+ <button id="startBtn">Start Recording</button>
313
+ <button id="stopBtn" class="stop-btn" disabled>Stop Recording</button>
314
+ </div>
315
+
316
+ <div class="visualizer" id="visualizer">
317
+ <!-- Bars will be added here -->
318
+ </div>
319
+
320
+ <div class="stats">
321
+ <div class="stat-card pitch">
322
+ <div class="stat-label">Pitch Frequency</div>
323
+ <div class="stat-value" id="pitchValue">0 Hz</div>
324
+ </div>
325
+ <div class="stat-card intensity">
326
+ <div class="stat-label">Voice Intensity</div>
327
+ <div class="stat-value" id="intensityValue">0 dB</div>
328
+ </div>
329
+ <div class="stat-card stability">
330
+ <div class="stat-label">Stability Index</div>
331
+ <div class="stat-value" id="stabilityValue">100%</div>
332
+ </div>
333
+ <div class="stat-card verdict">
334
+ <div class="stat-label">Verdict</div>
335
+ <div class="verdict-text" id="verdict">Ready</div>
336
+ <div class="meter">
337
+ <div class="meter-fill" id="meter"></div>
338
+ </div>
339
+ <div class="confidence" id="confidence">Awaiting recording...</div>
340
+ </div>
341
+ </div>
342
+
343
+ <div class="status-message" id="status">Tap "Start Recording" to begin</div>
344
+ </div>
345
+
346
+ <script>
347
+ let audioContext;
348
+ let analyser;
349
+ let microphone;
350
+ let isRecording = false;
351
+ let mediaStream;
352
+
353
+ const startBtn = document.getElementById('startBtn');
354
+ const stopBtn = document.getElementById('stopBtn');
355
+ const visualizer = document.getElementById('visualizer');
356
+ const pitchValue = document.getElementById('pitchValue');
357
+ const intensityValue = document.getElementById('intensityValue');
358
+ const stabilityValue = document.getElementById('stabilityValue');
359
+ const verdict = document.getElementById('verdict');
360
+ const confidence = document.getElementById('confidence');
361
+ const meter = document.getElementById('meter');
362
+ const status = document.getElementById('status');
363
+ const sensitivitySlider = document.getElementById('sensitivity');
364
+ const sensitivityText = document.getElementById('sensitivityText');
365
+
366
+ let pitchHistory = [];
367
+ let intensityHistory = [];
368
+ let baselinePitch = null;
369
+ let baselineIntensity = null;
370
+ let frameCount = 0;
371
+
372
+ startBtn.addEventListener('click', startRecording);
373
+ stopBtn.addEventListener('click', stopRecording);
374
+ sensitivitySlider.addEventListener('change', (e) => {
375
+ sensitivityText.textContent = e.target.value;
376
+ });
377
+
378
+ async function startRecording() {
379
+ try {
380
+ mediaStream = await navigator.mediaDevices.getUserMedia({
381
+ audio: {
382
+ echoCancellation: false,
383
+ noiseSuppression: false,
384
+ autoGainControl: false
385
+ }
386
+ });
387
+
388
+ audioContext = new (window.AudioContext || window.webkitAudioContext)();
389
+ analyser = audioContext.createAnalyser();
390
+ analyser.fftSize = 4096;
391
+ analyser.smoothingTimeConstant = 0.8;
392
+
393
+ microphone = audioContext.createMediaStreamSource(mediaStream);
394
+ microphone.connect(analyser);
395
+
396
+ isRecording = true;
397
+ startBtn.disabled = true;
398
+ stopBtn.disabled = false;
399
+ status.textContent = '🔴 Recording... Speak something!';
400
+ status.classList.add('recording');
401
+ status.innerHTML = '<span class="recording-indicator"></span>Recording... Speak something!';
402
+
403
+ pitchHistory = [];
404
+ intensityHistory = [];
405
+ baselinePitch = null;
406
+ baselineIntensity = null;
407
+ frameCount = 0;
408
+
409
+ analyzeAudio();
410
+ } catch (error) {
411
+ status.textContent = '❌ Microphone access denied. Please allow access.';
412
+ status.classList.remove('recording');
413
+ console.error('Microphone error:', error);
414
+ }
415
+ }
416
+
417
+ function stopRecording() {
418
+ isRecording = false;
419
+
420
+ if (mediaStream) {
421
+ mediaStream.getTracks().forEach(track => track.stop());
422
+ }
423
+
424
+ if (microphone) {
425
+ microphone.disconnect();
426
+ }
427
+
428
+ if (audioContext) {
429
+ audioContext.close();
430
+ }
431
+
432
+ startBtn.disabled = false;
433
+ stopBtn.disabled = true;
434
+
435
+ if (pitchHistory.length > 0) {
436
+ analyzeResults();
437
+ } else {
438
+ verdict.textContent = '⚠️ No Data';
439
+ confidence.textContent = 'Speak more clearly or louder';
440
+ }
441
+
442
+ status.textContent = '✅ Recording stopped. Analysis complete!';
443
+ status.classList.remove('recording');
444
+ }
445
+
446
+ function analyzeAudio() {
447
+ const dataArray = new Uint8Array(analyser.frequencyBinCount);
448
+ analyser.getByteFrequencyData(dataArray);
449
+
450
+ const timeDomainData = new Uint8Array(analyser.fftSize);
451
+ analyser.getByteTimeDomainData(timeDomainData);
452
+
453
+ // Check if there's actual audio
454
+ const isSilent = checkIfSilent(timeDomainData);
455
+
456
+ if (!isSilent) {
457
+ // Calculate intensity
458
+ const intensity = calculateIntensity(timeDomainData);
459
+ intensityHistory.push(intensity);
460
+
461
+ // Detect pitch with improved algorithm
462
+ const pitch = detectPitchFast(timeDomainData);
463
+ if (pitch > 60 && pitch < 400) {
464
+ pitchHistory.push(pitch);
465
+ }
466
+
467
+ // Update live display
468
+ updateVisualizer(dataArray);
469
+ updateLiveStats();
470
+
471
+ frameCount++;
472
+ }
473
+
474
+ if (isRecording) {
475
+ requestAnimationFrame(analyzeAudio);
476
+ }
477
+ }
478
+
479
+ function checkIfSilent(timeDomainData) {
480
+ let maxValue = 0;
481
+ for (let i = 0; i < timeDomainData.length; i++) {
482
+ const normalized = Math.abs((timeDomainData[i] - 128) / 128);
483
+ if (normalized > maxValue) maxValue = normalized;
484
+ }
485
+ return maxValue < 0.02; // Threshold for silence
486
+ }
487
+
488
+ function calculateIntensity(timeDomainData) {
489
+ let sum = 0;
490
+ for (let i = 0; i < timeDomainData.length; i++) {
491
+ const normalized = (timeDomainData[i] - 128) / 128;
492
+ sum += normalized * normalized;
493
+ }
494
+ const rms = Math.sqrt(sum / timeDomainData.length);
495
+ return 20 * Math.log10(Math.max(rms, 0.001));
496
+ }
497
+
498
+ function detectPitchFast(timeDomainData) {
499
+ // Improved autocorrelation for better pitch detection
500
+ const sampleRate = 44100;
501
+ const minPeriod = sampleRate / 400; // Min freq 400Hz
502
+ const maxPeriod = sampleRate / 60; // Max freq 60Hz
503
+
504
+ let maxValue = -Infinity;
505
+ let maxIndex = 0;
506
+
507
+ for (let lag = Math.floor(minPeriod); lag < Math.floor(maxPeriod); lag++) {
508
+ let sum = 0;
509
+ let sumSquareA = 0;
510
+ let sumSquareB = 0;
511
+
512
+ for (let i = 0; i < timeDomainData.length - lag; i++) {
513
+ const sample1 = (timeDomainData[i] - 128) / 256;
514
+ const sample2 = (timeDomainData[i + lag] - 128) / 256;
515
+
516
+ sum += sample1 * sample2;
517
+ sumSquareA += sample1 * sample1;
518
+ sumSquareB += sample2 * sample2;
519
+ }
520
+
521
+ const correlation = sum / Math.sqrt((sumSquareA * sumSquareB) + 0.001);
522
+
523
+ if (correlation > maxValue) {
524
+ maxValue = correlation;
525
+ maxIndex = lag;
526
+ }
527
+ }
528
+
529
+ if (maxIndex > 0 && maxValue > 0.7) {
530
+ return sampleRate / maxIndex;
531
+ }
532
+ return 0;
533
+ }
534
+
535
+ function updateVisualizer(dataArray) {
536
+ visualizer.innerHTML = '';
537
+ const barCount = 32;
538
+ const step = Math.floor(dataArray.length / barCount);
539
+
540
+ for (let i = 0; i < barCount; i++) {
541
+ const value = dataArray[i * step];
542
+ const bar = document.createElement('div');
543
+ bar.className = 'bar';
544
+ bar.style.height = (value / 255) * 200 + 'px';
545
+ visualizer.appendChild(bar);
546
+ }
547
+ }
548
+
549
+ function updateLiveStats() {
550
+ if (pitchHistory.length > 0) {
551
+ const avgPitch = pitchHistory.reduce((a, b) => a + b) / pitchHistory.length;
552
+ pitchValue.textContent = Math.round(avgPitch) + ' Hz';
553
+
554
+ if (baselinePitch === null && pitchHistory.length > 5) {
555
+ baselinePitch = avgPitch;
556
+ }
557
+ }
558
+
559
+ if (intensityHistory.length > 0) {
560
+ const avgIntensity = intensityHistory.reduce((a, b) => a + b) / intensityHistory.length;
561
+ intensityValue.textContent = Math.round(avgIntensity * 10) / 10 + ' dB';
562
+
563
+ if (baselineIntensity === null && intensityHistory.length > 5) {
564
+ baselineIntensity = avgIntensity;
565
+ }
566
+ }
567
+
568
+ if (pitchHistory.length > 20) {
569
+ const stability = calculateStability(pitchHistory.slice(-30));
570
+ stabilityValue.textContent = Math.round(stability) + '%';
571
+ }
572
+ }
573
+
574
+ function calculateStability(pitchData) {
575
+ if (pitchData.length < 2) return 100;
576
+
577
+ const mean = pitchData.reduce((a, b) => a + b) / pitchData.length;
578
+ const variance = pitchData.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / pitchData.length;
579
+ const stdDev = Math.sqrt(variance);
580
+
581
+ const stability = Math.max(0, 100 - (stdDev / mean) * 80);
582
+ return Math.min(100, stability);
583
+ }
584
+
585
+ function analyzeResults() {
586
+ if (pitchHistory.length < 10) {
587
+ verdict.textContent = '⚠️ Not Enough Data';
588
+ confidence.textContent = 'Record for longer and speak clearly';
589
+ meter.style.width = '0%';
590
+ return;
591
+ }
592
+
593
+ const avgPitch = pitchHistory.reduce((a, b) => a + b) / pitchHistory.length;
594
+ const avgIntensity = intensityHistory.length > 0
595
+ ? intensityHistory.reduce((a, b) => a + b) / intensityHistory.length
596
+ : -20;
597
+
598
+ const stability = calculateStability(pitchHistory);
599
+ const sensitivity = parseInt(sensitivitySlider.value);
600
+
601
+ let deceptionScore = 0;
602
+
603
+ // Calculate variations
604
+ const pitchVariation = ((Math.max(...pitchHistory) - Math.min(...pitchHistory)) / avgPitch) * 100;
605
+ const intensityVariation = intensityHistory.length > 5
606
+ ? ((Math.max(...intensityHistory) - Math.min(...intensityHistory)) / Math.abs(avgIntensity)) * 100
607
+ : 0;
608
+
609
+ // Apply sensitivity multiplier
610
+ const sensitivityMultiplier = sensitivity / 5;
611
+
612
+ // Score calculation
613
+ if (pitchVariation > 15) {
614
+ deceptionScore += 30 * sensitivityMultiplier; // Pitch instability
615
+ }
616
+ if (intensityVariation > 20) {
617
+ deceptionScore += 25 * sensitivityMultiplier; // Volume changes
618
+ }
619
+ if (stability < 70) {
620
+ deceptionScore += 25 * sensitivityMultiplier; // Low stability
621
+ }
622
+ if (pitchVariation > 30) {
623
+ deceptionScore += 20 * sensitivityMultiplier; // Very high pitch jump
624
+ }
625
+
626
+ deceptionScore = Math.min(100, Math.max(0, deceptionScore));
627
+ meter.style.width = deceptionScore + '%';
628
+
629
+ if (deceptionScore > 70) {
630
+ verdict.textContent = '🔴 LIKELY LIE';
631
+ confidence.textContent = `Confidence: ${Math.round(deceptionScore)}% - High stress & instability detected`;
632
+ } else if (deceptionScore > 50) {
633
+ verdict.textContent = '🟡 UNCERTAIN';
634
+ confidence.textContent = `Confidence: ${Math.round(deceptionScore)}% - Some stress signals detected`;
635
+ } else if (deceptionScore > 30) {
636
+ verdict.textContent = '🟢 LIKELY TRUTH';
637
+ confidence.textContent = `Confidence: ${100 - Math.round(deceptionScore)}% - Voice appears relatively calm`;
638
+ } else {
639
+ verdict.textContent = '✅ VERY TRUTHFUL';
640
+ confidence.textContent = `Confidence: ${100 - Math.round(deceptionScore)}% - No significant stress detected`;
641
+ }
642
+ }
643
+ </script>
644
+ </body>
645
+ </html>