face / index.html
koyelog's picture
Create index.html
2c6864e verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🎭 AI Emotion Detector | koyelog</title>
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- Font Awesome for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Poppins', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
color: #333;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
/* Header */
.header {
text-align: center;
color: white;
padding: 40px 20px;
margin-bottom: 40px;
animation: fadeInDown 0.8s ease;
}
.header h1 {
font-size: 3.5rem;
font-weight: 700;
margin-bottom: 15px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
}
.header p {
font-size: 1.2rem;
opacity: 0.95;
}
.badge {
display: inline-block;
background: rgba(255,255,255,0.2);
padding: 8px 20px;
border-radius: 20px;
margin: 10px 5px;
font-size: 0.9rem;
backdrop-filter: blur(10px);
}
/* Main Card */
.main-card {
background: white;
border-radius: 25px;
box-shadow: 0 20px 60px rgba(0,0,0,0.2);
overflow: hidden;
animation: fadeInUp 0.8s ease;
}
/* Tabs */
.tabs {
display: flex;
background: #f8f9fa;
border-bottom: 2px solid #e9ecef;
}
.tab-button {
flex: 1;
padding: 20px;
border: none;
background: transparent;
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
color: #666;
}
.tab-button:hover {
background: rgba(102, 126, 234, 0.1);
}
.tab-button.active {
background: white;
color: #667eea;
border-bottom: 3px solid #667eea;
}
.tab-button i {
margin-right: 10px;
}
/* Tab Content */
.tab-content {
display: none;
padding: 40px;
animation: fadeIn 0.5s ease;
}
.tab-content.active {
display: block;
}
/* Upload Section */
.upload-section {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
margin-bottom: 30px;
}
.upload-area {
border: 3px dashed #667eea;
border-radius: 20px;
padding: 40px;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
background: #f8f9fa;
}
.upload-area:hover {
background: #e9ecef;
border-color: #764ba2;
transform: translateY(-5px);
}
.upload-area i {
font-size: 4rem;
color: #667eea;
margin-bottom: 20px;
}
.upload-area input[type="file"] {
display: none;
}
/* Webcam */
#webcam-video, #preview-image {
width: 100%;
border-radius: 15px;
box-shadow: 0 4px 16px rgba(0,0,0,0.1);
}
.button {
display: inline-block;
padding: 15px 40px;
margin: 10px 5px;
border: none;
border-radius: 50px;
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
.button-primary {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
}
.button-primary:hover {
transform: translateY(-3px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}
.button-secondary {
background: #6c757d;
color: white;
}
.button-secondary:hover {
background: #5a6268;
}
/* Result Section */
.result-section {
background: linear-gradient(135deg, #f8f9fa, #e9ecef);
border-radius: 20px;
padding: 30px;
margin-top: 30px;
display: none;
animation: fadeIn 0.5s ease;
}
.result-section.show {
display: block;
}
.emotion-result {
text-align: center;
padding: 40px;
background: white;
border-radius: 20px;
margin-bottom: 30px;
box-shadow: 0 4px 16px rgba(0,0,0,0.1);
}
.emotion-emoji {
font-size: 120px;
animation: bounce 1s ease infinite;
}
.emotion-name {
font-size: 3rem;
font-weight: 700;
margin: 20px 0;
}
.confidence-bar {
width: 100%;
height: 40px;
background: #e9ecef;
border-radius: 20px;
overflow: hidden;
margin: 20px 0;
box-shadow: inset 0 2px 4px rgba(0,0,0,0.1);
}
.confidence-fill {
height: 100%;
background: linear-gradient(90deg, #667eea, #764ba2);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: 600;
transition: width 1s ease;
}
/* All Emotions List */
.emotions-list {
background: white;
padding: 25px;
border-radius: 15px;
box-shadow: 0 4px 16px rgba(0,0,0,0.1);
}
.emotion-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 15px;
margin: 10px 0;
background: #f8f9fa;
border-radius: 10px;
transition: all 0.3s ease;
}
.emotion-item:hover {
background: #e9ecef;
transform: translateX(5px);
}
.emotion-label {
display: flex;
align-items: center;
gap: 15px;
font-weight: 500;
}
.emotion-label span {
font-size: 2rem;
}
.emotion-bar {
width: 200px;
height: 10px;
background: #e9ecef;
border-radius: 5px;
overflow: hidden;
}
.emotion-bar-fill {
height: 100%;
transition: width 0.5s ease;
}
/* Animations */
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-20px); }
}
/* Loading Spinner */
.loading {
display: none;
text-align: center;
padding: 40px;
}
.loading.show {
display: block;
}
.spinner {
border: 5px solid #f3f3f3;
border-top: 5px solid #667eea;
border-radius: 50%;
width: 60px;
height: 60px;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Footer */
.footer {
text-align: center;
color: white;
padding: 40px 20px;
margin-top: 40px;
background: rgba(0,0,0,0.2);
border-radius: 20px;
backdrop-filter: blur(10px);
}
.footer a {
color: white;
text-decoration: underline;
}
/* Responsive */
@media (max-width: 768px) {
.header h1 {
font-size: 2.5rem;
}
.upload-section {
grid-template-columns: 1fr;
}
.tabs {
flex-direction: column;
}
.emotion-name {
font-size: 2rem;
}
.emotion-emoji {
font-size: 80px;
}
}
</style>
</head>
<body>
<div class="container">
<!-- Header -->
<div class="header">
<h1>🎭 AI Emotion Detector</h1>
<p>Powered by Vision Transformer | 98.80% Accuracy</p>
<div>
<span class="badge">Model: koyelog/face</span>
<span class="badge">7 Emotions</span>
<span class="badge">Real-time Detection</span>
</div>
</div>
<!-- Main Card -->
<div class="main-card">
<!-- Tabs -->
<div class="tabs">
<button class="tab-button active" onclick="switchTab('webcam')">
<i class="fas fa-video"></i> Live Webcam
</button>
<button class="tab-button" onclick="switchTab('upload')">
<i class="fas fa-upload"></i> Upload Image
</button>
</div>
<!-- Webcam Tab -->
<div id="webcam-tab" class="tab-content active">
<h2 style="text-align: center; margin-bottom: 30px;">
<i class="fas fa-camera"></i> Capture Your Emotion
</h2>
<div class="upload-section">
<div>
<video id="webcam-video" autoplay playsinline></video>
<canvas id="webcam-canvas" style="display: none;"></canvas>
<div style="text-align: center; margin-top: 20px;">
<button class="button button-primary" onclick="startWebcam()">
<i class="fas fa-play"></i> Start Webcam
</button>
<button class="button button-secondary" onclick="captureAndDetect()">
<i class="fas fa-camera"></i> Capture & Detect
</button>
</div>
</div>
<div id="webcam-result"></div>
</div>
</div>
<!-- Upload Tab -->
<div id="upload-tab" class="tab-content">
<h2 style="text-align: center; margin-bottom: 30px;">
<i class="fas fa-cloud-upload-alt"></i> Upload Face Image
</h2>
<div class="upload-section">
<div>
<div class="upload-area" onclick="document.getElementById('file-input').click()">
<i class="fas fa-image"></i>
<h3>Click to Upload</h3>
<p>or drag & drop your image here</p>
<p style="color: #999; margin-top: 10px;">Supports: JPG, PNG, JPEG</p>
<input type="file" id="file-input" accept="image/*" onchange="handleFileUpload(event)">
</div>
<img id="preview-image" style="display: none; margin-top: 20px;">
</div>
<div id="upload-result"></div>
</div>
</div>
<!-- Loading -->
<div id="loading" class="loading">
<div class="spinner"></div>
<p>Analyzing emotion...</p>
</div>
</div>
<!-- Footer -->
<div class="footer">
<h3>📊 Model Information</h3>
<p><strong>Model:</strong> koyelog/face | <strong>Architecture:</strong> Vision Transformer (ViT)</p>
<p><strong>Parameters:</strong> 85.8M | <strong>Training Accuracy:</strong> 99.29% | <strong>Validation Accuracy:</strong> 98.80%</p>
<p><strong>Emotions:</strong> 😠 Angry | 🤢 Disgust | 😨 Fear | 😊 Happy | 😢 Sad | 😲 Surprise | 😐 Neutral</p>
<p style="margin-top: 20px;">
Created by <strong>Koyeliya Ghosh</strong> |
<a href="https://huggingface.co/koyelog/face" target="_blank">View Model on HuggingFace</a>
</p>
</div>
</div>
<script>
// Configuration
const API_URL = 'YOUR_HUGGINGFACE_SPACE_URL/api/predict'; // Update this!
const EMOTIONS = {
0: { name: 'Angry', emoji: '😠', color: '#ff4444' },
1: { name: 'Disgust', emoji: '🤢', color: '#44ff44' },
2: { name: 'Fear', emoji: '😨', color: '#9944ff' },
3: { name: 'Happy', emoji: '😊', color: '#ffdd44' },
4: { name: 'Sad', emoji: '😢', color: '#4444ff' },
5: { name: 'Surprise', emoji: '😲', color: '#ff44ff' },
6: { name: 'Neutral', emoji: '😐', color: '#888888' }
};
let webcamStream = null;
// Tab switching
function switchTab(tab) {
document.querySelectorAll('.tab-button').forEach(btn => btn.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
if (tab === 'webcam') {
document.querySelector('.tab-button:first-child').classList.add('active');
document.getElementById('webcam-tab').classList.add('active');
} else {
document.querySelector('.tab-button:last-child').classList.add('active');
document.getElementById('upload-tab').classList.add('active');
}
}
// Start webcam
async function startWebcam() {
try {
webcamStream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: 'user' }
});
document.getElementById('webcam-video').srcObject = webcamStream;
} catch (error) {
alert('Error accessing webcam: ' + error.message);
}
}
// Capture and detect from webcam
function captureAndDetect() {
const video = document.getElementById('webcam-video');
const canvas = document.getElementById('webcam-canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0);
canvas.toBlob(blob => {
detectEmotion(blob, 'webcam-result');
}, 'image/jpeg');
}
// Handle file upload
function handleFileUpload(event) {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (e) => {
const preview = document.getElementById('preview-image');
preview.src = e.target.result;
preview.style.display = 'block';
detectEmotion(file, 'upload-result');
};
reader.readAsDataURL(file);
}
// Detect emotion (mock function - replace with API call)
async function detectEmotion(imageBlob, resultId) {
document.getElementById('loading').classList.add('show');
// Simulate API call (replace with actual fetch to your backend)
setTimeout(() => {
const mockResult = {
predicted_id: 3, // Happy
confidence: 0.87,
probabilities: [0.02, 0.01, 0.03, 0.87, 0.04, 0.02, 0.01]
};
displayResult(mockResult, resultId);
document.getElementById('loading').classList.remove('show');
}, 2000);
/*
// Uncomment this for real API call:
const formData = new FormData();
formData.append('file', imageBlob);
try {
const response = await fetch(API_URL, {
method: 'POST',
body: formData
});
const result = await response.json();
displayResult(result, resultId);
} catch (error) {
alert('Error: ' + error.message);
} finally {
document.getElementById('loading').classList.remove('show');
}
*/
}
// Display result
function displayResult(result, containerId) {
const emotion = EMOTIONS[result.predicted_id];
const confidence = (result.confidence * 100).toFixed(2);
let html = `
<div class="emotion-result" style="background: linear-gradient(135deg, ${emotion.color}15, ${emotion.color}30);">
<div class="emotion-emoji">${emotion.emoji}</div>
<div class="emotion-name" style="color: ${emotion.color};">${emotion.name}</div>
<p style="font-size: 1.5rem; color: #666;">Confidence: ${confidence}%</p>
<div class="confidence-bar">
<div class="confidence-fill" style="width: ${confidence}%; background: ${emotion.color};">
${confidence}%
</div>
</div>
</div>
<div class="emotions-list">
<h3 style="margin-bottom: 20px;">📊 All Emotions</h3>
`;
result.probabilities.forEach((prob, idx) => {
const emo = EMOTIONS[idx];
const percentage = (prob * 100).toFixed(1);
html += `
<div class="emotion-item">
<div class="emotion-label">
<span>${emo.emoji}</span>
<strong>${emo.name}</strong>
</div>
<div style="display: flex; align-items: center; gap: 15px;">
<div class="emotion-bar">
<div class="emotion-bar-fill" style="width: ${percentage}%; background: ${emo.color};"></div>
</div>
<span style="font-weight: 600; min-width: 50px;">${percentage}%</span>
</div>
</div>
`;
});
html += '</div>';
document.getElementById(containerId).innerHTML = html;
}
// Drag and drop
const uploadArea = document.querySelector('.upload-area');
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.style.background = '#e9ecef';
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.style.background = '#f8f9fa';
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.style.background = '#f8f9fa';
const file = e.dataTransfer.files[0];
if (file && file.type.startsWith('image/')) {
const event = { target: { files: [file] } };
handleFileUpload(event);
}
});
</script>
</body>
</html>