-
Notifications
You must be signed in to change notification settings - Fork 12
Frontend
The Muze frontend is built with HTML5, JavaScript, and jQuery, providing webcam capture, audio playback, and user interface functionality. This document details all frontend components and their interactions.
templates/
├── base.html # Base template with common structure
├── musi.html # Main music player interface
├── init.html # Initial webcam test page
├── index.html # Simple landing page
├── trial.html # Development/testing page
├── showgraph.html # Emotion graph display
└── styling.css # CSS styles
static/
├── jquery-1.7.1.min.js # jQuery library
├── music/ # MP3 files (001.mp3 - 903.mp3)
└── graph.jpg # Generated emotion visualization
Purpose: Base template providing common HTML structure
Structure:
<!DOCTYPE html>
<html>
<head>
<title>Muze</title>
<link rel="stylesheet" href="/static/style.css" />
<script src="/static/jquery-1.7.1.min.js"></script>
</head>
<body>
<div id="content">
{% block body %}{% endblock %}
</div>
</body>
</html>Features:
- Jinja2 template inheritance
- jQuery 1.7.1 inclusion
- CSS stylesheet link
- Content block for child templates
Purpose: Main application interface with webcam, audio player, and controls
Template Variables:
songs: List[str] # Playlist from backendKey Sections:
<script>
var songlist = {{ songs|tojson }};
var i = 0;
</script><article>
<img id="im"> <!-- Placeholder image -->
<div class="cont">
<h2 id="ss"></h2> <!-- Song name display -->
</div>
<audio id="aud" controls autoplay>
<source src="/static/music/init.mp3">
</audio>
<button onclick="stopWebcam();">Stop WebCam / See report</button>
<button onclick="play_again();">Get more music</button>
<canvas hidden id="myCanvas" width="400" height="350"></canvas>
</article>
<video id="video" width="200" height="200" controls autoplay></video>navigator.getUserMedia = (
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia ||
navigator.mediaDevices.getUserMedia
);
navigator.getUserMedia(
{ video: true, audio: false },
function(stream) {
video.src = window.URL.createObjectURL(stream);
webcamStream = stream;
},
function(err) {
console.log("Error: " + err);
}
);Browser Compatibility: Supports Chrome, Firefox, Safari, Edge
Purpose: Initialize canvas context on page load
function init() {
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext('2d');
}Called: <body onload="init();">
Purpose: Capture webcam frame and send to server
function snapshot() {
// Draw video frame to canvas
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
// Convert to base64 PNG
var dataURL = canvas.toDataURL("image/png");
// Send to server
$.ajax({
type: "POST",
url: "/hook",
data: { imageBase64: dataURL },
success: function(response) {
document.write(response);
}
}).done(function() {
console.log('sent');
});
}Triggered:
- After 5 seconds on initial load (empty playlist)
- After 20 songs during playback
Data Flow:
- Video frame → Canvas (400×350)
- Canvas → Base64 PNG string
- AJAX POST →
/hookendpoint - Response → Replace page content
Purpose: Stop webcam stream and navigate to emotion report
function stopWebcam() {
webcamStream.stop();
window.location.href = "/graph";
}Triggered: User clicks "Stop WebCam / See report" button
Effect:
- Releases webcam access
- Redirects to graph visualization page
Purpose: Restart the experience with new emotion detection
function play_again() {
window.location.href = "/";
}Triggered: User clicks "Get more music" button
Effect: Reloads main page, starts fresh session
Purpose: Automatically play next song in playlist
$('#aud').on('ended', function() {
// Parse song entry
var str = (songlist[i].split("_"))[0]; // "234.mp3"
var name = (songlist[i].split("_"))[1]; // "Song Name - Artist"
// Update display
document.getElementById('ss').innerHTML = name;
// Increment counter
i = i + 1;
// Check if time for new snapshot
if (i == 20) {
setTimeout(snapshot, 5000);
}
// Load next song
$('#aud').attr('src', '/static/music/' + str);
$('#aud').load();
});Flow:
- Current song ends
- Parse next song from playlist
- Update UI with song name
- Increment playlist index
- After 20 songs, trigger snapshot
- Load and play next song
if (songlist.length == 0) {
setTimeout(snapshot, 5000);
}Logic: If playlist is empty (first load), wait 5 seconds then capture snapshot
Element: <h2 id="ss"></h2>
Purpose: Show currently playing song name
Update:
document.getElementById('ss').innerHTML = "Night in Tunisia - Blakey";Styling: White text color (color: #ffffff)
Element: <audio id="aud" controls autoplay>
Attributes:
-
controls: Show playback controls -
autoplay: Start playing automatically -
id="aud": JavaScript reference
Initial Source: /static/music/init.mp3
Dynamic Source: Updated via jQuery
$('#aud').attr('src', '/static/music/234.mp3');
$('#aud').load();Element: <video id="video" width="200" height="200">
Position: Bottom-right corner (position:absolute;bottom:0;right:0;)
Purpose: Show live webcam feed to user
Source: WebRTC stream
video.src = window.URL.createObjectURL(webcamStream);Element: <canvas hidden id="myCanvas" width="400" height="350">
Purpose: Offscreen buffer for capturing video frames
Visibility: Hidden from user
Usage: Draw video frame, convert to image
<button onclick="stopWebcam();">Stop WebCam / See report</button>Action: Stop camera, view emotion history
<button onclick="play_again();">Get more music</button>Action: Restart with new emotion detection
┌──────────────┐
│ Page Load │
└──────┬───────┘
│
▼
┌──────────────┐
│ init() │
│ - Setup │
│ canvas │
└──────┬───────┘
│
▼
┌──────────────┐
│ getUserMedia │
│ - Request │
│ webcam │
└──────┬───────┘
│
▼
┌──────────────┐
│ Video Stream │
│ - Display in │
│ <video> │
└──────┬───────┘
│
│ (5 sec delay)
▼
┌──────────────┐
│ snapshot() │
│ - Capture │
│ - Upload │
└──────┬───────┘
│
▼
┌──────────────┐
│ Server │
│ Processing │
└──────┬───────┘
│
▼
┌──────────────┐
│ Page Reload │
│ with │
│ Playlist │
└──────┬───────┘
│
▼
┌──────────────┐
│ Audio Play │
│ - Song 1 │
└──────┬───────┘
│
│ (on 'ended')
▼
┌──────────────┐
│ Next Song │
│ - Update UI │
│ - Load audio │
└──────┬───────┘
│
│ (repeat)
▼
┌──────────────┐
│ After 20 │
│ Songs │
└──────┬───────┘
│
▼
┌──────────────┐
│ snapshot() │
│ again │
└──────────────┘
| Browser | getUserMedia | Notes |
|---|---|---|
| Chrome 53+ | ✅ | Full support |
| Firefox 36+ | ✅ | Full support |
| Safari 11+ | ✅ | Requires HTTPS |
| Edge 12+ | ✅ | Full support |
| IE 11 | ❌ | Not supported |
| Browser | MP3 Support | Notes |
|---|---|---|
| Chrome | ✅ | Native |
| Firefox | ✅ | Native |
| Safari | ✅ | Native |
| Edge | ✅ | Native |
| IE 9+ | ✅ | Native |
Support: All modern browsers (IE 9+)
Permission Required: User must grant camera access
HTTPS Requirement: Modern browsers require HTTPS for getUserMedia (except localhost)
Indicator: Browser shows camera active indicator
Current Setup: No CORS headers configured
Issue: May block cross-origin requests
Fix:
from flask_cors import CORS
CORS(app)Current: No CSP headers
Recommendation:
@app.after_request
def set_csp(response):
response.headers['Content-Security-Policy'] = (
"default-src 'self'; "
"media-src 'self' blob:; "
"script-src 'self' 'unsafe-inline';"
)
return response- Page Reload: Full page reload on playlist generation
- Blocking AJAX: Synchronous page replacement
- No Caching: Static assets not cached
- Large Images: Base64 encoding increases size by ~33%
$.ajax({
url: "/hook",
data: { imageBase64: dataURL },
success: function(data) {
songlist = JSON.parse(data.songs);
i = 0;
playNextSong();
}
});var dataURL = canvas.toDataURL("image/jpeg", 0.8);// Check for getUserMedia support
if (!navigator.getUserMedia) {
alert("Webcam not supported. Using random playlist.");
loadRandomPlaylist();
}// Preload next song
var nextAudio = new Audio('/static/music/' + nextSong);
nextAudio.load();- No keyboard navigation
- No screen reader support
- No captions for audio
- No alternative to webcam
<!-- Add ARIA labels -->
<button aria-label="Stop webcam and view emotion report">
Stop WebCam / See report
</button>
<!-- Add keyboard shortcuts -->
<script>
document.addEventListener('keydown', function(e) {
if (e.key === ' ') {
$('#aud')[0].paused ? $('#aud')[0].play() : $('#aud')[0].pause();
}
});
</script>
<!-- Add skip option -->
<button onclick="skipSong();">Skip Song</button>Minimal error handling:
- Console logging only
- No user feedback
- No retry logic
// Webcam error handling
function(err) {
alert("Camera access denied. Please enable camera permissions.");
console.log("Error: " + err);
}
// Audio error handling
$('#aud').on('error', function() {
console.log("Audio load failed, skipping...");
i++;
playNextSong();
});
// AJAX error handling
$.ajax({
// ...
error: function(xhr, status, error) {
alert("Failed to generate playlist. Please try again.");
console.error(error);
}
});- Webcam permission prompt appears
- Video preview shows in corner
- Initial snapshot taken after 5 seconds
- Playlist loads and plays automatically
- Song name displays correctly
- Audio controls work (play/pause/volume)
- Next song loads on 'ended' event
- Snapshot taken after 20 songs
- "Stop WebCam" button works
- "Get more music" button works
- Graph displays emotion history
Test in:
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
- Desktop (Windows/Mac/Linux)
- Mobile (iOS/Android) - Note: Limited support
- Tablet
- Modern CSS framework (Tailwind, Bootstrap)
- Responsive design
- Dark/light theme toggle
- Visualizer for audio playback
- Progress bar for playlist
- Skip song button
- Favorite/like songs
- Volume control
- Playlist shuffle
- Repeat mode
- Download playlist
- Loading indicators
- Smooth transitions
- Toast notifications
- Onboarding tutorial
- Help tooltips