vineetshukla.work@gmail.com
final commit
c5c9261
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VoiceGuard AI - Premium Voice Detection</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
* {
font-family: 'Outfit', sans-serif;
}
/* Glassmorphism */
.glass {
background: rgba(30, 41, 59, 0.7);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
/* Gradient text */
.gradient-text {
background: linear-gradient(135deg, #60a5fa 0%, #a78bfa 50%, #f472b6 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* Pulse animation for recording */
.recording-pulse {
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
0%,
100% {
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7);
transform: scale(1);
}
50% {
box-shadow: 0 0 0 20px rgba(239, 68, 68, 0);
transform: scale(1.05);
}
}
/* Sound wave animation */
.sound-wave {
display: flex;
align-items: center;
gap: 3px;
height: 40px;
}
.sound-wave span {
width: 4px;
background: linear-gradient(180deg, #60a5fa, #a78bfa);
border-radius: 4px;
animation: wave 0.5s ease-in-out infinite;
}
.sound-wave span:nth-child(1) {
animation-delay: 0s;
}
.sound-wave span:nth-child(2) {
animation-delay: 0.1s;
}
.sound-wave span:nth-child(3) {
animation-delay: 0.2s;
}
.sound-wave span:nth-child(4) {
animation-delay: 0.3s;
}
.sound-wave span:nth-child(5) {
animation-delay: 0.4s;
}
@keyframes wave {
0%,
100% {
height: 10px;
}
50% {
height: 35px;
}
}
/* Result card animations */
.result-human {
background: linear-gradient(135deg, rgba(34, 197, 94, 0.2), rgba(16, 185, 129, 0.1));
border-left: 4px solid #22c55e;
}
.result-ai {
background: linear-gradient(135deg, rgba(239, 68, 68, 0.2), rgba(249, 115, 22, 0.1));
border-left: 4px solid #ef4444;
}
/* Floating particles */
.particle {
position: absolute;
width: 6px;
height: 6px;
background: rgba(96, 165, 250, 0.3);
border-radius: 50%;
animation: float 15s infinite;
}
@keyframes float {
0%,
100% {
transform: translateY(0) translateX(0);
opacity: 0;
}
10% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
transform: translateY(-100vh) translateX(50px);
opacity: 0;
}
}
</style>
</head>
<body class="bg-slate-950 text-white min-h-screen overflow-x-hidden">
<!-- Floating particles background -->
<div class="fixed inset-0 overflow-hidden pointer-events-none">
<div class="particle" style="left: 10%; animation-delay: 0s;"></div>
<div class="particle" style="left: 20%; animation-delay: 2s;"></div>
<div class="particle" style="left: 35%; animation-delay: 4s;"></div>
<div class="particle" style="left: 50%; animation-delay: 1s;"></div>
<div class="particle" style="left: 65%; animation-delay: 3s;"></div>
<div class="particle" style="left: 80%; animation-delay: 5s;"></div>
<div class="particle" style="left: 90%; animation-delay: 2.5s;"></div>
</div>
<div class="relative z-10 flex flex-col items-center justify-center min-h-screen p-4">
<!-- Main Card -->
<div class="glass rounded-3xl shadow-2xl max-w-lg w-full overflow-hidden">
<!-- Header -->
<div class="p-8 pb-4 text-center border-b border-slate-700/50">
<div
class="inline-flex items-center gap-2 px-4 py-1 rounded-full bg-blue-500/10 border border-blue-500/30 text-blue-400 text-xs font-medium mb-4">
<span class="w-2 h-2 bg-green-400 rounded-full animate-pulse"></span>
API Online
</div>
<h1 class="text-4xl font-bold gradient-text mb-2">VoiceGuard AI</h1>
<p class="text-slate-400 text-sm">Advanced Deepfake Voice Detection System</p>
</div>
<!-- Main Interaction Area -->
<div class="p-8">
<!-- Mode Tabs -->
<div class="flex justify-center gap-2 mb-8">
<button id="recordTab"
class="flex items-center gap-2 px-5 py-2.5 rounded-xl bg-gradient-to-r from-blue-600 to-indigo-600 text-sm font-semibold transition-all hover:shadow-lg hover:shadow-blue-500/25">
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<path
d="M7 4a3 3 0 016 0v6a3 3 0 11-6 0V4zm4 10.93A7.001 7.001 0 0017 8a1 1 0 10-2 0A5 5 0 015 8a1 1 0 00-2 0 7.001 7.001 0 006 6.93V17H6a1 1 0 100 2h8a1 1 0 100-2h-3v-2.07z" />
</svg>
Record
</button>
<button id="uploadTab"
class="flex items-center gap-2 px-5 py-2.5 rounded-xl bg-slate-800 text-slate-300 text-sm font-semibold transition-all hover:bg-slate-700">
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<path
d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM6.293 6.707a1 1 0 010-1.414l3-3a1 1 0 011.414 0l3 3a1 1 0 01-1.414 1.414L11 5.414V13a1 1 0 11-2 0V5.414L7.707 6.707a1 1 0 01-1.414 0z" />
</svg>
Upload
</button>
</div>
<!-- Record Interface -->
<div id="recordArea" class="flex flex-col items-center gap-6">
<div class="relative">
<button id="recordBtn"
class="relative w-28 h-28 rounded-full bg-gradient-to-br from-slate-800 to-slate-900 border-2 border-slate-700 flex items-center justify-center transition-all hover:border-blue-500 hover:shadow-lg hover:shadow-blue-500/20 group">
<div id="micIcon" class="text-4xl transition-transform group-hover:scale-110">🎤</div>
</button>
<!-- Sound wave (hidden by default) -->
<div id="soundWave" class="hidden absolute -bottom-2 left-1/2 -translate-x-1/2">
<div class="sound-wave">
<span></span><span></span><span></span><span></span><span></span>
</div>
</div>
</div>
<p id="recordStatus" class="text-sm text-slate-400">Tap to start recording</p>
<div id="recordTimer" class="hidden text-2xl font-mono text-blue-400">00:00</div>
</div>
<!-- Upload Interface -->
<div id="uploadArea" class="hidden flex flex-col items-center gap-4">
<label class="w-full group cursor-pointer">
<div
class="h-40 flex flex-col items-center justify-center border-2 border-dashed border-slate-700 rounded-2xl transition-all group-hover:border-blue-500 group-hover:bg-blue-500/5">
<div class="text-4xl mb-3 transition-transform group-hover:scale-110">📁</div>
<span class="text-sm text-slate-400 group-hover:text-blue-400">Drop audio file or click to
browse</span>
<span class="text-xs text-slate-500 mt-1">MP3, WAV, FLAC supported</span>
</div>
<input type="file" id="fileInput" accept="audio/*" class="hidden">
</label>
<p id="fileName" class="text-sm text-slate-400 h-5"></p>
</div>
<!-- Analyze Button -->
<button id="analyzeBtn"
class="w-full mt-8 py-4 rounded-xl bg-gradient-to-r from-blue-600 via-indigo-600 to-purple-600 font-bold text-lg shadow-lg shadow-blue-500/25 disabled:opacity-50 disabled:cursor-not-allowed disabled:shadow-none transition-all transform hover:scale-[1.02] active:scale-[0.98]"
disabled>
<span id="analyzeBtnText">Analyze Voice</span>
</button>
</div>
<!-- Result Area (Hidden by default) -->
<div id="resultArea" class="hidden border-t border-slate-700/50 p-6 transition-all duration-500">
<div id="resultCard" class="rounded-xl p-5 transition-all">
<div class="flex items-center justify-between mb-4">
<div>
<p class="text-xs text-slate-500 uppercase tracking-wider mb-1">Classification</p>
<h3 id="resultLabel" class="text-2xl font-bold">Analyzing...</h3>
</div>
<div id="resultIcon" class="text-5xl">🔍</div>
</div>
<!-- Confidence Meter -->
<div class="mb-4">
<div class="flex justify-between text-xs text-slate-400 mb-1">
<span>Confidence</span>
<span id="confidencePercent">0%</span>
</div>
<div class="w-full bg-slate-800 rounded-full h-2.5 overflow-hidden">
<div id="confidenceBar" class="h-2.5 rounded-full transition-all duration-1000 ease-out"
style="width: 0%"></div>
</div>
</div>
<!-- Explanation -->
<p id="explanationText" class="text-sm text-slate-300 leading-relaxed"></p>
</div>
</div>
</div>
<!-- Footer -->
<div class="mt-8 text-center">
<p class="text-slate-500 text-xs">Powered by Deep Learning • Built for Hackathon</p>
</div>
</div>
<script>
// State
let audioBlob = null;
let mediaRecorder = null;
let audioChunks = [];
let isRecording = false;
let recordingTimer = null;
let recordingSeconds = 0;
// Elements
const recordTab = document.getElementById('recordTab');
const uploadTab = document.getElementById('uploadTab');
const recordArea = document.getElementById('recordArea');
const uploadArea = document.getElementById('uploadArea');
const recordBtn = document.getElementById('recordBtn');
const micIcon = document.getElementById('micIcon');
const soundWave = document.getElementById('soundWave');
const recordStatus = document.getElementById('recordStatus');
const recordTimer = document.getElementById('recordTimer');
const fileInput = document.getElementById('fileInput');
const fileName = document.getElementById('fileName');
const analyzeBtn = document.getElementById('analyzeBtn');
const analyzeBtnText = document.getElementById('analyzeBtnText');
const resultArea = document.getElementById('resultArea');
const resultCard = document.getElementById('resultCard');
const resultLabel = document.getElementById('resultLabel');
const resultIcon = document.getElementById('resultIcon');
const confidencePercent = document.getElementById('confidencePercent');
const confidenceBar = document.getElementById('confidenceBar');
const explanationText = document.getElementById('explanationText');
// Tab switching
recordTab.onclick = () => {
recordTab.className = 'flex items-center gap-2 px-5 py-2.5 rounded-xl bg-gradient-to-r from-blue-600 to-indigo-600 text-sm font-semibold transition-all hover:shadow-lg hover:shadow-blue-500/25';
uploadTab.className = 'flex items-center gap-2 px-5 py-2.5 rounded-xl bg-slate-800 text-slate-300 text-sm font-semibold transition-all hover:bg-slate-700';
recordArea.classList.remove('hidden');
uploadArea.classList.add('hidden');
};
uploadTab.onclick = () => {
uploadTab.className = 'flex items-center gap-2 px-5 py-2.5 rounded-xl bg-gradient-to-r from-blue-600 to-indigo-600 text-sm font-semibold transition-all hover:shadow-lg hover:shadow-blue-500/25';
recordTab.className = 'flex items-center gap-2 px-5 py-2.5 rounded-xl bg-slate-800 text-slate-300 text-sm font-semibold transition-all hover:bg-slate-700';
uploadArea.classList.remove('hidden');
recordArea.classList.add('hidden');
};
// Timer formatting
function formatTime(seconds) {
const mins = Math.floor(seconds / 60).toString().padStart(2, '0');
const secs = (seconds % 60).toString().padStart(2, '0');
return `${mins}:${secs}`;
}
// Recording
recordBtn.onclick = async () => {
if (!isRecording) {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream);
audioChunks = [];
mediaRecorder.ondataavailable = e => audioChunks.push(e.data);
mediaRecorder.onstop = () => {
audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
analyzeBtn.disabled = false;
analyzeBtnText.textContent = "Analyze Recording";
};
mediaRecorder.start();
isRecording = true;
// UI updates
recordBtn.classList.add('recording-pulse', 'border-red-500');
micIcon.textContent = "⏹️";
soundWave.classList.remove('hidden');
recordStatus.textContent = "Recording... tap to stop";
recordTimer.classList.remove('hidden');
// Start timer
recordingSeconds = 0;
recordTimer.textContent = formatTime(recordingSeconds);
recordingTimer = setInterval(() => {
recordingSeconds++;
recordTimer.textContent = formatTime(recordingSeconds);
}, 1000);
} catch (e) {
alert("Microphone access denied. Please allow microphone access.");
}
} else {
mediaRecorder.stop();
mediaRecorder.stream.getTracks().forEach(track => track.stop());
isRecording = false;
// UI updates
recordBtn.classList.remove('recording-pulse', 'border-red-500');
micIcon.textContent = "🎤";
soundWave.classList.add('hidden');
recordStatus.textContent = `Recording saved (${formatTime(recordingSeconds)})`;
clearInterval(recordingTimer);
}
};
// File upload
fileInput.onchange = (e) => {
const file = e.target.files[0];
if (file) {
audioBlob = file;
fileName.textContent = `📎 ${file.name}`;
analyzeBtn.disabled = false;
analyzeBtnText.textContent = "Analyze Upload";
}
};
// Analyze
analyzeBtn.onclick = async () => {
if (!audioBlob) return;
analyzeBtn.disabled = true;
analyzeBtnText.innerHTML = `<svg class="animate-spin h-5 w-5 inline-block mr-2" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>Analyzing...`;
resultArea.classList.add('hidden');
try {
const reader = new FileReader();
reader.onloadend = async () => {
const base64String = reader.result.split(',')[1];
try {
const response = await fetch('/detect', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'uNaRqJimOAQUK4uL-YRN_DjvHwpGiV8igbhJUUVm3NkY'
},
body: JSON.stringify({
language: "English",
audioFormat: "mp3",
audioBase64: base64String
})
});
const data = await response.json();
resultArea.classList.remove('hidden');
if (data.status === 'success') {
const isAI = data.classification === "AI_GENERATED";
resultCard.className = `rounded-xl p-5 transition-all ${isAI ? 'result-ai' : 'result-human'}`;
resultLabel.textContent = isAI ? "AI Generated" : "Human Voice";
resultLabel.className = `text-2xl font-bold ${isAI ? 'text-red-400' : 'text-green-400'}`;
resultIcon.textContent = isAI ? "🤖" : "👤";
const pct = Math.round(data.confidenceScore * 100);
confidencePercent.textContent = `${pct}%`;
confidenceBar.style.width = `${pct}%`;
confidenceBar.className = `h-2.5 rounded-full transition-all duration-1000 ease-out ${isAI ? 'bg-gradient-to-r from-red-500 to-orange-500' : 'bg-gradient-to-r from-green-500 to-emerald-500'}`;
explanationText.textContent = data.explanation;
} else {
resultCard.className = 'rounded-xl p-5 bg-yellow-500/10 border-l-4 border-yellow-500';
resultLabel.textContent = "Error";
resultLabel.className = "text-2xl font-bold text-yellow-400";
resultIcon.textContent = "⚠️";
explanationText.textContent = data.message || "An error occurred";
}
} catch (err) {
alert("Network error: " + err.message);
}
analyzeBtn.disabled = false;
analyzeBtnText.textContent = "Analyze Again";
};
reader.readAsDataURL(audioBlob);
} catch (err) {
console.error(err);
analyzeBtn.disabled = false;
analyzeBtnText.textContent = "Analyze Voice";
}
};
</script>
</body>
</html>