Add 2 files
Browse files- README.md +7 -5
- index.html +640 -19
README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 1 |
---
|
| 2 |
+
title: drums
|
| 3 |
+
emoji: 🐳
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: blue
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
| 8 |
+
tags:
|
| 9 |
+
- deepsite
|
| 10 |
---
|
| 11 |
|
| 12 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
index.html
CHANGED
|
@@ -1,19 +1,640 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>CRYSTAL DRUMS | MIDI Pattern Generator</title>
|
| 7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 9 |
+
<script src="https://cdn.jsdelivr.net/npm/tone@14.7.77/build/Tone.min.js"></script>
|
| 10 |
+
<script src="https://cdn.jsdelivr.net/npm/@magenta/music@1.23.1/es6/core.js"></script>
|
| 11 |
+
<style>
|
| 12 |
+
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&family=Syncopate:wght@400;700&display=swap');
|
| 13 |
+
|
| 14 |
+
body {
|
| 15 |
+
font-family: 'Montserrat', sans-serif;
|
| 16 |
+
background-color: #0f0f12;
|
| 17 |
+
color: #e0e0e0;
|
| 18 |
+
min-height: 100vh;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
.title-font {
|
| 22 |
+
font-family: 'Syncopate', sans-serif;
|
| 23 |
+
text-transform: uppercase;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
.glitch-box {
|
| 27 |
+
position: relative;
|
| 28 |
+
overflow: hidden;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
.glitch-box::before {
|
| 32 |
+
content: "";
|
| 33 |
+
position: absolute;
|
| 34 |
+
top: 0;
|
| 35 |
+
left: 0;
|
| 36 |
+
width: 100%;
|
| 37 |
+
height: 100%;
|
| 38 |
+
background: linear-gradient(
|
| 39 |
+
90deg,
|
| 40 |
+
transparent 0%,
|
| 41 |
+
rgba(255, 255, 255, 0.03) 20%,
|
| 42 |
+
rgba(255, 255, 255, 0.05) 50%,
|
| 43 |
+
rgba(255, 255, 255, 0.03) 80%,
|
| 44 |
+
transparent 100%
|
| 45 |
+
);
|
| 46 |
+
z-index: 1;
|
| 47 |
+
pointer-events: none;
|
| 48 |
+
animation: glitch 5s infinite alternate;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
@keyframes glitch {
|
| 52 |
+
0% { transform: translateX(-100%); }
|
| 53 |
+
15% { transform: translateX(0); opacity: 1; }
|
| 54 |
+
30% { opacity: 0.3; }
|
| 55 |
+
45% { opacity: 1; }
|
| 56 |
+
100% { transform: translateX(100%); }
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
.pixel-grid {
|
| 60 |
+
display: grid;
|
| 61 |
+
gap: 2px;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
.pixel {
|
| 65 |
+
width: 100%;
|
| 66 |
+
aspect-ratio: 1;
|
| 67 |
+
background-color: #1e1e25;
|
| 68 |
+
transition: all 0.1s ease;
|
| 69 |
+
position: relative;
|
| 70 |
+
overflow: hidden;
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
.pixel::before {
|
| 74 |
+
content: "";
|
| 75 |
+
position: absolute;
|
| 76 |
+
top: 0;
|
| 77 |
+
left: 0;
|
| 78 |
+
width: 100%;
|
| 79 |
+
height: 100%;
|
| 80 |
+
background: radial-gradient(circle, transparent 0%, rgba(0,0,0,0.3) 100%);
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
.pixel.active {
|
| 84 |
+
background-color: #7b2dff;
|
| 85 |
+
box-shadow: 0 0 10px #7b2dffaa;
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
.pixel.active.kick {
|
| 89 |
+
background-color: #ff2d6d;
|
| 90 |
+
box-shadow: 0 0 10px #ff2d6daa;
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
.pixel.active.snare {
|
| 94 |
+
background-color: #2dfff3;
|
| 95 |
+
box-shadow: 0 0 10px #2dfff3aa;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
.pixel.active.hihat {
|
| 99 |
+
background-color: #f3ff2d;
|
| 100 |
+
box-shadow: 0 0 10px #f3ff2daa;
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
.pixel.active.perc {
|
| 104 |
+
background-color: #2dff88;
|
| 105 |
+
box-shadow: 0 0 10px #2dff88aa;
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
.vhs-effect {
|
| 109 |
+
position: relative;
|
| 110 |
+
overflow: hidden;
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
.vhs-effect::after {
|
| 114 |
+
content: "";
|
| 115 |
+
position: absolute;
|
| 116 |
+
top: 0;
|
| 117 |
+
left: 0;
|
| 118 |
+
width: 100%;
|
| 119 |
+
height: 100%;
|
| 120 |
+
background: linear-gradient(0deg, transparent 0%, rgba(255,255,255,0.01) 1%, transparent 2%, transparent 100%);
|
| 121 |
+
pointer-events: none;
|
| 122 |
+
animation: vhsScan 0.1s infinite;
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
@keyframes vhsScan {
|
| 126 |
+
0% { transform: translateY(-100%); }
|
| 127 |
+
100% { transform: translateY(100%); }
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
.grid-line {
|
| 131 |
+
position: absolute;
|
| 132 |
+
top: 0;
|
| 133 |
+
height: 100%;
|
| 134 |
+
width: 0.5px;
|
| 135 |
+
background-color: rgba(255,255,255,0.05);
|
| 136 |
+
pointer-events: none;
|
| 137 |
+
}
|
| 138 |
+
</style>
|
| 139 |
+
</head>
|
| 140 |
+
<body class="bg-gray-900">
|
| 141 |
+
<!-- VHS overlay -->
|
| 142 |
+
<div class="fixed inset-0 vhs-effect pointer-events-none z-50 opacity-20"></div>
|
| 143 |
+
|
| 144 |
+
<header class="bg-black bg-opacity-80 border-b border-gray-800 backdrop-blur-md sticky top-0 z-40">
|
| 145 |
+
<div class="container mx-auto px-4 py-4">
|
| 146 |
+
<div class="flex justify-between items-center">
|
| 147 |
+
<div class="flex items-center space-x-3">
|
| 148 |
+
<i class="fas fa-drum text-2xl text-purple-500"></i>
|
| 149 |
+
<h1 class="text-xl md:text-2xl font-bold title-font text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-pink-500 tracking-wider">
|
| 150 |
+
CRYSTAL DRUMS
|
| 151 |
+
</h1>
|
| 152 |
+
</div>
|
| 153 |
+
<div class="flex space-x-3">
|
| 154 |
+
<button id="infoBtn" class="bg-gray-800 hover:bg-gray-700 text-gray-300 px-3 py-1 rounded-md text-sm font-medium transition-colors">
|
| 155 |
+
<i class="fas fa-question-circle mr-1"></i> Info
|
| 156 |
+
</button>
|
| 157 |
+
<button id="randomizeBtn" class="bg-purple-900 hover:bg-purple-800 text-purple-100 px-3 py-1 rounded-md text-sm font-medium transition-colors">
|
| 158 |
+
<i class="fas fa-random mr-1"></i> Randomize
|
| 159 |
+
</button>
|
| 160 |
+
</div>
|
| 161 |
+
</div>
|
| 162 |
+
</div>
|
| 163 |
+
</header>
|
| 164 |
+
|
| 165 |
+
<main class="container mx-auto px-4 py-8">
|
| 166 |
+
<div class="max-w-6xl mx-auto">
|
| 167 |
+
<div class="glitch-box bg-gray-900 border border-gray-800 rounded-lg p-6 mb-8">
|
| 168 |
+
<h2 class="text-lg font-semibold text-pink-400 mb-4">
|
| 169 |
+
<i class="fas fa-sliders-h mr-2"></i> CRYSTAL SOUNDS
|
| 170 |
+
</h2>
|
| 171 |
+
|
| 172 |
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
| 173 |
+
<div class="space-y-2">
|
| 174 |
+
<label class="block text-xs font-medium text-gray-400">CRUNCH</label>
|
| 175 |
+
<input type="range" min="0" max="100" value="70" class="w-full h-1 bg-gray-700 rounded-lg appearance-none cursor-pointer accent-purple-500">
|
| 176 |
+
</div>
|
| 177 |
+
<div class="space-y-2">
|
| 178 |
+
<label class="block text-xs font-medium text-gray-400">PITCH</label>
|
| 179 |
+
<input type="range" min="-12" max="12" value="-3" class="w-full h-1 bg-gray-700 rounded-lg appearance-none cursor-pointer accent-pink-500">
|
| 180 |
+
</div>
|
| 181 |
+
<div class="space-y-2">
|
| 182 |
+
<label class="block text-xs font-medium text-gray-400">STUTTER</label>
|
| 183 |
+
<input type="range" min="0" max="100" value="15" class="w-full h-1 bg-gray-700 rounded-lg appearance-none cursor-pointer accent-blue-400">
|
| 184 |
+
</div>
|
| 185 |
+
<div class="space-y-2">
|
| 186 |
+
<label class="block text-xs font-medium text-gray-400">TAPE HISS</label>
|
| 187 |
+
<input type="range" min="0" max="100" value="30" class="w-full h-1 bg-gray-700 rounded-lg appearance-none cursor-pointer accent-green-400">
|
| 188 |
+
</div>
|
| 189 |
+
</div>
|
| 190 |
+
|
| 191 |
+
<div class="grid grid-cols-4 gap-4">
|
| 192 |
+
<div class="space-y-2">
|
| 193 |
+
<div class="flex justify-between items-center">
|
| 194 |
+
<label class="text-xs font-medium text-gray-400">BPM</label>
|
| 195 |
+
<span class="text-xs font-mono text-gray-300">140</span>
|
| 196 |
+
</div>
|
| 197 |
+
<input type="range" min="80" max="200" value="140" id="bpmSlider" class="w-full h-1 bg-gray-700 rounded-lg appearance-none cursor-pointer accent-yellow-400">
|
| 198 |
+
</div>
|
| 199 |
+
<div class="space-y-2">
|
| 200 |
+
<div class="flex justify-between items-center">
|
| 201 |
+
<label class="text-xs font-medium text-gray-400">STEPS</label>
|
| 202 |
+
<span class="text-xs font-mono text-gray-300">16</span>
|
| 203 |
+
</div>
|
| 204 |
+
<input type="range" min="8" max="32" value="16" step="8" id="stepsSlider" class="w-full h-1 bg-gray-700 rounded-lg appearance-none cursor-pointer accent-red-400">
|
| 205 |
+
</div>
|
| 206 |
+
<div class="space-y-2">
|
| 207 |
+
<label class="block text-xs font-medium text-gray-400">SWING</label>
|
| 208 |
+
<input type="range" min="0" max="50" value="5" class="w-full h-1 bg-gray-700 rounded-lg appearance-none cursor-pointer accent-teal-400">
|
| 209 |
+
</div>
|
| 210 |
+
<div class="space-y-2">
|
| 211 |
+
<label class="block text-xs font-medium text-gray-400">VOLUME</label>
|
| 212 |
+
<input type="range" min="0" max="100" value="70" class="w-full h-1 bg-gray-700 rounded-lg appearance-none cursor-pointer accent-gray-400">
|
| 213 |
+
</div>
|
| 214 |
+
</div>
|
| 215 |
+
</div>
|
| 216 |
+
|
| 217 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
|
| 218 |
+
<div class="glitch-box bg-gray-900 border border-gray-800 rounded-lg p-6">
|
| 219 |
+
<h3 class="text-lg font-semibold text-pink-400 mb-4">
|
| 220 |
+
<i class="fas fa-music mr-2"></i> DRUM GRID
|
| 221 |
+
</h3>
|
| 222 |
+
|
| 223 |
+
<div id="drumGridContainer" class="relative">
|
| 224 |
+
<!-- Grid lines will be added via JS -->
|
| 225 |
+
<div id="drumGrid" class="pixel-grid grid grid-rows-4 grid-cols-16 gap-0.5"></div>
|
| 226 |
+
</div>
|
| 227 |
+
|
| 228 |
+
<div class="flex justify-between mt-4 text-xs text-gray-400">
|
| 229 |
+
<span>KICK</span>
|
| 230 |
+
<span>SNARE</span>
|
| 231 |
+
<span>HIHAT</span>
|
| 232 |
+
<span>PERC</span>
|
| 233 |
+
</div>
|
| 234 |
+
</div>
|
| 235 |
+
|
| 236 |
+
<div class="glitch-box bg-gray-900 border border-gray-800 rounded-lg p-6">
|
| 237 |
+
<h3 class="text-lg font-semibold text-blue-400 mb-4">
|
| 238 |
+
<i class="fas fa-compact-disc mr-2"></i> PATTERN PLAYER
|
| 239 |
+
</h3>
|
| 240 |
+
|
| 241 |
+
<div class="flex items-center space-x-4 mb-6">
|
| 242 |
+
<button id="playBtn" class="bg-purple-600 hover:bg-purple-500 text-white px-6 py-2 rounded-md font-medium transition-colors">
|
| 243 |
+
<i class="fas fa-play mr-2"></i> Play
|
| 244 |
+
</button>
|
| 245 |
+
<button id="stopBtn" class="bg-gray-700 hover:bg-gray-600 text-gray-200 px-4 py-2 rounded-md font-medium transition-colors">
|
| 246 |
+
<i class="fas fa-stop mr-2"></i> Stop
|
| 247 |
+
</button>
|
| 248 |
+
<button id="exportBtn" class="bg-pink-600 hover:bg-pink-500 text-white px-4 py-2 rounded-md font-medium transition-colors">
|
| 249 |
+
<i class="fas fa-download mr-2"></i> Export MIDI
|
| 250 |
+
</button>
|
| 251 |
+
</div>
|
| 252 |
+
|
| 253 |
+
<div class="space-y-4">
|
| 254 |
+
<div class="flex items-center">
|
| 255 |
+
<div class="w-2 h-2 rounded-full bg-purple-500 mr-2"></div>
|
| 256 |
+
<div class="flex-grow h-2 bg-gray-700 rounded-full">
|
| 257 |
+
<div id="playhead" class="h-2 bg-purple-400 rounded-full w-0"></div>
|
| 258 |
+
</div>
|
| 259 |
+
</div>
|
| 260 |
+
<div id="patternInfo" class="text-sm text-gray-400 font-mono">
|
| 261 |
+
<p>Pattern: <span class="text-gray-300">unmodified</span></p>
|
| 262 |
+
<p>BPM: <span class="text-gray-300">140</span></p>
|
| 263 |
+
<p>Steps: <span class="text-gray-300">16</span></p>
|
| 264 |
+
</div>
|
| 265 |
+
</div>
|
| 266 |
+
</div>
|
| 267 |
+
</div>
|
| 268 |
+
|
| 269 |
+
<div class="glitch-box bg-gray-900 border border-gray-800 rounded-lg p-6">
|
| 270 |
+
<h3 class="text-lg font-semibold text-yellow-400 mb-4">
|
| 271 |
+
<i class="fas fa-flask mr-2"></i> CRYSTAL LAB
|
| 272 |
+
</h3>
|
| 273 |
+
|
| 274 |
+
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-4">
|
| 275 |
+
<button class="bg-gray-800 hover:bg-gray-700 text-gray-300 px-4 py-2 rounded-md flex items-center justify-center space-x-2">
|
| 276 |
+
<i class="fas fa-trash"></i>
|
| 277 |
+
<span>Clear All</span>
|
| 278 |
+
</button>
|
| 279 |
+
<button class="bg-gray-800 hover:bg-gray-700 text-gray-300 px-4 py-2 rounded-md flex items-center justify-center space-x-2">
|
| 280 |
+
<i class="fas fa-random"></i>
|
| 281 |
+
<span>Random Fill</span>
|
| 282 |
+
</button>
|
| 283 |
+
<button class="bg-gray-800 hover:bg-gray-700 text-gray-300 px-4 py-2 rounded-md flex items-center justify-center space-x-2">
|
| 284 |
+
<i class="fas fa-undo"></i>
|
| 285 |
+
<span>Undo</span>
|
| 286 |
+
</button>
|
| 287 |
+
<button class="bg-gray-800 hover:bg-gray-700 text-gray-300 px-4 py-2 rounded-md flex items-center justify-center space-x-2">
|
| 288 |
+
<i class="fas fa-redo"></i>
|
| 289 |
+
<span>Redo</span>
|
| 290 |
+
</button>
|
| 291 |
+
</div>
|
| 292 |
+
|
| 293 |
+
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
| 294 |
+
<button class="bg-purple-900 hover:bg-purple-800 text-purple-100 px-4 py-2 rounded-md flex items-center justify-center space-x-2">
|
| 295 |
+
<i class="fas fa-bolt"></i>
|
| 296 |
+
<span>Glitch Pattern</span>
|
| 297 |
+
</button>
|
| 298 |
+
<button class="bg-pink-900 hover:bg-pink-800 text-pink-100 px-4 py-2 rounded-md flex items-center justify-center space-x-2">
|
| 299 |
+
<i class="fas fa-forward"></i>
|
| 300 |
+
<span>Shift Right</span>
|
| 301 |
+
</button>
|
| 302 |
+
<button class="bg-blue-900 hover:bg-blue-800 text-blue-100 px-4 py-2 rounded-md flex items-center justify-center space-x-2">
|
| 303 |
+
<i class="fas fa-backward"></i>
|
| 304 |
+
<span>Shift Left</span>
|
| 305 |
+
</button>
|
| 306 |
+
<button class="bg-green-900 hover:bg-green-800 text-green-100 px-4 py-2 rounded-md flex items-center justify-center space-x-2">
|
| 307 |
+
<i class="fas fa-expand"></i>
|
| 308 |
+
<span>Double Time</span>
|
| 309 |
+
</button>
|
| 310 |
+
</div>
|
| 311 |
+
</div>
|
| 312 |
+
</div>
|
| 313 |
+
</main>
|
| 314 |
+
|
| 315 |
+
<footer class="bg-black bg-opacity-80 border-t border-gray-800 py-6 mt-10">
|
| 316 |
+
<div class="container mx-auto px-4">
|
| 317 |
+
<div class="flex flex-col md:flex-row justify-between items-center">
|
| 318 |
+
<div class="mb-4 md:mb-0">
|
| 319 |
+
<div class="flex items-center space-x-2">
|
| 320 |
+
<i class="fas fa-drum text-xl text-purple-500"></i>
|
| 321 |
+
<h2 class="text-xl font-bold title-font tracking-wider text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-pink-500">
|
| 322 |
+
CRYSTAL DRUMS
|
| 323 |
+
</h2>
|
| 324 |
+
</div>
|
| 325 |
+
<p class="text-gray-500 mt-2 text-xs">Inspired by the chaotic beauty of Crystal Castles</p>
|
| 326 |
+
</div>
|
| 327 |
+
<div class="flex space-x-4">
|
| 328 |
+
<a href="#" class="text-gray-500 hover:text-purple-400 transition-colors">
|
| 329 |
+
<i class="fab fa-github"></i>
|
| 330 |
+
</a>
|
| 331 |
+
<a href="#" class="text-gray-500 hover:text-pink-400 transition-colors">
|
| 332 |
+
<i class="fab fa-twitter"></i>
|
| 333 |
+
</a>
|
| 334 |
+
<a href="#" class="text-gray-500 hover:text-blue-400 transition-colors">
|
| 335 |
+
<i class="fas fa-envelope"></i>
|
| 336 |
+
</a>
|
| 337 |
+
</div>
|
| 338 |
+
</div>
|
| 339 |
+
<div class="border-t border-gray-800 mt-6 pt-6 text-center text-gray-600 text-xs">
|
| 340 |
+
<p>© 2023 CRYSTAL DRUMS. Not affiliated with Crystal Castles.</p>
|
| 341 |
+
</div>
|
| 342 |
+
</div>
|
| 343 |
+
</footer>
|
| 344 |
+
|
| 345 |
+
<script>
|
| 346 |
+
// Initialize Tone.js
|
| 347 |
+
const sampler = new Tone.Sampler({
|
| 348 |
+
urls: {
|
| 349 |
+
A1: "kick.wav",
|
| 350 |
+
A2: "snare.wav",
|
| 351 |
+
A3: "hihat.wav",
|
| 352 |
+
A4: "perc.wav"
|
| 353 |
+
},
|
| 354 |
+
release: 1,
|
| 355 |
+
baseUrl: "https://tonejs.github.io/audio/drum-samples/",
|
| 356 |
+
}).toDestination();
|
| 357 |
+
|
| 358 |
+
// Constants
|
| 359 |
+
const DRUM_ROWS = 4;
|
| 360 |
+
let currentCols = 16;
|
| 361 |
+
let bpm = 140;
|
| 362 |
+
let isPlaying = false;
|
| 363 |
+
let playheadInterval;
|
| 364 |
+
let currentStep = 0;
|
| 365 |
+
let patternData = [];
|
| 366 |
+
|
| 367 |
+
// DOM Elements
|
| 368 |
+
const drumGrid = document.getElementById('drumGrid');
|
| 369 |
+
const drumGridContainer = document.getElementById('drumGridContainer');
|
| 370 |
+
const playBtn = document.getElementById('playBtn');
|
| 371 |
+
const stopBtn = document.getElementById('stopBtn');
|
| 372 |
+
const exportBtn = document.getElementById('exportBtn');
|
| 373 |
+
const randomizeBtn = document.getElementById('randomizeBtn');
|
| 374 |
+
const infoBtn = document.getElementById('infoBtn');
|
| 375 |
+
const playhead = document.getElementById('playhead');
|
| 376 |
+
const patternInfo = document.getElementById('patternInfo');
|
| 377 |
+
const bpmSlider = document.getElementById('bpmSlider');
|
| 378 |
+
const stepsSlider = document.getElementById('stepsSlider');
|
| 379 |
+
|
| 380 |
+
// Create the drum grid
|
| 381 |
+
function createDrumGrid(rows, cols) {
|
| 382 |
+
drumGrid.innerHTML = '';
|
| 383 |
+
|
| 384 |
+
// Clear existing grid lines
|
| 385 |
+
const oldLines = document.querySelectorAll('.grid-line');
|
| 386 |
+
oldLines.forEach(line => line.remove());
|
| 387 |
+
|
| 388 |
+
// Add vertical grid lines
|
| 389 |
+
for (let i = 0; i <= cols; i++) {
|
| 390 |
+
const line = document.createElement('div');
|
| 391 |
+
line.className = 'grid-line';
|
| 392 |
+
line.style.left = `${(i / cols) * 100}%`;
|
| 393 |
+
drumGridContainer.appendChild(line);
|
| 394 |
+
}
|
| 395 |
+
|
| 396 |
+
// Create grid cells (pixels)
|
| 397 |
+
for (let row = 0; row < rows; row++) {
|
| 398 |
+
for (let col = 0; col < cols; col++) {
|
| 399 |
+
const pixel = document.createElement('div');
|
| 400 |
+
pixel.className = 'pixel';
|
| 401 |
+
|
| 402 |
+
if (row === 0) pixel.classList.add('kick-cell');
|
| 403 |
+
if (row === 1) pixel.classList.add('snare-cell');
|
| 404 |
+
if (row === 2) pixel.classList.add('hihat-cell');
|
| 405 |
+
if (row === 3) pixel.classList.add('perc-cell');
|
| 406 |
+
|
| 407 |
+
pixel.dataset.row = row;
|
| 408 |
+
pixel.dataset.col = col;
|
| 409 |
+
|
| 410 |
+
pixel.addEventListener('click', togglePixel);
|
| 411 |
+
drumGrid.appendChild(pixel);
|
| 412 |
+
}
|
| 413 |
+
}
|
| 414 |
+
|
| 415 |
+
// Initialize empty pattern data
|
| 416 |
+
patternData = Array.from({ length: rows }, () =>
|
| 417 |
+
Array.from({ length: cols }, () => false)
|
| 418 |
+
);
|
| 419 |
+
|
| 420 |
+
// Update grid layout
|
| 421 |
+
drumGrid.style.gridTemplateRows = `repeat(${rows}, 1fr)`;
|
| 422 |
+
drumGrid.style.gridTemplateColumns = `repeat(${cols}, 1fr)`;
|
| 423 |
+
}
|
| 424 |
+
|
| 425 |
+
// Toggle pixel state
|
| 426 |
+
function togglePixel(e) {
|
| 427 |
+
const pixel = e.target;
|
| 428 |
+
const row = parseInt(pixel.dataset.row);
|
| 429 |
+
const col = parseInt(pixel.dataset.col);
|
| 430 |
+
|
| 431 |
+
// Toggle the pixel state
|
| 432 |
+
patternData[row][col] = !patternData[row][col];
|
| 433 |
+
|
| 434 |
+
// Update visual state
|
| 435 |
+
pixel.classList.toggle('active');
|
| 436 |
+
|
| 437 |
+
// Add color class based on row (drum type)
|
| 438 |
+
if (row === 0) pixel.classList.toggle('kick');
|
| 439 |
+
if (row === 1) pixel.classList.toggle('snare');
|
| 440 |
+
if (row === 2) pixel.classList.toggle('hihat');
|
| 441 |
+
if (row === 3) pixel.classList.toggle('perc');
|
| 442 |
+
}
|
| 443 |
+
|
| 444 |
+
// Randomize the pattern
|
| 445 |
+
function randomizePattern() {
|
| 446 |
+
patternData = patternData.map(row =>
|
| 447 |
+
row.map(() => Math.random() < 0.2)
|
| 448 |
+
);
|
| 449 |
+
|
| 450 |
+
updateGridVisuals();
|
| 451 |
+
}
|
| 452 |
+
|
| 453 |
+
// Update grid visuals based on pattern data
|
| 454 |
+
function updateGridVisuals() {
|
| 455 |
+
const pixels = document.querySelectorAll('.pixel');
|
| 456 |
+
pixels.forEach(pixel => {
|
| 457 |
+
const row = parseInt(pixel.dataset.row);
|
| 458 |
+
const col = parseInt(pixel.dataset.col);
|
| 459 |
+
|
| 460 |
+
pixel.classList.remove('active', 'kick', 'snare', 'hihat', 'perc');
|
| 461 |
+
|
| 462 |
+
if (patternData[row][col]) {
|
| 463 |
+
pixel.classList.add('active');
|
| 464 |
+
if (row === 0) pixel.classList.add('kick');
|
| 465 |
+
if (row === 1) pixel.classList.add('snare');
|
| 466 |
+
if (row === 2) pixel.classList.add('hihat');
|
| 467 |
+
if (row === 3) pixel.classList.add('perc');
|
| 468 |
+
}
|
| 469 |
+
});
|
| 470 |
+
}
|
| 471 |
+
|
| 472 |
+
// Play the pattern
|
| 473 |
+
function playPattern() {
|
| 474 |
+
if (isPlaying) return;
|
| 475 |
+
|
| 476 |
+
isPlaying = true;
|
| 477 |
+
Tone.start();
|
| 478 |
+
Tone.getDestination().volume.value = -10;
|
| 479 |
+
|
| 480 |
+
const stepInterval = 60 / bpm / 4; // quarter notes
|
| 481 |
+
let step = 0;
|
| 482 |
+
|
| 483 |
+
const loop = new Tone.Loop((time) => {
|
| 484 |
+
currentStep = step;
|
| 485 |
+
|
| 486 |
+
// Update playhead position
|
| 487 |
+
const playheadPos = (step / currentCols) * 100;
|
| 488 |
+
playhead.style.width = `${playheadPos}%`;
|
| 489 |
+
|
| 490 |
+
// Play each row that has a hit at this step
|
| 491 |
+
for (let row = 0; row < DRUM_ROWS; row++) {
|
| 492 |
+
if (patternData[row][step]) {
|
| 493 |
+
playDrum(row, time);
|
| 494 |
+
}
|
| 495 |
+
}
|
| 496 |
+
|
| 497 |
+
step = (step + 1) % currentCols;
|
| 498 |
+
}, stepInterval).start(0);
|
| 499 |
+
|
| 500 |
+
Tone.Transport.bpm.value = bpm;
|
| 501 |
+
Tone.Transport.start();
|
| 502 |
+
|
| 503 |
+
playBtn.innerHTML = '<i class="fas fa-pause mr-2"></i> Pause';
|
| 504 |
+
playBtn.classList.remove('bg-purple-600', 'hover:bg-purple-500');
|
| 505 |
+
playBtn.classList.add('bg-yellow-600', 'hover:bg-yellow-500');
|
| 506 |
+
}
|
| 507 |
+
|
| 508 |
+
// Play drum sound based on row
|
| 509 |
+
function playDrum(row, time) {
|
| 510 |
+
let note;
|
| 511 |
+
switch (row) {
|
| 512 |
+
case 0: note = "A1"; break; // Kick
|
| 513 |
+
case 1: note = "A2"; break; // Snare
|
| 514 |
+
case 2: note = "A3"; break; // Hihat
|
| 515 |
+
case 3: note = "A4"; break; // Perc
|
| 516 |
+
}
|
| 517 |
+
|
| 518 |
+
// Add some random pitch variation to humanize
|
| 519 |
+
const pitchVariation = (Math.random() * 0.2) - 0.1;
|
| 520 |
+
sampler.triggerAttackRelease(note, "8n", time, 0.3 + pitchVariation);
|
| 521 |
+
}
|
| 522 |
+
|
| 523 |
+
// Stop playback
|
| 524 |
+
function stopPlayback() {
|
| 525 |
+
if (!isPlaying) return;
|
| 526 |
+
|
| 527 |
+
isPlaying = false;
|
| 528 |
+
Tone.Transport.stop();
|
| 529 |
+
currentStep = 0;
|
| 530 |
+
playhead.style.width = '0%';
|
| 531 |
+
|
| 532 |
+
playBtn.innerHTML = '<i class="fas fa-play mr-2"></i> Play';
|
| 533 |
+
playBtn.classList.remove('bg-yellow-600', 'hover:bg-yellow-500');
|
| 534 |
+
playBtn.classList.add('bg-purple-600', 'hover:bg-purple-500');
|
| 535 |
+
}
|
| 536 |
+
|
| 537 |
+
// Export as MIDI
|
| 538 |
+
function exportMIDI() {
|
| 539 |
+
// Convert pattern to Magenta.js NoteSequence
|
| 540 |
+
const sequence = {
|
| 541 |
+
notes: [],
|
| 542 |
+
quantizationInfo: { stepsPerQuarter: 4 },
|
| 543 |
+
totalQuantizedSteps: currentCols,
|
| 544 |
+
timeSignatures: [{ time: 4, numerator: 4, denominator: 4 }],
|
| 545 |
+
tempos: [{ qpm: bpm }],
|
| 546 |
+
ticksPerQuarter: 220,
|
| 547 |
+
};
|
| 548 |
+
|
| 549 |
+
// Add drum hits
|
| 550 |
+
for (let step = 0; step < currentCols; step++) {
|
| 551 |
+
for (let row = 0; row < DRUM_ROWS; row++) {
|
| 552 |
+
if (patternData[row][step]) {
|
| 553 |
+
let pitch;
|
| 554 |
+
let instrument = 0;
|
| 555 |
+
|
| 556 |
+
switch (row) {
|
| 557 |
+
case 0: pitch = 36; instrument = 1; break; // Kick
|
| 558 |
+
case 1: pitch = 38; instrument = 2; break; // Snare
|
| 559 |
+
case 2: pitch = 42; instrument = 3; break; // Hihat
|
| 560 |
+
case 3: pitch = 45; instrument = 4; break; // Perc
|
| 561 |
+
}
|
| 562 |
+
|
| 563 |
+
sequence.notes.push({
|
| 564 |
+
pitch: pitch,
|
| 565 |
+
quantizedStartStep: step,
|
| 566 |
+
quantizedEndStep: step + 1,
|
| 567 |
+
instrument: instrument,
|
| 568 |
+
isDrum: true,
|
| 569 |
+
velocity: 80
|
| 570 |
+
});
|
| 571 |
+
}
|
| 572 |
+
}
|
| 573 |
+
}
|
| 574 |
+
|
| 575 |
+
// Convert to MIDI and trigger download
|
| 576 |
+
const midiBytes = core.sequenceProtoToMidi(sequence);
|
| 577 |
+
const blob = new Blob([midiBytes], { type: 'audio/midi' });
|
| 578 |
+
const url = URL.createObjectURL(blob);
|
| 579 |
+
|
| 580 |
+
const a = document.createElement('a');
|
| 581 |
+
a.href = url;
|
| 582 |
+
a.download = `crystal-drums-${Date.now()}.mid`;
|
| 583 |
+
document.body.appendChild(a);
|
| 584 |
+
a.click();
|
| 585 |
+
|
| 586 |
+
setTimeout(() => {
|
| 587 |
+
document.body.removeChild(a);
|
| 588 |
+
URL.revokeObjectURL(url);
|
| 589 |
+
}, 100);
|
| 590 |
+
}
|
| 591 |
+
|
| 592 |
+
// Event listeners
|
| 593 |
+
playBtn.addEventListener('click', () => {
|
| 594 |
+
if (isPlaying) {
|
| 595 |
+
stopPlayback();
|
| 596 |
+
} else {
|
| 597 |
+
playPattern();
|
| 598 |
+
}
|
| 599 |
+
});
|
| 600 |
+
|
| 601 |
+
stopBtn.addEventListener('click', stopPlayback);
|
| 602 |
+
exportBtn.addEventListener('click', exportMIDI);
|
| 603 |
+
randomizeBtn.addEventListener('click', randomizePattern);
|
| 604 |
+
|
| 605 |
+
infoBtn.addEventListener('click', () => {
|
| 606 |
+
alert("CRYSTAL DRUMS is a MIDI drum pattern generator inspired by the raw, glitchy beats of Crystal Castles.\n\nCreate patterns by clicking on the grid, then play them back with randomized pitch and timing for that authentic lo-fi sound.\n\nExport your patterns as MIDI files to use in your DAW.");
|
| 607 |
+
});
|
| 608 |
+
|
| 609 |
+
bpmSlider.addEventListener('input', (e) => {
|
| 610 |
+
bpm = parseInt(e.target.value);
|
| 611 |
+
updateBpmDisplay();
|
| 612 |
+
});
|
| 613 |
+
|
| 614 |
+
stepsSlider.addEventListener('input', (e) => {
|
| 615 |
+
currentCols = parseInt(e.target.value);
|
| 616 |
+
createDrumGrid(DRUM_ROWS, currentCols);
|
| 617 |
+
updateStepDisplay();
|
| 618 |
+
});
|
| 619 |
+
|
| 620 |
+
// Update displayed BPM
|
| 621 |
+
function updateBpmDisplay() {
|
| 622 |
+
const bpmDisplay = patternInfo.querySelector('p:nth-child(2) span');
|
| 623 |
+
bpmDisplay.textContent = bpm;
|
| 624 |
+
}
|
| 625 |
+
|
| 626 |
+
// Update displayed steps
|
| 627 |
+
function updateStepDisplay() {
|
| 628 |
+
const stepsDisplay = patternInfo.querySelector('p:nth-child(3) span');
|
| 629 |
+
stepsDisplay.textContent = currentCols;
|
| 630 |
+
}
|
| 631 |
+
|
| 632 |
+
// Initialize
|
| 633 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 634 |
+
createDrumGrid(DRUM_ROWS, currentCols);
|
| 635 |
+
updateBpmDisplay();
|
| 636 |
+
updateStepDisplay();
|
| 637 |
+
});
|
| 638 |
+
</script>
|
| 639 |
+
<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 <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - <a href="https://enzostvs-deepsite.hf.space?remix=drxzh/drums" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
|
| 640 |
+
</html>
|