esc50-model / index.html
mateo496's picture
Upload folder using huggingface_hub
5fe51bf verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ESC50 Audio Classifier</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.container {
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
max-width: 800px;
width: 100%;
padding: 40px;
}
h1 {
color: #333;
margin-bottom: 10px;
font-size: 2em;
}
.subtitle {
color: #666;
margin-bottom: 30px;
font-size: 1.1em;
}
.upload-area {
border: 3px dashed #667eea;
border-radius: 15px;
padding: 40px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
background: #f8f9ff;
}
.upload-area:hover {
background: #eef1ff;
border-color: #764ba2;
}
.upload-area.dragover {
background: #e0e7ff;
border-color: #667eea;
transform: scale(1.02);
}
.upload-icon {
font-size: 48px;
margin-bottom: 20px;
}
input[type="file"] {
display: none;
}
.file-name {
margin-top: 20px;
color: #667eea;
font-weight: 500;
}
.button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 15px 40px;
border-radius: 25px;
font-size: 1.1em;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
margin-top: 20px;
width: 100%;
}
.button:hover {
transform: translateY(-2px);
box-shadow: 0 10px 25px rgba(102, 126, 234, 0.4);
}
.button:disabled {
background: #ccc;
cursor: not-allowed;
transform: none;
}
.results {
margin-top: 30px;
padding: 25px;
background: #f8f9ff;
border-radius: 15px;
display: none;
}
.results.show {
display: block;
animation: slideIn 0.4s ease;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.prediction-main {
background: white;
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
border-left: 5px solid #667eea;
}
.prediction-class {
font-size: 1.5em;
color: #333;
font-weight: 700;
margin-bottom: 10px;
}
.confidence {
font-size: 1.2em;
color: #667eea;
font-weight: 600;
}
.top-predictions {
margin-top: 20px;
}
.top-predictions h3 {
color: #333;
margin-bottom: 15px;
font-size: 1.2em;
}
.prediction-item {
background: white;
padding: 12px 15px;
border-radius: 8px;
margin-bottom: 8px;
display: flex;
justify-content: space-between;
align-items: center;
}
.prediction-rank {
background: #667eea;
color: white;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
margin-right: 15px;
}
.prediction-name {
flex: 1;
font-weight: 500;
color: #333;
}
.prediction-conf {
color: #667eea;
font-weight: 600;
}
.loading {
display: none;
text-align: center;
padding: 20px;
}
.loading.show {
display: block;
}
.spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #667eea;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 0 auto 15px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.error {
background: #fee;
color: #c33;
padding: 15px;
border-radius: 8px;
margin-top: 20px;
display: none;
}
.error.show {
display: block;
}
.progress-bar {
width: 100%;
height: 6px;
background: #e0e0e0;
border-radius: 3px;
overflow: hidden;
margin-top: 15px;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #667eea, #764ba2);
width: 0%;
transition: width 0.3s;
}
.classes-info {
margin-top: 20px;
border-radius: 15px;
overflow: hidden;
border: 2px solid #e0e4ff;
}
.classes-toggle {
width: 100%;
background: #f8f9ff;
border: none;
padding: 15px 20px;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
font-size: 1em;
font-weight: 600;
color: #667eea;
transition: background 0.2s;
}
.classes-toggle:hover { background: #eef1ff; }
.classes-toggle .arrow {
transition: transform 0.3s;
font-style: normal;
}
.classes-toggle.open .arrow { transform: rotate(180deg); }
.classes-grid {
display: none;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 8px;
padding: 15px;
background: white;
}
.classes-grid.show { display: grid; }
.class-tag {
background: #f8f9ff;
border: 1px solid #e0e4ff;
border-radius: 20px;
padding: 6px 12px;
font-size: 0.82em;
color: #555;
text-align: center;
}
.k-selector {
display: flex;
align-items: center;
justify-content: center;
gap: 15px;
margin-top: 20px;
color: #667eea;
font-weight: 600;
}
.k-selector input[type="range"] {
appearance: none;
height: 6px;
border-radius: 3px;
background: #e0e4ff;
outline: none;
}
.k-selector input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea, #764ba2);
cursor: pointer;
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.4);
}
</style>
</head>
<body>
<div class="container">
<h1>ESC50 Audio Classifier</h1>
<p class="subtitle">Upload an audio file to classify environmental sounds</p>
<div class="upload-area" id="uploadArea">
<h3>Drag & Drop Audio File</h3>
<p>or click to browse</p>
<input type="file" id="fileInput" accept="audio/*">
<div class="file-name" id="fileName"></div>
</div>
<div class="k-selector">
<label for="kInput">Number of predictions: <span id="kValue">5</span> </label>
<input type="range" id="kInput" min="1" max="10" value="5">
</div>
<button class="button" id="classifyBtn" disabled>Classify Audio</button>
<div class="loading" id="loading">
<div class="spinner"></div>
<p>Analyzing audio...</p>
</div>
<div class="error" id="error"></div>
<div class="results" id="results">
<div class="prediction-main">
<div class="prediction-class" id="predictedClass"></div>
<div class="confidence" id="confidence"></div>
</div>
<div class="top-predictions">
<h3></h3>
<div id="topPredictions"></div>
</div>
</div>
<div class="classes-info">
<button class="classes-toggle", id="classesToggle">
<span>ℹ️ 50 Identifiable Sound Classes </span>
<i class="arrow"></i>
</button>
<div class="classes-grid" id="classesGrid"></div>
</div>
</div>
<script>
const uploadArea = document.getElementById('uploadArea');
const fileInput = document.getElementById('fileInput');
const fileName = document.getElementById('fileName');
const classifyBtn = document.getElementById('classifyBtn');
const loading = document.getElementById('loading');
const results = document.getElementById('results');
const error = document.getElementById('error');
let selectedFile = null;
// API endpoint - update this to your server URL
const API_URL = '';
// Click to upload
uploadArea.addEventListener('click', () => fileInput.click());
// File selection
fileInput.addEventListener('change', (e) => {
handleFile(e.target.files[0]);
});
// Drag and drop
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('dragover');
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.classList.remove('dragover');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('dragover');
handleFile(e.dataTransfer.files[0]);
});
function handleFile(file) {
if (!file) return;
// Check if it's an audio file
if (!file.type.startsWith('audio/')) {
showError('Please select an audio file');
return;
}
selectedFile = file;
fileName.textContent = `Selected: ${file.name}`;
classifyBtn.disabled = false;
hideError();
}
const kInput = document.getElementById('kInput');
kInput.addEventListener('input', () => {
document.getElementById('kValue').textContent = kInput.value;
});
// Classify button
classifyBtn.addEventListener('click', async () => {
if (!selectedFile) return;
classifyBtn.disabled = true;
loading.classList.add('show');
results.classList.remove('show');
hideError();
try {
const formData = new FormData();
formData.append('file', selectedFile);
const response = await fetch(`${API_URL}/predict-top-k?k=${kInput.value}`, {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error(`Server error: ${response.status}`);
}
const data = await response.json();
displayResults(data);
} catch (err) {
showError(`Error: ${err.message}. Make sure the server is running at ${API_URL}`);
} finally {
loading.classList.remove('show');
classifyBtn.disabled = false;
}
});
function displayResults(data) {
document.querySelector('.top-predictions h3').textContent = `Top ${kInput.value} Predictions`;
// Main prediction
document.getElementById('predictedClass').textContent =
data.predicted_class.replace(/_/g, ' ').toUpperCase();
document.getElementById('confidence').textContent =
`Confidence: ${(data.confidence * 100).toFixed(1)}% (Raw softmax output, not normalized)`;
// Top predictions
const topPredictionsDiv = document.getElementById('topPredictions');
topPredictionsDiv.innerHTML = data.top_predictions.map((pred, idx) => `
<div class="prediction-item">
<span class="prediction-rank">${idx + 1}</span>
<span class="prediction-name">${pred.class.replace(/_/g, ' ')}</span>
<span class="prediction-conf">${(pred.confidence * 100).toFixed(1)}%</span>
</div>
`).join('');
results.classList.add('show');
}
function showError(message) {
error.textContent = message;
error.classList.add('show');
}
function hideError() {
error.classList.remove('show');
}
// Check server status on load
window.addEventListener('load', async () => {
try {
const response = await fetch(`${API_URL}/`);
if (response.ok) {
console.log('Server is running');
}
} catch (err) {
showError(`Cannot connect to server at ${API_URL}. Make sure it's running.`);
}
});
const classesToggle = document.getElementById('classesToggle');
const classesGrid = document.getElementById('classesGrid');
window.addEventListener('load', async () => {
const res = await fetch(`${API_URL}/labels`);
const { labels } = await res.json();
classesGrid.innerHTML = labels.map(c =>
`<span class="class-tag">${c.replace(/_/g, ' ')}</span>`
).join('');
});
classesToggle.addEventListener('click', () =>{
classesToggle.classList.toggle('open');
classesGrid.classList.toggle('show');
});
</script>
</body>
</html>