Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/Jennys-Drumste / drum-teacher-pro.html |
| Date | Not specified in source |
| Repository | Janady13/Jennys-Drumste |
| Revision | c601bf1648fe71058835589331c7f7e669940b8b |
| Branch | main |
| Path or archive member | drum-teacher-pro.html |
| Original SHA-256 | f1cb6620699816d00f043caa08d760396b543d928b13ab36002de2b01bd155e4 |
| Git blob SHA | 1e5e63c6435077c051d5583d3e8f6d874288bcab |
| Reading-copy SHA-256 | f1cb6620699816d00f043caa08d760396b543d928b13ab36002de2b01bd155e4 |
Every recorded occurrence
These 2 records contain identical original source bytes. Their paths and revisions remain part of the archive.
| Original record | Branch or archive | Revision / identifier | Original location |
|---|---|---|---|
| Janady13/Jennys-Drumste / drum-teacher-pro.html | main | c601bf1648fe71058835589331c7f7e669940b8b | Open original |
| Janady13/Jennys-Drumste / drum-teacher-pro.html | claude/complete-integration-feature-011CUkTzyyk8bKmXaRXJAR61 | 01c36ac1a180916e11864b4573b8c1999e2c5db3 | Open original |
Recovered source transcript
Download complete source text ↓1,802 lines · 61.3 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 - Advanced Drum Learning System</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Inter:wght@300;400;600;700&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--primary: #00ffcc;
--secondary: #ff00ff;
--accent: #ffff00;
--dark: #0a0a0f;
--darker: #050508;
--light: #ffffff;
--success: #00ff88;
--error: #ff0044;
--warning: #ffaa00;
}
body {
font-family: 'Inter', sans-serif;
background: var(--darker);
min-height: 100vh;
color: var(--light);
overflow-x: hidden;
position: relative;
}
/* Animated background */
.bg-animation {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
radial-gradient(circle at 20% 50%, rgba(0, 255, 204, 0.1) 0%, transparent 50%),
radial-gradient(circle at 80% 50%, rgba(255, 0, 255, 0.1) 0%, transparent 50%),
radial-gradient(circle at 50% 100%, rgba(255, 255, 0, 0.05) 0%, transparent 50%);
z-index: -2;
animation: bgPulse 10s ease-in-out infinite;
}
@keyframes bgPulse {
0%, 100% { opacity: 0.5; }
50% { opacity: 1; }
}
.particles {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
}
.particle {
position: absolute;
width: 2px;
height: 2px;
background: var(--primary);
opacity: 0;
animation: float 10s linear infinite;
}
@keyframes float {
0% {
transform: translateY(100vh) translateX(0);
opacity: 0;
}
10% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
transform: translateY(-100vh) translateX(100px);
opacity: 0;
}
}
/* Header */
header {
background: linear-gradient(135deg, rgba(10, 10, 15, 0.95), rgba(5, 5, 8, 0.95));
backdrop-filter: blur(20px);
border-bottom: 1px solid rgba(0, 255, 204, 0.2);
padding: 20px;
position: relative;
z-index: 100;
}
.header-content {
max-width: 1600px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
}
h1 {
font-family: 'Orbitron', monospace;
font-size: 2.5em;
font-weight: 900;
background: linear-gradient(135deg, var(--primary), var(--secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 0 0 40px rgba(0, 255, 204, 0.5);
}
.connection-hub {
display: flex;
align-items: center;
gap: 20px;
}
.connection-status {
padding: 12px 24px;
background: rgba(0, 255, 204, 0.1);
border: 1px solid rgba(0, 255, 204, 0.3);
border-radius: 50px;
font-size: 0.9em;
display: flex;
align-items: center;
gap: 10px;
transition: all 0.3s;
}
.connection-status.connected {
background: rgba(0, 255, 136, 0.2);
border-color: var(--success);
box-shadow: 0 0 20px rgba(0, 255, 136, 0.3);
}
.pulse-dot {
width: 8px;
height: 8px;
background: var(--error);
border-radius: 50%;
animation: pulse 2s infinite;
}
.pulse-dot.active {
background: var(--success);
}
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.5); opacity: 0.5; }
}
/* Main Layout */
.app-container {
max-width: 1600px;
margin: 0 auto;
padding: 30px 20px;
display: grid;
grid-template-columns: 1fr 3fr 1fr;
gap: 30px;
}
/* Left Panel */
.left-panel {
display: flex;
flex-direction: column;
gap: 20px;
}
.panel-section {
background: linear-gradient(135deg, rgba(10, 10, 15, 0.8), rgba(5, 5, 8, 0.8));
backdrop-filter: blur(10px);
border: 1px solid rgba(0, 255, 204, 0.1);
border-radius: 20px;
padding: 20px;
position: relative;
overflow: hidden;
}
.panel-section::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent, var(--primary), transparent);
animation: shimmer 3s infinite;
}
@keyframes shimmer {
0%, 100% { transform: translateX(-100%); }
50% { transform: translateX(100%); }
}
.section-title {
font-family: 'Orbitron', monospace;
font-size: 1.1em;
font-weight: 700;
color: var(--primary);
margin-bottom: 15px;
text-transform: uppercase;
letter-spacing: 2px;
}
/* Modern Buttons */
.btn {
background: linear-gradient(135deg, rgba(0, 255, 204, 0.1), rgba(255, 0, 255, 0.1));
border: 1px solid rgba(0, 255, 204, 0.3);
color: var(--light);
padding: 12px 24px;
border-radius: 12px;
cursor: pointer;
font-size: 1em;
font-weight: 600;
transition: all 0.3s;
position: relative;
overflow: hidden;
}
.btn::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
background: radial-gradient(circle, var(--primary), transparent);
transition: width 0.3s, height 0.3s;
transform: translate(-50%, -50%);
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 30px rgba(0, 255, 204, 0.3);
border-color: var(--primary);
}
.btn:hover::before {
width: 200px;
height: 200px;
}
.btn.primary {
background: linear-gradient(135deg, var(--primary), var(--secondary));
border: none;
color: var(--dark);
font-weight: 700;
}
.btn:disabled {
opacity: 0.3;
cursor: not-allowed;
}
/* Drum Kit Visualization */
.drum-kit-container {
background: linear-gradient(135deg, rgba(10, 10, 15, 0.9), rgba(5, 5, 8, 0.9));
border-radius: 30px;
padding: 30px;
border: 1px solid rgba(0, 255, 204, 0.1);
position: relative;
}
.drum-kit-3d {
perspective: 1000px;
margin: 20px 0;
}
.drum-kit {
display: grid;
grid-template-areas:
"crash hihat hihat ride"
"tom1 tom1 tom2 tom2"
"snare snare tom3 tom3"
"kick kick kick kick";
gap: 15px;
transform-style: preserve-3d;
animation: floatKit 6s ease-in-out infinite;
}
@keyframes floatKit {
0%, 100% { transform: rotateX(5deg) rotateY(-5deg); }
50% { transform: rotateX(5deg) rotateY(5deg); }
}
.drum-pad {
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: 15px;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s;
position: relative;
overflow: hidden;
min-height: 100px;
}
.drum-pad[data-name="Crash"] { grid-area: crash; }
.drum-pad[data-name="Hi-Hat"] { grid-area: hihat; }
.drum-pad[data-name="Ride"] { grid-area: ride; }
.drum-pad[data-name="Tom 1"] { grid-area: tom1; }
.drum-pad[data-name="Tom 2"] { grid-area: tom2; }
.drum-pad[data-name="Tom 3"] { grid-area: tom3; }
.drum-pad[data-name="Snare"] { grid-area: snare; }
.drum-pad[data-name="Kick"] { grid-area: kick; }
.drum-pad .drum-name {
font-weight: 700;
font-size: 1.1em;
margin-bottom: 5px;
}
.drum-pad .drum-icon {
font-size: 2em;
margin-bottom: 5px;
}
.drum-pad .velocity-bar {
width: 100%;
height: 4px;
background: rgba(0, 0, 0, 0.3);
border-radius: 2px;
overflow: hidden;
margin-top: 10px;
}
.velocity-fill {
height: 100%;
background: linear-gradient(90deg, var(--success), var(--primary));
width: 0%;
transition: width 0.1s;
}
.drum-pad.active {
animation: drumHit 0.3s;
border-color: var(--primary);
box-shadow: 0 0 30px rgba(0, 255, 204, 0.6);
}
@keyframes drumHit {
0% { transform: scale(1) translateZ(0); }
50% { transform: scale(1.15) translateZ(20px); }
100% { transform: scale(1) translateZ(0); }
}
.drum-pad.target {
animation: targetPulse 0.5s infinite;
border-color: var(--accent);
background: linear-gradient(135deg, rgba(255, 255, 0, 0.1), rgba(255, 165, 0, 0.1));
}
@keyframes targetPulse {
0%, 100% {
box-shadow: 0 0 20px rgba(255, 255, 0, 0.4);
transform: scale(1);
}
50% {
box-shadow: 0 0 40px rgba(255, 255, 0, 0.8);
transform: scale(1.05);
}
}
.drum-pad.hit-perfect {
animation: perfectHit 0.5s;
background: linear-gradient(135deg, rgba(0, 255, 136, 0.3), rgba(0, 255, 204, 0.3));
}
.drum-pad.hit-good {
animation: goodHit 0.5s;
background: linear-gradient(135deg, rgba(0, 255, 204, 0.2), rgba(0, 136, 255, 0.2));
}
.drum-pad.hit-miss {
animation: missHit 0.5s;
background: linear-gradient(135deg, rgba(255, 0, 68, 0.2), rgba(255, 0, 136, 0.2));
}
@keyframes perfectHit {
0% { transform: scale(1) rotateZ(0); }
50% { transform: scale(1.3) rotateZ(5deg); }
100% { transform: scale(1) rotateZ(0); }
}
@keyframes goodHit {
0% { transform: scale(1); }
50% { transform: scale(1.2); }
100% { transform: scale(1); }
}
@keyframes missHit {
0% { transform: translateX(0); }
25% { transform: translateX(-10px); }
75% { transform: translateX(10px); }
100% { transform: translateX(0); }
}
/* Pattern Visualizer */
.pattern-visualizer {
background: rgba(0, 0, 0, 0.5);
border-radius: 15px;
padding: 20px;
margin: 20px 0;
height: 200px;
position: relative;
overflow: hidden;
}
.pattern-grid {
display: grid;
grid-template-rows: repeat(9, 1fr);
height: 100%;
position: relative;
}
.pattern-track {
display: flex;
align-items: center;
border-bottom: 1px solid rgba(0, 255, 204, 0.1);
position: relative;
}
.track-label {
width: 60px;
font-size: 0.8em;
color: var(--primary);
font-weight: 600;
}
.track-timeline {
flex: 1;
height: 100%;
position: relative;
}
.beat-marker {
position: absolute;
width: 10px;
height: 10px;
background: var(--accent);
border-radius: 50%;
top: 50%;
transform: translateY(-50%);
box-shadow: 0 0 10px rgba(255, 255, 0, 0.5);
}
.playhead {
position: absolute;
width: 2px;
height: 100%;
background: linear-gradient(180deg, transparent, var(--primary), transparent);
left: 0;
animation: movePlayhead 2s linear infinite;
box-shadow: 0 0 20px var(--primary);
}
@keyframes movePlayhead {
from { left: 0; }
to { left: 100%; }
}
/* Right Panel - Stats */
.right-panel {
display: flex;
flex-direction: column;
gap: 20px;
}
.stat-card {
background: linear-gradient(135deg, rgba(10, 10, 15, 0.8), rgba(5, 5, 8, 0.8));
border: 1px solid rgba(0, 255, 204, 0.1);
border-radius: 15px;
padding: 15px;
text-align: center;
position: relative;
overflow: hidden;
}
.stat-label {
font-size: 0.9em;
color: rgba(255, 255, 255, 0.6);
margin-bottom: 5px;
text-transform: uppercase;
letter-spacing: 1px;
}
.stat-value {
font-family: 'Orbitron', monospace;
font-size: 2em;
font-weight: 700;
background: linear-gradient(135deg, var(--primary), var(--secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.stat-chart {
margin-top: 10px;
height: 40px;
position: relative;
}
.mini-bars {
display: flex;
align-items: flex-end;
height: 100%;
gap: 2px;
}
.mini-bar {
flex: 1;
background: linear-gradient(180deg, var(--primary), rgba(0, 255, 204, 0.3));
border-radius: 2px;
transition: height 0.3s;
}
/* Achievement System */
.achievements {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
margin-top: 15px;
}
.achievement {
width: 50px;
height: 50px;
background: linear-gradient(135deg, rgba(20, 20, 30, 0.9), rgba(10, 10, 20, 0.9));
border: 2px solid rgba(0, 255, 204, 0.1);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5em;
cursor: pointer;
transition: all 0.3s;
position: relative;
}
.achievement.unlocked {
border-color: var(--accent);
background: linear-gradient(135deg, rgba(255, 255, 0, 0.1), rgba(255, 165, 0, 0.1));
animation: achievementUnlock 0.5s;
}
@keyframes achievementUnlock {
0% { transform: scale(0) rotateZ(0); }
50% { transform: scale(1.2) rotateZ(180deg); }
100% { transform: scale(1) rotateZ(360deg); }
}
.achievement-tooltip {
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: var(--dark);
color: var(--light);
padding: 5px 10px;
border-radius: 5px;
font-size: 0.8em;
white-space: nowrap;
pointer-events: none;
opacity: 0;
transition: opacity 0.3s;
}
.achievement:hover .achievement-tooltip {
opacity: 1;
}
/* Recording Controls */
.recording-panel {
display: flex;
gap: 10px;
margin-top: 20px;
}
.record-btn {
background: linear-gradient(135deg, rgba(255, 0, 68, 0.2), rgba(255, 0, 136, 0.2));
border-color: rgba(255, 0, 68, 0.5);
}
.record-btn.recording {
animation: recordPulse 1s infinite;
background: linear-gradient(135deg, rgba(255, 0, 68, 0.4), rgba(255, 0, 136, 0.4));
}
@keyframes recordPulse {
0%, 100% { box-shadow: 0 0 20px rgba(255, 0, 68, 0.5); }
50% { box-shadow: 0 0 40px rgba(255, 0, 68, 0.8); }
}
/* Performance Graph */
.performance-graph {
height: 150px;
background: rgba(0, 0, 0, 0.3);
border-radius: 10px;
padding: 10px;
position: relative;
margin-top: 20px;
}
.graph-canvas {
width: 100%;
height: 100%;
}
/* Modal */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.9);
backdrop-filter: blur(10px);
z-index: 1000;
align-items: center;
justify-content: center;
}
.modal.active {
display: flex;
animation: fadeIn 0.3s;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.modal-content {
background: linear-gradient(135deg, rgba(10, 10, 15, 0.95), rgba(5, 5, 8, 0.95));
border: 1px solid var(--primary);
border-radius: 30px;
padding: 40px;
max-width: 600px;
text-align: center;
animation: slideUp 0.3s;
}
@keyframes slideUp {
from { transform: translateY(50px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.modal h2 {
font-family: 'Orbitron', monospace;
font-size: 2em;
background: linear-gradient(135deg, var(--primary), var(--secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 20px;
}
/* Lesson Selector */
.lesson-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
margin-top: 15px;
}
.lesson-card {
background: rgba(0, 255, 204, 0.05);
border: 1px solid rgba(0, 255, 204, 0.2);
border-radius: 10px;
padding: 10px;
cursor: pointer;
transition: all 0.3s;
}
.lesson-card:hover {
background: rgba(0, 255, 204, 0.1);
transform: translateY(-2px);
}
.lesson-card.active {
background: rgba(0, 255, 204, 0.2);
border-color: var(--primary);
}
.lesson-card .lesson-name {
font-weight: 600;
font-size: 0.9em;
margin-bottom: 5px;
}
.lesson-card .lesson-difficulty {
font-size: 0.8em;
color: rgba(255, 255, 255, 0.6);
}
.difficulty-stars {
color: var(--accent);
}
/* Tempo Control */
.tempo-control {
display: flex;
align-items: center;
gap: 15px;
margin-top: 15px;
}
.tempo-slider {
flex: 1;
-webkit-appearance: none;
height: 6px;
background: rgba(0, 255, 204, 0.1);
border-radius: 3px;
outline: none;
}
.tempo-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
background: var(--primary);
border-radius: 50%;
cursor: pointer;
box-shadow: 0 0 10px var(--primary);
}
.tempo-display {
font-family: 'Orbitron', monospace;
font-size: 1.2em;
color: var(--primary);
min-width: 80px;
}
/* Sound Toggle */
.sound-toggle {
display: flex;
align-items: center;
gap: 10px;
margin-top: 15px;
}
.toggle-switch {
width: 50px;
height: 25px;
background: rgba(0, 255, 204, 0.1);
border-radius: 25px;
position: relative;
cursor: pointer;
transition: background 0.3s;
}
.toggle-switch.active {
background: var(--primary);
}
.toggle-knob {
width: 21px;
height: 21px;
background: var(--light);
border-radius: 50%;
position: absolute;
top: 2px;
left: 2px;
transition: transform 0.3s;
}
.toggle-switch.active .toggle-knob {
transform: translateX(25px);
}
/* Loading Animation */
.loader {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 2000;
}
.loader.active {
display: block;
}
.loader-circle {
width: 60px;
height: 60px;
border: 3px solid rgba(0, 255, 204, 0.1);
border-top-color: var(--primary);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
@media (max-width: 1200px) {
.app-container {
grid-template-columns: 1fr;
}
.left-panel, .right-panel {
grid-template-columns: repeat(2, 1fr);
display: grid;
}
}
</style>
</head>
<body>
<div class="bg-animation"></div>
<div class="particles" id="particles"></div>
<header>
<div class="header-content">
<h1>🥁 DrumMaster Pro</h1>
<div class="connection-hub">
<div class="connection-status" id="connectionStatus">
<span class="pulse-dot" id="pulseDot"></span>
<span id="statusText">Disconnected - Click Connect</span>
</div>
<button class="btn primary" id="connectBtn">Connect MIDI</button>
</div>
</div>
</header>
<div class="app-container">
<!-- Left Panel -->
<div class="left-panel">
<!-- Controls -->
<div class="panel-section">
<h3 class="section-title">Controls</h3>
<div style="display: flex; flex-direction: column; gap: 10px;">
<button class="btn primary" id="startBtn" disabled>Start Session</button>
<button class="btn" id="stopBtn" disabled>Stop</button>
<button class="btn" id="freePlayBtn">Free Play</button>
<button class="btn" id="metronomeBtn">Metronome</button>
</div>
</div>
<!-- Lessons -->
<div class="panel-section">
<h3 class="section-title">Training Modules</h3>
<div class="lesson-grid" id="lessonGrid">
<!-- Lessons will be populated here -->
</div>
</div>
<!-- Tempo -->
<div class="panel-section">
<h3 class="section-title">Tempo</h3>
<div class="tempo-control">
<input type="range" class="tempo-slider" id="tempoSlider" min="40" max="200" value="90">
<span class="tempo-display" id="tempoDisplay">90 BPM</span>
</div>
<div class="sound-toggle">
<span>Click Track</span>
<div class="toggle-switch" id="clickToggle">
<div class="toggle-knob"></div>
</div>
</div>
</div>
<!-- Recording -->
<div class="panel-section">
<h3 class="section-title">Recording</h3>
<div class="recording-panel">
<button class="btn record-btn" id="recordBtn">⏺ Record</button>
<button class="btn" id="playbackBtn" disabled>▶ Play</button>
<button class="btn" id="saveBtn" disabled>💾 Save</button>
</div>
</div>
</div>
<!-- Center - Drum Kit -->
<div class="center-panel">
<div class="drum-kit-container">
<div class="pattern-visualizer">
<div class="pattern-grid" id="patternGrid">
<div class="playhead" id="playhead"></div>
<!-- Pattern tracks will be populated here -->
</div>
</div>
<div class="drum-kit-3d">
<div class="drum-kit" id="drumKit">
<div class="drum-pad" data-note="49" data-name="Crash">
<span class="drum-icon">💥</span>
<span class="drum-name">Crash</span>
<div class="velocity-bar"><div class="velocity-fill"></div></div>
</div>
<div class="drum-pad" data-note="46" data-name="Hi-Hat">
<span class="drum-icon">🎩</span>
<span class="drum-name">Hi-Hat</span>
<div class="velocity-bar"><div class="velocity-fill"></div></div>
</div>
<div class="drum-pad" data-note="51" data-name="Ride">
<span class="drum-icon">🔔</span>
<span class="drum-name">Ride</span>
<div class="velocity-bar"><div class="velocity-fill"></div></div>
</div>
<div class="drum-pad" data-note="48" data-name="Tom 1">
<span class="drum-icon">🥁</span>
<span class="drum-name">Tom 1</span>
<div class="velocity-bar"><div class="velocity-fill"></div></div>
</div>
<div class="drum-pad" data-note="45" data-name="Tom 2">
<span class="drum-icon">🥁</span>
<span class="drum-name">Tom 2</span>
<div class="velocity-bar"><div class="velocity-fill"></div></div>
</div>
<div class="drum-pad" data-note="43" data-name="Tom 3">
<span class="drum-icon">🥁</span>
<span class="drum-name">Tom 3</span>
<div class="velocity-bar"><div class="velocity-fill"></div></div>
</div>
<div class="drum-pad" data-note="38" data-name="Snare">
<span class="drum-icon">🎵</span>
<span class="drum-name">Snare</span>
<div class="velocity-bar"><div class="velocity-fill"></div></div>
</div>
<div class="drum-pad" data-note="36" data-name="Kick">
<span class="drum-icon">🦶</span>
<span class="drum-name">Kick</span>
<div class="velocity-bar"><div class="velocity-fill"></div></div>
</div>
</div>
</div>
<div class="performance-graph">
<canvas class="graph-canvas" id="performanceGraph"></canvas>
</div>
</div>
</div>
<!-- Right Panel - Stats -->
<div class="right-panel">
<div class="stat-card">
<div class="stat-label">Accuracy</div>
<div class="stat-value" id="accuracy">0%</div>
<div class="stat-chart">
<div class="mini-bars" id="accuracyBars"></div>
</div>
</div>
<div class="stat-card">
<div class="stat-label">Combo</div>
<div class="stat-value" id="combo">0</div>
<div class="stat-chart">
<div class="mini-bars" id="comboBars"></div>
</div>
</div>
<div class="stat-card">
<div class="stat-label">Score</div>
<div class="stat-value" id="score">0</div>
</div>
<div class="stat-card">
<div class="stat-label">Session Time</div>
<div class="stat-value" id="sessionTime">0:00</div>
</div>
<div class="panel-section">
<h3 class="section-title">Achievements</h3>
<div class="achievements" id="achievements">
<!-- Achievements will be populated here -->
</div>
</div>
<div class="panel-section">
<h3 class="section-title">Leaderboard</h3>
<div id="leaderboard">
<div style="text-align: center; color: rgba(255,255,255,0.5); padding: 20px;">
Coming Soon
</div>
</div>
</div>
</div>
</div>
<!-- Modal -->
<div class="modal" id="modal">
<div class="modal-content">
<h2 id="modalTitle">Session Complete!</h2>
<div id="modalBody"></div>
<button class="btn primary" onclick="closeModal()">Continue</button>
</div>
</div>
<div class="loader" id="loader">
<div class="loader-circle"></div>
</div>
<script>
// Advanced Lesson System
const LESSONS = {
basic: {
name: 'Fundamentals',
difficulty: 1,
icon: '📚',
patterns: [
{ time: 0, drum: 36 },
{ time: 500, drum: 38 },
{ time: 1000, drum: 36 },
{ time: 1500, drum: 38 }
],
duration: 2000
},
groove: {
name: 'Pocket Groove',
difficulty: 2,
icon: '🎸',
patterns: [
{ time: 0, drum: 36 },
{ time: 0, drum: 46 },
{ time: 250, drum: 46 },
{ time: 500, drum: 38 },
{ time: 500, drum: 46 },
{ time: 750, drum: 46 },
{ time: 1000, drum: 36 },
{ time: 1000, drum: 46 },
{ time: 1250, drum: 46 },
{ time: 1500, drum: 38 },
{ time: 1500, drum: 46 },
{ time: 1750, drum: 46 }
],
duration: 2000
},
funk: {
name: 'Funk Pattern',
difficulty: 3,
icon: '🎷',
patterns: [
{ time: 0, drum: 36 },
{ time: 0, drum: 46 },
{ time: 125, drum: 46 },
{ time: 250, drum: 38 },
{ time: 250, drum: 46 },
{ time: 375, drum: 36 },
{ time: 500, drum: 46 },
{ time: 625, drum: 38 },
{ time: 625, drum: 46 },
{ time: 750, drum: 46 },
{ time: 875, drum: 36 },
{ time: 1000, drum: 46 },
{ time: 1125, drum: 38 },
{ time: 1250, drum: 46 },
{ time: 1375, drum: 36 },
{ time: 1500, drum: 46 },
{ time: 1625, drum: 38 },
{ time: 1750, drum: 46 }
],
duration: 2000
},
jazz: {
name: 'Jazz Swing',
difficulty: 4,
icon: '🎺',
patterns: [
{ time: 0, drum: 51 },
{ time: 333, drum: 51 },
{ time: 500, drum: 51 },
{ time: 666, drum: 51 },
{ time: 1000, drum: 51 },
{ time: 1333, drum: 51 },
{ time: 1500, drum: 51 },
{ time: 1666, drum: 51 },
{ time: 250, drum: 36 },
{ time: 750, drum: 38 },
{ time: 1250, drum: 36 },
{ time: 1750, drum: 38 }
],
duration: 2000
},
metal: {
name: 'Metal Blast',
difficulty: 5,
icon: '🤘',
patterns: [
{ time: 0, drum: 36 },
{ time: 0, drum: 49 },
{ time: 125, drum: 36 },
{ time: 250, drum: 38 },
{ time: 375, drum: 36 },
{ time: 500, drum: 36 },
{ time: 625, drum: 38 },
{ time: 750, drum: 36 },
{ time: 875, drum: 36 },
{ time: 1000, drum: 38 },
{ time: 1125, drum: 36 },
{ time: 1250, drum: 36 },
{ time: 1375, drum: 38 },
{ time: 1500, drum: 36 },
{ time: 1625, drum: 36 },
{ time: 1750, drum: 38 }
],
duration: 2000
},
latin: {
name: 'Latin Rhythms',
difficulty: 3,
icon: '💃',
patterns: [
{ time: 0, drum: 36 },
{ time: 0, drum: 51 },
{ time: 187, drum: 51 },
{ time: 375, drum: 51 },
{ time: 500, drum: 38 },
{ time: 500, drum: 51 },
{ time: 687, drum: 51 },
{ time: 875, drum: 51 },
{ time: 1000, drum: 36 },
{ time: 1000, drum: 51 },
{ time: 1187, drum: 51 },
{ time: 1375, drum: 51 },
{ time: 1500, drum: 38 },
{ time: 1500, drum: 51 },
{ time: 1687, drum: 51 },
{ time: 1875, drum: 51 }
],
duration: 2000
},
paradiddle: {
name: 'Paradiddles',
difficulty: 4,
icon: '🔄',
patterns: [
{ time: 0, drum: 38 },
{ time: 125, drum: 38 },
{ time: 250, drum: 38 },
{ time: 375, drum: 38 },
{ time: 500, drum: 38 },
{ time: 625, drum: 38 },
{ time: 750, drum: 38 },
{ time: 875, drum: 38 }
],
duration: 1000
},
ghost: {
name: 'Ghost Notes',
difficulty: 4,
icon: '👻',
patterns: [
{ time: 0, drum: 36 },
{ time: 125, drum: 38, velocity: 30 },
{ time: 250, drum: 38, velocity: 30 },
{ time: 375, drum: 38 },
{ time: 500, drum: 36 },
{ time: 625, drum: 38, velocity: 30 },
{ time: 750, drum: 38, velocity: 30 },
{ time: 875, drum: 38 },
{ time: 1000, drum: 36 },
{ time: 1125, drum: 38, velocity: 30 },
{ time: 1250, drum: 36 },
{ time: 1375, drum: 38 },
{ time: 1500, drum: 38, velocity: 30 },
{ time: 1625, drum: 38, velocity: 30 },
{ time: 1750, drum: 38 }
],
duration: 2000
}
};
// Achievement System
const ACHIEVEMENTS = [
{ id: 'first_hit', name: 'First Hit', icon: '🎯', condition: () => stats.totalHits >= 1 },
{ id: 'combo_10', name: 'Combo x10', icon: '🔥', condition: () => stats.maxCombo >= 10 },
{ id: 'combo_50', name: 'Combo x50', icon: '💥', condition: () => stats.maxCombo >= 50 },
{ id: 'perfect_run', name: 'Perfect Run', icon: '⭐', condition: () => stats.perfectRuns >= 1 },
{ id: 'speed_demon', name: 'Speed Demon', icon: '⚡', condition: () => stats.maxBPM >= 160 },
{ id: 'marathon', name: 'Marathon', icon: '🏃', condition: () => stats.sessionTime >= 1800 },
{ id: 'accuracy_master', name: 'Accuracy Master', icon: '🎯', condition: () => stats.sessionAccuracy >= 95 },
{ id: 'all_drums', name: 'Kit Master', icon: '🥁', condition: () => stats.drumsUsed.size >= 8 },
{ id: 'level_10', name: 'Level 10', icon: '🏆', condition: () => stats.level >= 10 }
];
// Global State
let midiAccess = null;
let midiInput = null;
let audioContext = null;
let isPlaying = false;
let isRecording = false;
let currentLesson = 'basic';
let bpm = 90;
let clickEnabled = false;
let metronomeInterval = null;
let sessionStartTime = null;
let recordedPattern = [];
let performanceHistory = [];
// Statistics
let stats = {
accuracy: 0,
combo: 0,
maxCombo: 0,
score: 0,
totalHits: 0,
perfectHits: 0,
goodHits: 0,
missedHits: 0,
sessionTime: 0,
sessionAccuracy: 0,
perfectRuns: 0,
maxBPM: 90,
drumsUsed: new Set(),
level: 1,
unlockedAchievements: new Set()
};
// Initialize
document.addEventListener('DOMContentLoaded', () => {
initParticles();
initLessons();
initAchievements();
initEventListeners();
initAudioContext();
updateStats();
startSessionTimer();
initPerformanceGraph();
});
function initParticles() {
const container = document.getElementById('particles');
for (let i = 0; i < 50; i++) {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.left = Math.random() * 100 + '%';
particle.style.animationDelay = Math.random() * 10 + 's';
particle.style.animationDuration = (10 + Math.random() * 10) + 's';
container.appendChild(particle);
}
}
function initLessons() {
const grid = document.getElementById('lessonGrid');
for (const [key, lesson] of Object.entries(LESSONS)) {
const card = document.createElement('div');
card.className = 'lesson-card';
card.dataset.lesson = key;
card.innerHTML = `
<div class="lesson-name">${lesson.icon} ${lesson.name}</div>
<div class="lesson-difficulty">
<span class="difficulty-stars">${'★'.repeat(lesson.difficulty)}${'☆'.repeat(5 - lesson.difficulty)}</span>
</div>
`;
card.addEventListener('click', () => selectLesson(key));
grid.appendChild(card);
}
selectLesson('basic');
}
function selectLesson(lessonKey) {
currentLesson = lessonKey;
document.querySelectorAll('.lesson-card').forEach(card => {
card.classList.toggle('active', card.dataset.lesson === lessonKey);
});
visualizePattern(LESSONS[lessonKey]);
}
function visualizePattern(lesson) {
const grid = document.getElementById('patternGrid');
const drums = [49, 51, 46, 48, 45, 43, 38, 41, 36];
const drumNames = ['Crash', 'Ride', 'Hi-Hat', 'Tom1', 'Tom2', 'Tom3', 'Snare', 'Floor', 'Kick'];
grid.innerHTML = '<div class="playhead" id="playhead"></div>';
drums.forEach((note, index) => {
const track = document.createElement('div');
track.className = 'pattern-track';
track.innerHTML = `<div class="track-label">${drumNames[index]}</div><div class="track-timeline" data-note="${note}"></div>`;
grid.appendChild(track);
const timeline = track.querySelector('.track-timeline');
lesson.patterns.forEach(beat => {
if (beat.drum === note) {
const marker = document.createElement('div');
marker.className = 'beat-marker';
marker.style.left = (beat.time / lesson.duration * 100) + '%';
timeline.appendChild(marker);
}
});
});
}
function initAchievements() {
const container = document.getElementById('achievements');
ACHIEVEMENTS.forEach(achievement => {
const elem = document.createElement('div');
elem.className = 'achievement';
elem.dataset.id = achievement.id;
elem.innerHTML = `
${achievement.icon}
<div class="achievement-tooltip">${achievement.name}</div>
`;
container.appendChild(elem);
});
}
function initEventListeners() {
document.getElementById('connectBtn').addEventListener('click', connectMIDI);
document.getElementById('startBtn').addEventListener('click', startSession);
document.getElementById('stopBtn').addEventListener('click', stopSession);
document.getElementById('freePlayBtn').addEventListener('click', toggleFreePlay);
document.getElementById('metronomeBtn').addEventListener('click', toggleMetronome);
document.getElementById('recordBtn').addEventListener('click', toggleRecording);
document.getElementById('playbackBtn').addEventListener('click', playbackRecording);
document.getElementById('tempoSlider').addEventListener('input', updateTempo);
document.getElementById('clickToggle').addEventListener('click', toggleClick);
// Keyboard support
document.addEventListener('keydown', handleKeyboard);
}
function initAudioContext() {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
}
async function connectMIDI() {
const loader = document.getElementById('loader');
loader.classList.add('active');
try {
midiAccess = await navigator.requestMIDIAccess();
const inputs = midiAccess.inputs.values();
for (let input of inputs) {
midiInput = input;
midiInput.onmidimessage = handleMIDIMessage;
break;
}
if (midiInput) {
updateConnectionStatus(true, midiInput.name || 'MIDI Device');
document.getElementById('startBtn').disabled = false;
playSuccessAnimation();
} else {
updateConnectionStatus(false, 'No device found');
}
} catch (error) {
console.error('MIDI connection failed:', error);
updateConnectionStatus(false, 'MIDI not supported');
} finally {
loader.classList.remove('active');
}
}
function updateConnectionStatus(connected, message) {
const status = document.getElementById('connectionStatus');
const statusText = document.getElementById('statusText');
const pulseDot = document.getElementById('pulseDot');
const connectBtn = document.getElementById('connectBtn');
if (connected) {
status.classList.add('connected');
pulseDot.classList.add('active');
statusText.textContent = `Connected: ${message}`;
connectBtn.textContent = 'Connected';
connectBtn.disabled = true;
} else {
status.classList.remove('connected');
pulseDot.classList.remove('active');
statusText.textContent = message;
}
}
function handleMIDIMessage(message) {
const [status, note, velocity] = message.data;
if (status >= 144 && status <= 159 && velocity > 0) {
handleDrumHit(note, velocity);
}
}
function handleDrumHit(note, velocity) {
// Visual feedback
const pad = document.querySelector(`.drum-pad[data-note="${note}"]`);
if (pad) {
pad.classList.add('active');
const velocityFill = pad.querySelector('.velocity-fill');
velocityFill.style.width = (velocity / 127 * 100) + '%';
setTimeout(() => {
pad.classList.remove('active');
velocityFill.style.width = '0%';
}, 300);
}
// Update statistics
stats.totalHits++;
stats.drumsUsed.add(note);
if (isRecording) {
recordedPattern.push({
time: performance.now(),
note: note,
velocity: velocity
});
}
if (isPlaying) {
checkAccuracy(note, velocity);
}
updateStats();
checkAchievements();
}
function checkAccuracy(note, velocity) {
// Check timing accuracy
const tolerance = 100; // ms
const now = performance.now();
// Implementation would check against expected pattern timing
// For demo, simulate accuracy
const random = Math.random();
if (random > 0.7) {
stats.perfectHits++;
stats.combo++;
stats.score += 100 * (1 + stats.combo * 0.1);
showHitFeedback('perfect');
} else if (random > 0.3) {
stats.goodHits++;
stats.combo++;
stats.score += 50;
showHitFeedback('good');
} else {
stats.missedHits++;
stats.combo = 0;
showHitFeedback('miss');
}
stats.maxCombo = Math.max(stats.maxCombo, stats.combo);
updateAccuracy();
}
function showHitFeedback(type) {
// Visual feedback for hit quality
const pad = document.querySelector('.drum-pad.active');
if (pad) {
pad.classList.remove('hit-perfect', 'hit-good', 'hit-miss');
pad.classList.add(`hit-${type}`);
}
}
function updateAccuracy() {
const total = stats.perfectHits + stats.goodHits + stats.missedHits;
if (total > 0) {
stats.accuracy = Math.round(((stats.perfectHits + stats.goodHits) / total) * 100);
stats.sessionAccuracy = stats.accuracy;
}
// Update accuracy chart
performanceHistory.push(stats.accuracy);
if (performanceHistory.length > 20) performanceHistory.shift();
updateAccuracyBars();
}
function updateAccuracyBars() {
const bars = document.getElementById('accuracyBars');
bars.innerHTML = '';
performanceHistory.forEach(value => {
const bar = document.createElement('div');
bar.className = 'mini-bar';
bar.style.height = value + '%';
bars.appendChild(bar);
});
}
function startSession() {
isPlaying = true;
sessionStartTime = Date.now();
document.getElementById('startBtn').disabled = true;
document.getElementById('stopBtn').disabled = false;
// Start pattern playback for selected lesson
playPattern(LESSONS[currentLesson]);
}
function stopSession() {
isPlaying = false;
document.getElementById('startBtn').disabled = false;
document.getElementById('stopBtn').disabled = true;
// Show session results
showSessionResults();
}