dj-console / index.html
melromyeah's picture
Add 2 files
2ef2c35 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Virtual DJ Mixer</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* Custom styles for turntables and knobs */
.turntable {
background: radial-gradient(circle, #111 30%, #222 70%, #333 100%);
box-shadow: inset 0 0 20px rgba(0,0,0,0.8), 0 5px 15px rgba(0,0,0,0.5);
}
.vinyl {
background: radial-gradient(circle, #111 10%, transparent 10.5%),
repeating-radial-gradient(circle, #111 0, #111 1px, #333 1px, #333 2px);
background-size: 100% 100%, 100% 100%;
animation: spin 5s linear infinite;
animation-play-state: paused;
}
.knob {
background: radial-gradient(circle at 30% 30%, #555, #222);
box-shadow: 0 2px 5px rgba(0,0,0,0.5);
}
.knob-mark {
background: linear-gradient(to bottom, #ff0, #f80);
box-shadow: 0 0 3px rgba(255,255,0,0.8);
}
.fader-track {
background: linear-gradient(to right, #333, #666, #333);
}
.fader-knob {
background: linear-gradient(to bottom, #555, #999, #555);
box-shadow: 0 2px 5px rgba(0,0,0,0.5);
}
.button {
transition: all 0.1s;
}
.button:active {
transform: translateY(2px);
box-shadow: 0 1px 2px rgba(0,0,0,0.3);
}
.active-effect {
background: linear-gradient(to bottom, #4ade80, #16a34a);
box-shadow: 0 0 10px rgba(74, 222, 128, 0.7);
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* Custom scrollbar for track list */
.track-list::-webkit-scrollbar {
width: 8px;
}
.track-list::-webkit-scrollbar-track {
background: #333;
}
.track-list::-webkit-scrollbar-thumb {
background: #666;
border-radius: 4px;
}
.track-list::-webkit-scrollbar-thumb:hover {
background: #888;
}
</style>
</head>
<body class="bg-gray-900 text-white min-h-screen flex flex-col items-center py-8 px-4">
<header class="text-center mb-8">
<h1 class="text-4xl font-bold bg-gradient-to-r from-purple-500 to-blue-500 bg-clip-text text-transparent">VIRTUAL DJ MIXER</h1>
<p class="text-gray-400 mt-2">Spin and mix like a pro</p>
</header>
<main class="w-full max-w-6xl bg-gray-800 rounded-xl shadow-2xl overflow-hidden">
<!-- Main mixer section -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 p-6">
<!-- Left deck -->
<div class="bg-gray-700 rounded-xl p-4 shadow-lg">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold text-purple-300">DECK A</h2>
<div class="flex space-x-2">
<button class="button bg-gray-600 hover:bg-gray-500 w-8 h-8 rounded-full flex items-center justify-center">
<i class="fas fa-sync-alt text-sm"></i>
</button>
<button class="button bg-gray-600 hover:bg-gray-500 w-8 h-8 rounded-full flex items-center justify-center">
<i class="fas fa-random text-sm"></i>
</button>
</div>
</div>
<div class="turntable relative w-full aspect-square rounded-full flex items-center justify-center mb-4">
<div class="vinyl absolute w-3/4 h-3/4 rounded-full" id="vinyl-a"></div>
<div class="absolute w-1/6 h-1/6 rounded-full bg-gray-900 z-10"></div>
</div>
<div class="flex justify-between mb-4">
<button class="button bg-red-500 hover:bg-red-600 px-4 py-2 rounded-lg font-medium" id="play-a">
<i class="fas fa-play mr-2"></i> Play
</button>
<button class="button bg-gray-600 hover:bg-gray-500 px-4 py-2 rounded-lg font-medium">
<i class="fas fa-folder-open mr-2"></i> Load
</button>
</div>
<div class="mb-4">
<div class="flex justify-between items-center mb-2">
<span class="text-sm font-medium">Tempo</span>
<span class="text-xs bg-gray-600 px-2 py-1 rounded" id="bpm-a">128 BPM</span>
</div>
<input type="range" min="80" max="160" value="128" class="w-full h-2 bg-gray-600 rounded-lg appearance-none cursor-pointer" id="tempo-a">
</div>
<div class="grid grid-cols-3 gap-4 mb-4">
<div class="flex flex-col items-center">
<div class="knob relative w-12 h-12 rounded-full mb-2 flex items-center justify-center" id="high-a">
<div class="knob-mark absolute w-2 h-4 rounded-full top-0"></div>
</div>
<span class="text-xs">High</span>
</div>
<div class="flex flex-col items-center">
<div class="knob relative w-12 h-12 rounded-full mb-2 flex items-center justify-center" id="mid-a">
<div class="knob-mark absolute w-2 h-4 rounded-full top-0"></div>
</div>
<span class="text-xs">Mid</span>
</div>
<div class="flex flex-col items-center">
<div class="knob relative w-12 h-12 rounded-full mb-2 flex items-center justify-center" id="low-a">
<div class="knob-mark absolute w-2 h-4 rounded-full top-0"></div>
</div>
<span class="text-xs">Low</span>
</div>
</div>
<div class="flex justify-between items-center">
<span class="text-sm font-medium">Volume</span>
<span class="text-xs bg-gray-600 px-2 py-1 rounded" id="volume-value-a">75%</span>
</div>
<input type="range" min="0" max="100" value="75" class="w-full h-2 bg-gray-600 rounded-lg appearance-none cursor-pointer mb-2" id="volume-a">
<div class="flex justify-between items-center">
<span class="text-sm font-medium">Filter</span>
<span class="text-xs bg-gray-600 px-2 py-1 rounded" id="filter-value-a">50%</span>
</div>
<input type="range" min="0" max="100" value="50" class="w-full h-2 bg-gray-600 rounded-lg appearance-none cursor-pointer" id="filter-a">
</div>
<!-- Mixer section -->
<div class="bg-gray-700 rounded-xl p-4 shadow-lg flex flex-col">
<h2 class="text-xl font-bold text-center text-blue-300 mb-6">MIXER</h2>
<div class="flex-grow flex flex-col justify-between">
<div class="flex justify-center mb-8">
<div class="fader-track relative w-8 h-48 rounded-lg mx-4">
<div class="fader-knob absolute w-12 h-6 rounded-lg -left-2 cursor-pointer" id="fader-a" style="top: 30%;"></div>
</div>
<div class="fader-track relative w-8 h-48 rounded-lg mx-4">
<div class="fader-knob absolute w-12 h-6 rounded-lg -left-2 cursor-pointer" id="fader-b" style="top: 70%;"></div>
</div>
</div>
<div class="mb-8">
<div class="flex justify-between items-center mb-2">
<span class="text-sm font-medium">Crossfader</span>
<span class="text-xs bg-gray-600 px-2 py-1 rounded" id="crossfader-value">Center</span>
</div>
<div class="fader-track relative w-full h-4 rounded-lg">
<div class="fader-knob absolute w-6 h-8 rounded-lg -top-2 cursor-pointer" id="crossfader" style="left: 50%;"></div>
</div>
</div>
<div class="grid grid-cols-4 gap-2 mb-4">
<button class="button effect-btn bg-gray-600 hover:bg-gray-500 py-2 rounded-lg text-xs" data-effect="echo">
<i class="fas fa-wave-square mr-1"></i> Echo
</button>
<button class="button effect-btn bg-gray-600 hover:bg-gray-500 py-2 rounded-lg text-xs" data-effect="flanger">
<i class="fas fa-water mr-1"></i> Flanger
</button>
<button class="button effect-btn bg-gray-600 hover:bg-gray-500 py-2 rounded-lg text-xs" data-effect="reverb">
<i class="fas fa-mountain mr-1"></i> Reverb
</button>
<button class="button effect-btn bg-gray-600 hover:bg-gray-500 py-2 rounded-lg text-xs" data-effect="phaser">
<i class="fas fa-slash mr-1"></i> Phaser
</button>
</div>
<div class="flex justify-center">
<button class="button bg-yellow-500 hover:bg-yellow-600 px-4 py-2 rounded-lg font-medium">
<i class="fas fa-record-vinyl mr-2"></i> Record Mix
</button>
</div>
</div>
</div>
<!-- Right deck -->
<div class="bg-gray-700 rounded-xl p-4 shadow-lg">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold text-green-300">DECK B</h2>
<div class="flex space-x-2">
<button class="button bg-gray-600 hover:bg-gray-500 w-8 h-8 rounded-full flex items-center justify-center">
<i class="fas fa-sync-alt text-sm"></i>
</button>
<button class="button bg-gray-600 hover:bg-gray-500 w-8 h-8 rounded-full flex items-center justify-center">
<i class="fas fa-random text-sm"></i>
</button>
</div>
</div>
<div class="turntable relative w-full aspect-square rounded-full flex items-center justify-center mb-4">
<div class="vinyl absolute w-3/4 h-3/4 rounded-full" id="vinyl-b"></div>
<div class="absolute w-1/6 h-1/6 rounded-full bg-gray-900 z-10"></div>
</div>
<div class="flex justify-between mb-4">
<button class="button bg-red-500 hover:bg-red-600 px-4 py-2 rounded-lg font-medium" id="play-b">
<i class="fas fa-play mr-2"></i> Play
</button>
<button class="button bg-gray-600 hover:bg-gray-500 px-4 py-2 rounded-lg font-medium">
<i class="fas fa-folder-open mr-2"></i> Load
</button>
</div>
<div class="mb-4">
<div class="flex justify-between items-center mb-2">
<span class="text-sm font-medium">Tempo</span>
<span class="text-xs bg-gray-600 px-2 py-1 rounded" id="bpm-b">125 BPM</span>
</div>
<input type="range" min="80" max="160" value="125" class="w-full h-2 bg-gray-600 rounded-lg appearance-none cursor-pointer" id="tempo-b">
</div>
<div class="grid grid-cols-3 gap-4 mb-4">
<div class="flex flex-col items-center">
<div class="knob relative w-12 h-12 rounded-full mb-2 flex items-center justify-center" id="high-b">
<div class="knob-mark absolute w-2 h-4 rounded-full top-0"></div>
</div>
<span class="text-xs">High</span>
</div>
<div class="flex flex-col items-center">
<div class="knob relative w-12 h-12 rounded-full mb-2 flex items-center justify-center" id="mid-b">
<div class="knob-mark absolute w-2 h-4 rounded-full top-0"></div>
</div>
<span class="text-xs">Mid</span>
</div>
<div class="flex flex-col items-center">
<div class="knob relative w-12 h-12 rounded-full mb-2 flex items-center justify-center" id="low-b">
<div class="knob-mark absolute w-2 h-4 rounded-full top-0"></div>
</div>
<span class="text-xs">Low</span>
</div>
</div>
<div class="flex justify-between items-center">
<span class="text-sm font-medium">Volume</span>
<span class="text-xs bg-gray-600 px-2 py-1 rounded" id="volume-value-b">65%</span>
</div>
<input type="range" min="0" max="100" value="65" class="w-full h-2 bg-gray-600 rounded-lg appearance-none cursor-pointer mb-2" id="volume-b">
<div class="flex justify-between items-center">
<span class="text-sm font-medium">Filter</span>
<span class="text-xs bg-gray-600 px-2 py-1 rounded" id="filter-value-b">50%</span>
</div>
<input type="range" min="0" max="100" value="50" class="w-full h-2 bg-gray-600 rounded-lg appearance-none cursor-pointer" id="filter-b">
</div>
</div>
<!-- Track library section -->
<div class="bg-gray-900 p-6">
<h2 class="text-xl font-bold text-center text-pink-300 mb-4">TRACK LIBRARY</h2>
<div class="relative">
<input type="text" placeholder="Search tracks..." class="w-full bg-gray-800 text-white px-4 py-2 rounded-lg mb-4 focus:outline-none focus:ring-2 focus:ring-purple-500">
<i class="fas fa-search absolute right-4 top-3 text-gray-400"></i>
</div>
<div class="track-list h-48 overflow-y-auto bg-gray-800 rounded-lg p-2">
<div class="grid grid-cols-1 gap-2">
<div class="flex items-center justify-between p-2 hover:bg-gray-700 rounded-lg cursor-pointer">
<div class="flex items-center">
<div class="w-10 h-10 bg-purple-900 rounded mr-3 flex items-center justify-center">
<i class="fas fa-music"></i>
</div>
<div>
<p class="font-medium">Summer Vibes</p>
<p class="text-xs text-gray-400">House • 128 BPM</p>
</div>
</div>
<span class="text-xs bg-gray-700 px-2 py-1 rounded">3:42</span>
</div>
<div class="flex items-center justify-between p-2 hover:bg-gray-700 rounded-lg cursor-pointer">
<div class="flex items-center">
<div class="w-10 h-10 bg-blue-900 rounded mr-3 flex items-center justify-center">
<i class="fas fa-music"></i>
</div>
<div>
<p class="font-medium">Midnight Groove</p>
<p class="text-xs text-gray-400">Techno • 132 BPM</p>
</div>
</div>
<span class="text-xs bg-gray-700 px-2 py-1 rounded">4:15</span>
</div>
<div class="flex items-center justify-between p-2 hover:bg-gray-700 rounded-lg cursor-pointer">
<div class="flex items-center">
<div class="w-10 h-10 bg-green-900 rounded mr-3 flex items-center justify-center">
<i class="fas fa-music"></i>
</div>
<div>
<p class="font-medium">Bass Drop</p>
<p class="text-xs text-gray-400">Drum & Bass • 174 BPM</p>
</div>
</div>
<span class="text-xs bg-gray-700 px-2 py-1 rounded">5:22</span>
</div>
<div class="flex items-center justify-between p-2 hover:bg-gray-700 rounded-lg cursor-pointer">
<div class="flex items-center">
<div class="w-10 h-10 bg-yellow-900 rounded mr-3 flex items-center justify-center">
<i class="fas fa-music"></i>
</div>
<div>
<p class="font-medium">Disco Fever</p>
<p class="text-xs text-gray-400">Disco • 120 BPM</p>
</div>
</div>
<span class="text-xs bg-gray-700 px-2 py-1 rounded">3:58</span>
</div>
<div class="flex items-center justify-between p-2 hover:bg-gray-700 rounded-lg cursor-pointer">
<div class="flex items-center">
<div class="w-10 h-10 bg-red-900 rounded mr-3 flex items-center justify-center">
<i class="fas fa-music"></i>
</div>
<div>
<p class="font-medium">Urban Flow</p>
<p class="text-xs text-gray-400">Hip Hop • 95 BPM</p>
</div>
</div>
<span class="text-xs bg-gray-700 px-2 py-1 rounded">4:30</span>
</div>
</div>
</div>
</div>
</main>
<footer class="mt-8 text-center text-gray-500 text-sm">
<p>Virtual DJ Mixer - Spin your favorite tracks</p>
<p class="mt-1">© 2023 DJ Web App. All rights reserved.</p>
</footer>
<script>
// DOM elements
const vinylA = document.getElementById('vinyl-a');
const vinylB = document.getElementById('vinyl-b');
const playButtonA = document.getElementById('play-a');
const playButtonB = document.getElementById('play-b');
const tempoSliderA = document.getElementById('tempo-a');
const tempoSliderB = document.getElementById('tempo-b');
const bpmDisplayA = document.getElementById('bpm-a');
const bpmDisplayB = document.getElementById('bpm-b');
const volumeSliderA = document.getElementById('volume-a');
const volumeSliderB = document.getElementById('volume-b');
const volumeDisplayA = document.getElementById('volume-value-a');
const volumeDisplayB = document.getElementById('volume-value-b');
const filterSliderA = document.getElementById('filter-a');
const filterSliderB = document.getElementById('filter-b');
const filterDisplayA = document.getElementById('filter-value-a');
const filterDisplayB = document.getElementById('filter-value-b');
const faderA = document.getElementById('fader-a');
const faderB = document.getElementById('fader-b');
const crossfader = document.getElementById('crossfader');
const crossfaderDisplay = document.getElementById('crossfader-value');
const effectButtons = document.querySelectorAll('.effect-btn');
// State
let isPlayingA = false;
let isPlayingB = false;
let activeEffect = null;
// Initialize knobs
function initKnob(knobId, initialValue = 50) {
const knob = document.getElementById(knobId);
let isDragging = false;
let startY = 0;
let startRotation = initialValue * 3.6; // Convert percentage to degrees
// Set initial rotation
knob.style.transform = `rotate(${startRotation}deg)`;
knob.addEventListener('mousedown', (e) => {
isDragging = true;
startY = e.clientY;
startRotation = parseInt(knob.style.transform.replace('rotate(', '').replace('deg)', '')) || 0;
e.preventDefault();
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const deltaY = startY - e.clientY;
let newRotation = startRotation + deltaY;
// Limit rotation between 0 and 360 degrees
newRotation = Math.max(0, Math.min(360, newRotation));
knob.style.transform = `rotate(${newRotation}deg)`;
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
}
// Initialize faders
function initFader(faderId, vertical = true, callback = null) {
const fader = document.getElementById(faderId);
let isDragging = false;
let startPos = 0;
let startTop = 0;
fader.addEventListener('mousedown', (e) => {
isDragging = true;
startPos = vertical ? e.clientY : e.clientX;
startTop = vertical ? parseInt(fader.style.top) || 0 : parseInt(fader.style.left) || 0;
e.preventDefault();
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const delta = vertical ? e.clientY - startPos : e.clientX - startPos;
let newPos = vertical ? startTop + delta : startTop + delta;
// Limit position within track
if (vertical) {
const trackHeight = fader.parentElement.offsetHeight;
const faderHeight = fader.offsetHeight;
newPos = Math.max(0, Math.min(trackHeight - faderHeight, newPos));
fader.style.top = `${newPos}px`;
// Calculate percentage (0-100)
const percent = 100 - Math.round((newPos / (trackHeight - faderHeight)) * 100);
if (callback) callback(percent);
} else {
const trackWidth = fader.parentElement.offsetWidth;
const faderWidth = fader.offsetWidth;
newPos = Math.max(0, Math.min(trackWidth - faderWidth, newPos));
fader.style.left = `${newPos}px`;
// Calculate percentage (0-100)
const percent = Math.round((newPos / (trackWidth - faderWidth)) * 100);
if (callback) callback(percent);
}
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
}
// Initialize all knobs
initKnob('high-a', 70);
initKnob('mid-a', 50);
initKnob('low-a', 60);
initKnob('high-b', 65);
initKnob('mid-b', 55);
initKnob('low-b', 70);
// Initialize faders
initFader('fader-a', true, (percent) => {
// Update volume based on fader position
console.log(`Deck A fader: ${percent}%`);
});
initFader('fader-b', true, (percent) => {
// Update volume based on fader position
console.log(`Deck B fader: ${percent}%`);
});
initFader('crossfader', false, (percent) => {
// Update crossfader position
let position;
if (percent < 40) {
position = "Left";
} else if (percent > 60) {
position = "Right";
} else {
position = "Center";
}
crossfaderDisplay.textContent = position;
});
// Play/pause functionality
playButtonA.addEventListener('click', () => {
isPlayingA = !isPlayingA;
if (isPlayingA) {
vinylA.style.animationPlayState = 'running';
playButtonA.innerHTML = '<i class="fas fa-pause mr-2"></i> Pause';
playButtonA.classList.remove('bg-red-500', 'hover:bg-red-600');
playButtonA.classList.add('bg-yellow-500', 'hover:bg-yellow-600');
} else {
vinylA.style.animationPlayState = 'paused';
playButtonA.innerHTML = '<i class="fas fa-play mr-2"></i> Play';
playButtonA.classList.remove('bg-yellow-500', 'hover:bg-yellow-600');
playButtonA.classList.add('bg-red-500', 'hover:bg-red-600');
}
});
playButtonB.addEventListener('click', () => {
isPlayingB = !isPlayingB;
if (isPlayingB) {
vinylB.style.animationPlayState = 'running';
playButtonB.innerHTML = '<i class="fas fa-pause mr-2"></i> Pause';
playButtonB.classList.remove('bg-red-500', 'hover:bg-red-600');
playButtonB.classList.add('bg-yellow-500', 'hover:bg-yellow-600');
} else {
vinylB.style.animationPlayState = 'paused';
playButtonB.innerHTML = '<i class="fas fa-play mr-2"></i> Play';
playButtonB.classList.remove('bg-yellow-500', 'hover:bg-yellow-600');
playButtonB.classList.add('bg-red-500', 'hover:bg-red-600');
}
});
// Tempo control
tempoSliderA.addEventListener('input', () => {
const bpm = tempoSliderA.value;
bpmDisplayA.textContent = `${bpm} BPM`;
// Adjust vinyl speed based on BPM
const speed = 128 / bpm; // Normalize to 128 BPM
vinylA.style.animationDuration = `${5 * speed}s`;
});
tempoSliderB.addEventListener('input', () => {
const bpm = tempoSliderB.value;
bpmDisplayB.textContent = `${bpm} BPM`;
// Adjust vinyl speed based on BPM
const speed = 128 / bpm; // Normalize to 128 BPM
vinylB.style.animationDuration = `${5 * speed}s`;
});
// Volume control
volumeSliderA.addEventListener('input', () => {
const volume = volumeSliderA.value;
volumeDisplayA.textContent = `${volume}%`;
});
volumeSliderB.addEventListener('input', () => {
const volume = volumeSliderB.value;
volumeDisplayB.textContent = `${volume}%`;
});
// Filter control
filterSliderA.addEventListener('input', () => {
const filter = filterSliderA.value;
filterDisplayA.textContent = `${filter}%`;
});
filterSliderB.addEventListener('input', () => {
const filter = filterSliderB.value;
filterDisplayB.textContent = `${filter}%`;
});
// Effect buttons
effectButtons.forEach(button => {
button.addEventListener('click', () => {
const effect = button.dataset.effect;
if (activeEffect === effect) {
// Turn off effect
button.classList.remove('active-effect');
button.classList.add('bg-gray-600', 'hover:bg-gray-500');
activeEffect = null;
} else {
// Turn off any active effect first
effectButtons.forEach(btn => {
btn.classList.remove('active-effect');
btn.classList.add('bg-gray-600', 'hover:bg-gray-500');
});
// Turn on this effect
button.classList.remove('bg-gray-600', 'hover:bg-gray-500');
button.classList.add('active-effect');
activeEffect = effect;
}
console.log(`Effect: ${activeEffect || 'None'}`);
});
});
// Track selection
const tracks = document.querySelectorAll('.track-list > div > div');
tracks.forEach(track => {
track.addEventListener('click', () => {
// Remove active class from all tracks
tracks.forEach(t => t.classList.remove('bg-gray-700', 'ring-2', 'ring-purple-500'));
// Add active class to clicked track
track.classList.add('bg-gray-700', 'ring-2', 'ring-purple-500');
// Get track info
const trackName = track.querySelector('p:first-child').textContent;
const bpmText = track.querySelector('p:last-child').textContent;
const bpm = bpmText.match(/\d+/)[0];
console.log(`Selected track: ${trackName} (${bpm} BPM)`);
});
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body>
</html>