anycoder-81ca7f2b / index.html
r0cketboy's picture
Upload folder using huggingface_hub
63c6c50 verified
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ferrofluid Audio Visualizer</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #000;
font-family: 'Arial', sans-serif;
overflow: hidden;
color: white;
}
.header {
position: absolute;
top: 0;
width: 100%;
padding: 20px;
z-index: 100;
display: flex;
justify-content: space-between;
align-items: center;
background: rgba(0, 0, 0, 0.5);
}
.header h1 {
font-size: 1.5rem;
}
.anycoder-link {
color: #00ff88;
text-decoration: none;
font-size: 0.9rem;
transition: color 0.3s;
}
.anycoder-link:hover {
color: #00cc66;
}
.container {
position: relative;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
#visualizer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.controls {
position: absolute;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
z-index: 100;
background: rgba(0, 0, 0, 0.7);
padding: 20px;
border-radius: 10px;
display: flex;
flex-direction: column;
gap: 15px;
align-items: center;
min-width: 300px;
}
.file-input-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}
.file-input-label {
background: #00ff88;
color: black;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-weight: bold;
transition: background 0.3s;
}
.file-input-label:hover {
background: #00cc66;
}
#audioFile {
display: none;
}
.button-group {
display: flex;
gap: 10px;
}
button {
background: #333;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s;
}
button:hover {
background: #555;
}
button:disabled {
background: #222;
color: #666;
cursor: not-allowed;
}
.slider-container {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
}
.slider-container label {
min-width: 80px;
}
input[type="range"] {
flex: 1;
height: 5px;
background: #333;
outline: none;
border-radius: 5px;
}
.status {
font-size: 0.9rem;
color: #00ff88;
}
@media (max-width: 768px) {
.controls {
width: 90%;
min-width: unset;
}
.header h1 {
font-size: 1.2rem;
}
.button-group {
flex-direction: column;
width: 100%;
}
button {
width: 100%;
}
}
</style>
</head>
<body>
<div class="header">
<h1>Ferrofluid Audio Visualizer</h1>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" class="anycoder-link" target="_blank">Built with anycoder</a>
</div>
<div class="container">
<canvas id="visualizer"></canvas>
<div class="controls">
<div class="file-input-container">
<label for="audioFile" class="file-input-label">Audio Datei auswählen</label>
<input type="file" id="audioFile" accept="audio/*">
<div class="status" id="fileStatus">Keine Datei ausgewählt</div>
</div>
<div class="slider-container">
<label for="sensitivity">Empfindlichkeit:</label>
<input type="range" id="sensitivity" min="1" max="10" value="5">
</div>
<div class="slider-container">
<label for="smoothness">Glättung:</label>
<input type="range" id="smoothness" min="1" max="10" value="3">
</div>
<div class="button-group">
<button id="playBtn" disabled>Abspielen</button>
<button id="pauseBtn" disabled>Pause</button>
<button id="stopBtn" disabled>Stop</button>
</div>
</div>
</div>
<script>
// Canvas und Audio Kontext Setup
const canvas = document.getElementById('visualizer');
const ctx = canvas.getContext('2d');
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
// DOM Elemente
const audioFileInput = document.getElementById('audioFile');
const playBtn = document.getElementById('playBtn');
const pauseBtn = document.getElementById('pauseBtn');
const stopBtn = document.getElementById('stopBtn');
const sensitivitySlider = document.getElementById('sensitivity');
const smoothnessSlider = document.getElementById('smoothness');
const fileStatus = document.getElementById('fileStatus');
// Variablen
let audioSource;
let analyser;
let dataArray;
let bufferLength;
let animationId;
let isPlaying = false;
let audioBuffer;
let rotationX = 0;
let rotationY = 0;
// Canvas Größe anpassen
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
// Audio File Handling
audioFileInput.addEventListener('change', function(e) {
const file = e.target.files[0];
if (!file) return;
fileStatus.textContent = `Geladen: ${file.name}`;
const reader = new FileReader();
reader.onload = function(e) {
const arrayBuffer = e.target.result;
audioContext.decodeAudioData(arrayBuffer, function(buffer) {
audioBuffer = buffer;
setupAudioAnalysis();
playBtn.disabled = false;
}, function(e) {
console.error('Error decoding audio data', e);
fileStatus.textContent = 'Fehler beim Laden der Audio-Datei';
});
};
reader.readAsArrayBuffer(file);
});
// Audio Analyse Setup
function setupAudioAnalysis() {
if (analyser) {
analyser.disconnect();
}
analyser = audioContext.createAnalyser();
analyser.fftSize = 256;
bufferLength = analyser.frequencyBinCount;
dataArray = new Uint8Array(bufferLength);
// Smoothing-Faktor basierend auf Slider
analyser.smoothingTimeConstant = (11 - smoothnessSlider.value) / 10;
}
// Ferrofluid Visualisierung
function visualize() {
if (!isPlaying || !analyser) return;
analyser.getByteFrequencyData(dataArray);
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const baseRadius = Math.min(canvas.width, canvas.height) / 4;
const sensitivity = sensitivitySlider.value / 2;
ctx.save();
ctx.translate(centerX, centerY);
// 3D-Rotation basierend auf Audio-Daten
rotationX += dataArray[10] / 1000;
rotationY += dataArray[20] / 800;
ctx.rotateX(rotationX);
ctx.rotateY(rotationY);
// Ferrofluid-ähnliche Blob-Form zeichnen
ctx.beginPath();
for (let i = 0; i < bufferLength; i++) {
const amplitude = dataArray[i] / 255;
const angle = (i / bufferLength) * Math.PI * 2;
const radius = baseRadius + (amplitude * baseRadius * sensitivity);
const x = Math.cos(angle) * radius;
const y = Math.sin(angle) * radius;
// Z-Koordinate für 3D-Effekt
const z = Math.sin(angle * 2) * amplitude * 50;
if (i === 0) {
ctx.moveTo(x, y + z);
} else {
ctx.lineTo(x, y + z);
}
}
ctx.closePath();
// Ferrofluid-ähnlicher Gradient
const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, baseRadius * 2);
gradient.addColorStop(0, 'rgba(0, 255, 136, 0.8)');
gradient.addColorStop(0.5, 'rgba(0, 200, 100, 0.6)');
gradient.addColorStop(1, 'rgba(0, 100, 50, 0.4)');
ctx.fillStyle = gradient;
ctx.fill();
// Glänzende Highlights
ctx.strokeStyle = 'rgba(255, 255, 255, 0.3)';
ctx.lineWidth = 2;
ctx.stroke();
ctx.restore();
animationId = requestAnimationFrame(visualize);
}
// Steuerung
playBtn.addEventListener('click', function() {
if (!audioBuffer) return;
if (audioContext.state === 'suspended') {
audioContext.resume();
}
if (audioSource) {
audioSource.stop();
}
audioSource = audioContext.createBufferSource();
audioSource.buffer = audioBuffer;
audioSource.connect(analyser);
analyser.connect(audioContext.destination);
audioSource.start(0);
isPlaying = true;
playBtn.disabled = true;
pauseBtn.disabled = false;
stopBtn.disabled = false;
visualize();
});
pauseBtn.addEventListener('click', function() {
if (audioContext.state === 'running') {
audioContext.suspend().then(function() {
isPlaying = false;
playBtn.disabled = false;
pauseBtn.disabled = true;
cancelAnimationFrame(animationId);
});
}
});
stopBtn.addEventListener('click', function() {
if (audioSource) {
audioSource.stop();
audioSource = null;
}
isPlaying = false;
playBtn.disabled = false;
pauseBtn.disabled = true;
stopBtn.disabled = true;
cancelAnimationFrame(animationId);
// Canvas zurücksetzen
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
});
// Slider-Event-Listener
smoothnessSlider.addEventListener('input', function() {
if (analyser) {
analyser.smoothingTimeConstant = (11 - this.value) / 10;
}
});
// Maus-Interaktion für zusätzliche Rotation
let mouseX = 0;
let mouseY = 0;
canvas.addEventListener('mousemove', function(e) {
if (!isPlaying) return;
mouseX = (e.clientX / canvas.width - 0.5) * 2;
mouseY = (e.clientY / canvas.height - 0.5) * 2;
});
// Touch-Interaktion für Mobile
canvas.addEventListener('touchmove', function(e) {
if (!isPlaying) return;
e.preventDefault();
const touch = e.touches[0];
mouseX = (touch.clientX / canvas.width - 0.5) * 2;
mouseY = (touch.clientY / canvas.height - 0.5) * 2;
}, { passive: false });
// Initialen schwarzen Hintergrund zeichnen
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
</script>
</body>
</html>