Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/Jennys-Drumste / drum-master-ultimate.html |
| Date | Not specified in source |
| Repository | Janady13/Jennys-Drumste |
| Revision | 01c36ac1a180916e11864b4573b8c1999e2c5db3 |
| Branch | claude/complete-integration-feature-011CUkTzyyk8bKmXaRXJAR61 |
| Path or archive member | drum-master-ultimate.html |
| Original SHA-256 | 521761aff5188a837a36f5f3e718bd44f8538c9dc8d7989dec7e79d9e356ed5d |
| Git blob SHA | aaa350e0f2775b695b1edda6780d288fd8d5241b |
| Reading-copy SHA-256 | 521761aff5188a837a36f5f3e718bd44f8538c9dc8d7989dec7e79d9e356ed5d |
Recovered source transcript
Download complete source text ↓940 lines · 33.0 KB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DrumMaster Pro Ultimate - The Hive DNA AI Edition</title>
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Inter:wght@300;400;600;700;800;900&display=swap" rel="stylesheet">
<style>
:root {
--primary: #00ffcc;
--secondary: #ff00ff;
--accent: #ffff00;
--dark: #0a0a0f;
--darker: #050508;
--light: #ffffff;
--success: #00ff88;
--error: #ff0044;
--warning: #ffaa00;
--glow: rgba(0, 255, 204, 0.5);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
background: var(--darker);
color: var(--light);
overflow: hidden;
height: 100vh;
}
/* Ultra-modern gradient background */
.ultra-bg {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
radial-gradient(circle at 20% 50%, rgba(0, 255, 204, 0.15) 0%, transparent 50%),
radial-gradient(circle at 80% 50%, rgba(255, 0, 255, 0.15) 0%, transparent 50%),
radial-gradient(circle at 50% 100%, rgba(255, 255, 0, 0.1) 0%, transparent 50%),
linear-gradient(135deg, #0a0a0f 0%, #050508 100%);
z-index: -2;
animation: bgShift 20s ease-in-out infinite;
}
@keyframes bgShift {
0%, 100% { filter: hue-rotate(0deg); }
50% { filter: hue-rotate(30deg); }
}
/* Main container */
.app-container {
display: grid;
grid-template-rows: auto 1fr auto;
grid-template-columns: 300px 1fr 350px;
grid-template-areas:
"header header header"
"left center right"
"footer footer footer";
height: 100vh;
gap: 15px;
padding: 15px;
}
/* Epic Header */
.epic-header {
grid-area: header;
background: linear-gradient(135deg, rgba(0, 255, 204, 0.1) 0%, rgba(255, 0, 255, 0.1) 100%);
backdrop-filter: blur(20px);
border: 2px solid rgba(0, 255, 204, 0.2);
border-radius: 20px;
padding: 20px 30px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 10px 40px rgba(0, 255, 204, 0.2);
}
.logo-ultimate {
font-family: 'Orbitron', monospace;
font-size: 2.5em;
font-weight: 900;
background: linear-gradient(135deg, var(--primary), var(--secondary), var(--accent));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 0 0 40px var(--glow);
animation: logoGlow 3s ease-in-out infinite;
}
@keyframes logoGlow {
0%, 100% { filter: brightness(1); }
50% { filter: brightness(1.5); }
}
.ai-badge {
background: linear-gradient(135deg, rgba(0, 255, 204, 0.3), rgba(255, 0, 255, 0.3));
border: 2px solid var(--primary);
border-radius: 50px;
padding: 10px 25px;
font-family: 'Orbitron', monospace;
font-weight: 700;
display: flex;
align-items: center;
gap: 10px;
box-shadow: 0 0 30px var(--glow);
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); box-shadow: 0 0 30px var(--glow); }
50% { transform: scale(1.05); box-shadow: 0 0 50px var(--glow); }
}
.ai-pulse {
width: 10px;
height: 10px;
background: var(--success);
border-radius: 50%;
animation: aiPulse 1s infinite;
}
@keyframes aiPulse {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.5); opacity: 0.5; }
}
/* Left Panel - AI Coach */
.left-panel {
grid-area: left;
display: flex;
flex-direction: column;
gap: 15px;
overflow-y: auto;
}
.panel-card {
background: linear-gradient(135deg, rgba(10, 10, 15, 0.9), rgba(5, 5, 8, 0.9));
backdrop-filter: blur(10px);
border: 1px solid rgba(0, 255, 204, 0.2);
border-radius: 15px;
padding: 20px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3);
position: relative;
overflow: hidden;
}
.panel-card::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 2px;
background: linear-gradient(90deg, transparent, var(--primary), transparent);
animation: shimmer 3s infinite;
}
@keyframes shimmer {
0% { left: -100%; }
100% { left: 100%; }
}
.card-title {
font-family: 'Orbitron', monospace;
font-size: 1.2em;
font-weight: 700;
color: var(--primary);
margin-bottom: 15px;
text-transform: uppercase;
letter-spacing: 2px;
}
/* Center - 3D Visualizer */
.center-panel {
grid-area: center;
position: relative;
}
#webgl-canvas {
width: 100%;
height: 100%;
border-radius: 20px;
box-shadow: 0 10px 50px rgba(0, 0, 0, 0.5);
border: 2px solid rgba(0, 255, 204, 0.2);
}
.overlay-hud {
position: absolute;
top: 20px;
left: 20px;
right: 20px;
display: flex;
justify-content: space-between;
pointer-events: none;
}
.hud-stat {
background: rgba(10, 10, 15, 0.8);
backdrop-filter: blur(10px);
border: 1px solid rgba(0, 255, 204, 0.3);
border-radius: 10px;
padding: 10px 20px;
font-family: 'Orbitron', monospace;
}
.stat-value {
font-size: 2em;
font-weight: 900;
background: linear-gradient(135deg, var(--primary), var(--accent));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.stat-label {
font-size: 0.8em;
color: rgba(255, 255, 255, 0.6);
text-transform: uppercase;
}
/* Right Panel - Analytics */
.right-panel {
grid-area: right;
display: flex;
flex-direction: column;
gap: 15px;
overflow-y: auto;
}
/* AI Feedback Display */
.ai-feedback {
padding: 15px;
border-radius: 10px;
margin-top: 10px;
font-size: 0.95em;
animation: fadeIn 0.3s;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
.feedback-perfect {
background: linear-gradient(135deg, rgba(0, 255, 136, 0.2), rgba(0, 255, 204, 0.2));
border: 2px solid var(--success);
box-shadow: 0 0 20px rgba(0, 255, 136, 0.3);
}
.feedback-great {
background: linear-gradient(135deg, rgba(0, 255, 204, 0.2), rgba(0, 136, 255, 0.2));
border: 2px solid var(--primary);
}
.feedback-good {
background: rgba(255, 255, 0, 0.1);
border: 2px solid var(--warning);
}
.feedback-miss {
background: rgba(255, 0, 68, 0.1);
border: 2px solid var(--error);
}
/* Progress Bars */
.progress-bar {
width: 100%;
height: 8px;
background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
overflow: hidden;
margin: 10px 0;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--primary), var(--accent));
border-radius: 4px;
transition: width 0.5s ease;
box-shadow: 0 0 10px var(--glow);
}
/* Buttons */
.btn-ultimate {
padding: 12px 24px;
background: linear-gradient(135deg, var(--primary), var(--secondary));
color: var(--dark);
border: none;
border-radius: 10px;
font-weight: 700;
font-size: 1em;
cursor: pointer;
transition: all 0.3s;
position: relative;
overflow: hidden;
}
.btn-ultimate::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
background: rgba(255, 255, 255, 0.5);
border-radius: 50%;
transform: translate(-50%, -50%);
transition: width 0.5s, height 0.5s;
}
.btn-ultimate:hover {
transform: translateY(-2px);
box-shadow: 0 10px 30px var(--glow);
}
.btn-ultimate:hover::before {
width: 300px;
height: 300px;
}
.btn-ultimate:active {
transform: translateY(0);
}
/* Mini drum pads */
.drum-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
margin-top: 15px;
}
.drum-pad-mini {
aspect-ratio: 1;
background: linear-gradient(135deg, rgba(20, 20, 30, 0.9), rgba(10, 10, 20, 0.9));
border: 2px solid rgba(0, 255, 204, 0.2);
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s;
position: relative;
overflow: hidden;
}
.drum-pad-mini:hover {
border-color: var(--primary);
transform: scale(1.05);
}
.drum-pad-mini.active {
background: linear-gradient(135deg, var(--primary), var(--secondary));
border-color: var(--light);
transform: scale(0.95);
box-shadow: 0 0 30px var(--glow);
}
.drum-icon {
font-size: 2em;
}
.drum-name {
font-size: 0.8em;
font-weight: 600;
margin-top: 5px;
}
/* Mastery Meter */
.mastery-meter {
margin: 10px 0;
}
.mastery-bar {
display: flex;
gap: 3px;
margin-top: 5px;
}
.mastery-segment {
flex: 1;
height: 8px;
background: rgba(255, 255, 255, 0.1);
border-radius: 2px;
transition: all 0.3s;
}
.mastery-segment.filled {
background: linear-gradient(90deg, var(--success), var(--primary));
box-shadow: 0 0 5px var(--glow);
}
/* Footer */
.footer {
grid-area: footer;
background: rgba(10, 10, 15, 0.9);
backdrop-filter: blur(10px);
border: 1px solid rgba(0, 255, 204, 0.2);
border-radius: 15px;
padding: 15px 30px;
display: flex;
justify-content: space-between;
align-items: center;
}
.controls {
display: flex;
gap: 10px;
}
.fps-counter {
font-family: 'Orbitron', monospace;
color: var(--primary);
}
/* Scrollbar styling */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: linear-gradient(180deg, var(--primary), var(--secondary));
border-radius: 4px;
}
/* Responsive */
@media (max-width: 1200px) {
.app-container {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"center"
"right"
"left"
"footer";
}
}
/* Loading Animation */
.loading {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--darker);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
transition: opacity 0.5s;
}
.loading.hidden {
opacity: 0;
pointer-events: none;
}
.loading-spinner {
width: 80px;
height: 80px;
border: 4px solid rgba(0, 255, 204, 0.1);
border-top: 4px solid var(--primary);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>
</head>
<body>
<!-- Loading Screen -->
<div class="loading" id="loading">
<div class="loading-spinner"></div>
</div>
<!-- Ultra-modern background -->
<div class="ultra-bg"></div>
<!-- Main App Container -->
<div class="app-container">
<!-- Epic Header -->
<header class="epic-header">
<div class="logo-ultimate">🥁 DRUMMASTER PRO</div>
<div class="ai-badge">
<div class="ai-pulse"></div>
THE HIVE DNA AI
<div class="ai-pulse"></div>
</div>
<div id="connection-status" style="padding: 10px 20px; background: rgba(255, 0, 68, 0.2); border: 2px solid var(--error); border-radius: 50px; font-weight: 600;">
🔴 NO MIDI DETECTED
</div>
</header>
<!-- Left Panel - AI Coach -->
<aside class="left-panel">
<!-- AI Coach Card -->
<div class="panel-card">
<div class="card-title">🧠 AI COACH</div>
<div id="ai-coach-message" style="font-size: 0.95em; line-height: 1.6; color: rgba(255,255,255,0.8);">
Welcome to DrumMaster Pro Ultimate! Connect your MIDI drum kit to begin your journey with The Hive DNA AI.
</div>
<div id="ai-feedback-container"></div>
</div>
<!-- User DNA Profile -->
<div class="panel-card">
<div class="card-title">🧬 YOUR DNA PROFILE</div>
<div class="mastery-meter">
<div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
<span style="font-size: 0.9em;">Overall Skill</span>
<span id="overall-skill" style="font-weight: 700; color: var(--primary);">0%</span>
</div>
<div class="progress-bar">
<div id="overall-progress" class="progress-fill" style="width: 0%"></div>
</div>
</div>
<div class="mastery-meter">
<div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
<span style="font-size: 0.9em;">Learning Rate</span>
<span id="learning-rate" style="font-weight: 700; color: var(--accent);">1.0x</span>
</div>
</div>
<div id="skill-level" style="margin-top: 15px; padding: 10px; background: rgba(0,255,204,0.1); border-radius: 8px; text-align: center; font-weight: 700; color: var(--primary);">
BEGINNER
</div>
</div>
<!-- Practice Recommendations -->
<div class="panel-card">
<div class="card-title">🎯 PRACTICE FOCUS</div>
<div id="practice-recommendations" style="font-size: 0.9em;">
<p style="color: rgba(255,255,255,0.6);">Start playing to get personalized recommendations...</p>
</div>
</div>
</aside>
<!-- Center - 3D Visualizer -->
<main class="center-panel">
<canvas id="webgl-canvas"></canvas>
<!-- Overlay HUD -->
<div class="overlay-hud">
<div class="hud-stat">
<div class="stat-value" id="hud-score">0</div>
<div class="stat-label">Score</div>
</div>
<div class="hud-stat">
<div class="stat-value" id="hud-combo">0</div>
<div class="stat-label">Combo</div>
</div>
<div class="hud-stat">
<div class="stat-value" id="hud-accuracy">0%</div>
<div class="stat-label">Accuracy</div>
</div>
</div>
</main>
<!-- Right Panel - Analytics & Drums -->
<aside class="right-panel">
<!-- Real-time Stats -->
<div class="panel-card">
<div class="card-title">📊 LIVE STATS</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
<div style="text-align: center; padding: 10px; background: rgba(255,255,255,0.05); border-radius: 8px;">
<div style="font-size: 1.5em; font-weight: 900; color: var(--primary);" id="stat-timing">0%</div>
<div style="font-size: 0.8em; color: rgba(255,255,255,0.6);">Timing</div>
</div>
<div style="text-align: center; padding: 10px; background: rgba(255,255,255,0.05); border-radius: 8px;">
<div style="font-size: 1.5em; font-weight: 900; color: var(--accent);" id="stat-velocity">0%</div>
<div style="font-size: 0.8em; color: rgba(255,255,255,0.6);">Dynamics</div>
</div>
<div style="text-align: center; padding: 10px; background: rgba(255,255,255,0.05); border-radius: 8px;">
<div style="font-size: 1.5em; font-weight: 900; color: var(--success);" id="stat-hits">0</div>
<div style="font-size: 0.8em; color: rgba(255,255,255,0.6);">Total Hits</div>
</div>
<div style="text-align: center; padding: 10px; background: rgba(255,255,255,0.05); border-radius: 8px;">
<div style="font-size: 1.5em; font-weight: 900; color: var(--warning);" id="stat-level">1</div>
<div style="font-size: 0.8em; color: rgba(255,255,255,0.6);">Level</div>
</div>
</div>
</div>
<!-- Drum Mastery -->
<div class="panel-card">
<div class="card-title">🎵 DRUM MASTERY</div>
<div id="drum-mastery-list" style="font-size: 0.85em;">
<!-- Will be populated by JavaScript -->
</div>
</div>
<!-- Mini Drum Pads -->
<div class="panel-card">
<div class="card-title">🥁 DRUM KIT</div>
<div class="drum-grid">
<div class="drum-pad-mini" data-drum="Crash">
<div class="drum-icon">💥</div>
<div class="drum-name">Crash</div>
</div>
<div class="drum-pad-mini" data-drum="Hi-Hat">
<div class="drum-icon">⚡</div>
<div class="drum-name">Hi-Hat</div>
</div>
<div class="drum-pad-mini" data-drum="Ride">
<div class="drum-icon">🌟</div>
<div class="drum-name">Ride</div>
</div>
<div class="drum-pad-mini" data-drum="Tom 1">
<div class="drum-icon">🔵</div>
<div class="drum-name">Tom 1</div>
</div>
<div class="drum-pad-mini" data-drum="Snare">
<div class="drum-icon">🎯</div>
<div class="drum-name">Snare</div>
</div>
<div class="drum-pad-mini" data-drum="Tom 2">
<div class="drum-icon">🟣</div>
<div class="drum-name">Tom 2</div>
</div>
<div class="drum-pad-mini" data-drum="Tom 3">
<div class="drum-icon">🟢</div>
<div class="drum-name">Tom 3</div>
</div>
<div class="drum-pad-mini" data-drum="Kick">
<div class="drum-icon">🔴</div>
<div class="drum-name">Kick</div>
</div>
</div>
</div>
</aside>
<!-- Footer -->
<footer class="footer">
<div class="controls">
<button class="btn-ultimate" onclick="app.startPractice()">🎯 START PRACTICE</button>
<button class="btn-ultimate" onclick="app.viewAnalytics()">📊 ANALYTICS</button>
<button class="btn-ultimate" onclick="app.resetSession()">🔄 RESET</button>
</div>
<div class="fps-counter">FPS: <span id="fps-display">60</span></div>
</footer>
</div>
<!-- Load JavaScript Modules -->
<script src="hive-dna-ai.js"></script>
<script src="3d-drum-visualizer.js"></script>
<script>
/**
* DrumMaster Pro Ultimate - Main Application
* Integrates The Hive DNA AI with 3D Visualizer
*/
class DrumMasterUltimate {
constructor() {
this.ai = new HiveDNAAI();
this.visualizer = new DrumVisualizer3D('webgl-canvas');
this.midi = null;
this.session = {
score: 0,
combo: 0,
maxCombo: 0,
hits: 0,
startTime: Date.now()
};
this.init();
}
async init() {
// Initialize AI
await this.ai.initialize({
id: 'user_' + Date.now(),
level: 1
});
// Setup MIDI
this.setupMIDI();
// Setup UI updates
this.setupUIUpdates();
// Hide loading screen
setTimeout(() => {
document.getElementById('loading').classList.add('hidden');
}, 1000);
console.log('🚀 DrumMaster Pro Ultimate initialized');
}
async setupMIDI() {
if (!navigator.requestMIDIAccess) {
this.showMessage('MIDI not supported. Please use Chrome or Edge browser.', 'error');
return;
}
try {
const midiAccess = await navigator.requestMIDIAccess();
this.midi = midiAccess;
midiAccess.inputs.forEach(input => {
input.onmidimessage = (msg) => this.handleMIDIMessage(msg);
});
if (midiAccess.inputs.size > 0) {
this.updateConnectionStatus(true);
} else {
this.showMessage('No MIDI device found. Please connect your drum kit.', 'warning');
}
} catch (error) {
console.error('MIDI Error:', error);
this.showMessage('Failed to access MIDI devices', 'error');
}
}
handleMIDIMessage(message) {
const [status, note, velocity] = message.data;
// Note On message
if (status === 144 && velocity > 0) {
const drumName = this.noteToIDrum(note);
this.onDrumHit(drumName, velocity, Date.now());
}
}
onDrumHit(drum, velocity, timestamp) {
// Analyze with AI
const analysis = this.ai.analyzeHit({
drum,
timestamp,
velocity,
targetTime: null,
isCorrect: true
});
// Update 3D visualizer
this.visualizer.hitDrum(drum, velocity);
// Update session
this.session.hits++;
this.session.score += Math.round(analysis.overallScore);
if (analysis.overallScore > 70) {
this.session.combo++;
this.session.maxCombo = Math.max(this.session.maxCombo, this.session.combo);
} else {
this.session.combo = 0;
}
// Flash drum pad
this.flashDrumPad(drum);
// Show AI feedback
this.showAIFeedback(analysis.feedback);
// Update UI
this.updateUI(analysis);
}
noteToIDrum(note) {
const noteMap = {
36: 'Kick', // Bass drum
38: 'Snare', // Snare
42: 'Hi-Hat', // Closed hi-hat
46: 'Hi-Hat', // Open hi-hat
48: 'Tom 1', // High tom
45: 'Tom 2', // Mid tom
43: 'Tom 3', // Low tom
49: 'Crash', // Crash cymbal
51: 'Ride' // Ride cymbal
};
return noteMap[note] || 'Unknown';
}
flashDrumPad(drum) {
const pad = document.querySelector(`[data-drum="${drum}"]`);
if (pad) {
pad.classList.add('active');
setTimeout(() => pad.classList.remove('active'), 200);
}
}
showAIFeedback(feedback) {
const container = document.getElementById('ai-feedback-container');
const feedbackEl = document.createElement('div');
feedbackEl.className = `ai-feedback feedback-${feedback.type}`;
feedbackEl.innerHTML = `
<div style="font-weight: 700; margin-bottom: 5px;">${feedback.message}</div>
${feedback.tips.map(tip => `<div style="font-size: 0.85em; margin: 3px 0;">• ${tip}</div>`).join('')}
`;
container.innerHTML = '';
container.appendChild(feedbackEl);
// Remove after 5 seconds
setTimeout(() => feedbackEl.remove(), 5000);
}
updateUI(analysis) {
// Update HUD
document.getElementById('hud-score').textContent = this.session.score;
document.getElementById('hud-combo').textContent = this.session.combo;
document.getElementById('hud-accuracy').textContent = Math.round(analysis.timingAccuracy) + '%';
// Update stats
document.getElementById('stat-timing').textContent = Math.round(analysis.timingAccuracy) + '%';
document.getElementById('stat-velocity').textContent = Math.round(analysis.velocityScore) + '%';
document.getElementById('stat-hits').textContent = this.session.hits;
// Update DNA profile
const analytics = this.ai.getPerformanceAnalytics();
document.getElementById('overall-skill').textContent = analytics.overall.score + '%';
document.getElementById('overall-progress').style.width = analytics.overall.score + '%';
document.getElementById('skill-level').textContent = analytics.overall.level.toUpperCase();
document.getElementById('learning-rate').textContent = analytics.learningRate.toFixed(1) + 'x';
document.getElementById('stat-level').textContent = this.getLevelNumber(analytics.overall.level);
// Update drum mastery
this.updateDrumMastery(analytics.mastery);
// Update practice recommendations
if (analytics.predictions.recommendedPractice.length > 0) {
const recs = analytics.predictions.recommendedPractice
.map(rec => `<div style="margin: 8px 0; padding: 8px; background: rgba(0,255,204,0.1); border-left: 3px solid var(--primary); border-radius: 4px;">
<strong>${rec.focus}</strong>: ${rec.exercise}
</div>`)
.join('');
document.getElementById('practice-recommendations').innerHTML = recs;
}
}
updateDrumMastery(mastery) {
const container = document.getElementById('drum-mastery-list');
let html = '';
for (const [drum, masteryLevel] of Object.entries(mastery)) {
const segments = 10;
const filled = Math.floor((masteryLevel / 100) * segments);
html += `
<div style="margin: 10px 0;">
<div style="display: flex; justify-content: space-between; margin-bottom: 3px;">
<span>${drum}</span>
<span style="color: var(--primary); font-weight: 700;">${Math.round(masteryLevel)}%</span>
</div>
<div class="mastery-bar">
${Array(segments).fill(0).map((_, i) =>
`<div class="mastery-segment ${i < filled ? 'filled' : ''}"></div>`
).join('')}
</div>
</div>
`;
}
container.innerHTML = html || '<p style="color: rgba(255,255,255,0.5);">No data yet...</p>';
}
getLevelNumber(levelName) {
const levels = { 'Beginner': 1, 'Novice': 2, 'Intermediate': 3, 'Advanced': 4, 'Expert': 5 };
return levels[levelName] || 1;
}
setupUIUpdates() {
// Update FPS counter
setInterval(() => {
document.getElementById('fps-display').textContent = this.visualizer.getFPS();
}, 1000);
}
updateConnectionStatus(connected) {
const status = document.getElementById('connection-status');
if (connected) {
status.innerHTML = '🟢 MIDI CONNECTED';
status.style.background = 'rgba(0, 255, 136, 0.2)';
status.style.borderColor = 'var(--success)';
} else {
status.innerHTML = '🔴 NO MIDI DETECTED';
status.style.background = 'rgba(255, 0, 68, 0.2)';
status.style.borderColor = 'var(--error)';
}
}
showMessage(message, type = 'info') {
const coach = document.getElementById('ai-coach-message');
coach.textContent = message;
coach.style.color = type === 'error' ? 'var(--error)' :
type === 'warning' ? 'var(--warning)' :
'rgba(255,255,255,0.8)';
}
startPractice() {
this.showMessage('Practice mode activated! Play your drums to begin training.', 'info');
}
viewAnalytics() {
const analytics = this.ai.getPerformanceAnalytics();
console.log('📊 Full Analytics:', analytics);
alert('Check console for detailed analytics');
}
resetSession() {
this.session = {
score: 0,
combo: 0,
maxCombo: 0,
hits: 0,
startTime: Date.now()
};
this.ai.reset();
this.showMessage('Session reset! Ready for a fresh start.', 'info');
this.updateUI({ timingAccuracy: 0, velocityScore: 0, overallScore: 0 });
}
}
// Initialize app
let app;
window.addEventListener('DOMContentLoaded', () => {
app = new DrumMasterUltimate();
});
</script>
</body>
</html>