CODE:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sci-Fi Ring Sound Visualizer</title>
<style>
.scifiringvisualizer3914-body {
font-family: 'Arial', sans-serif;
background-color: #000;
color: #0ff;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
overflow: hidden;
}
.scifiringvisualizer3914-container {
display: flex;
flex-direction: row;
max-width: 1200px;
width: 100%;
}
.scifiringvisualizer3914-controls {
flex: 1;
padding: 20px;
background-color: rgba(0, 255, 255, 0.1);
border-radius: 10px;
margin-right: 20px;
z-index: 10;
}
.scifiringvisualizer3914-visualizer-container {
flex: 2;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(0, 255, 255, 0.05);
border-radius: 10px;
overflow: hidden;
position: relative;
}
.scifiringvisualizer3914-visualizer {
width: 100%;
height: 100%;
max-width: 800px;
max-height: 800px;
}
.scifiringvisualizer3914-button {
background-color: #0ff;
color: #000;
border: none;
padding: 10px 20px;
margin: 10px 0;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s;
}
.scifiringvisualizer3914-button:hover {
background-color: #00ffff80;
}
.scifiringvisualizer3914-file-input {
display: none;
}
.scifiringvisualizer3914-file-label {
background-color: #0ff;
color: #000;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
display: inline-block;
margin: 10px 0;
}
.scifiringvisualizer3914-description {
margin-bottom: 20px;
}
.scifiringvisualizer3914-instructions {
list-style-type: none;
padding: 0;
}
.scifiringvisualizer3914-instructions li {
margin-bottom: 10px;
}
@media (max-width: 768px) {
.scifiringvisualizer3914-container {
flex-direction: column;
}
.scifiringvisualizer3914-controls {
margin-right: 0;
margin-bottom: 20px;
}
}
</style>
</head>
<body class="scifiringvisualizer3914-body">
<div class="scifiringvisualizer3914-container">
<div class="scifiringvisualizer3914-controls">
<h1>Sci-Fi Ring Sound Visualizer</h1>
<p class="scifiringvisualizer3914-description">
Experience your music in a futuristic ring-shaped visual spectacle!
</p>
<ul class="scifiringvisualizer3914-instructions">
<li>1. Upload an audio file to start</li>
<li>2. Use Pause/Resume to control playback</li>
<li>3. Upload a new file to change the track</li>
</ul>
<input type="file" id="audioFile" accept="audio/*" class="scifiringvisualizer3914-file-input">
<label for="audioFile" class="scifiringvisualizer3914-file-label">Choose Audio File</label>
<button id="pauseResumeButton" class="scifiringvisualizer3914-button">Pause</button>
</div>
<div class="scifiringvisualizer3914-visualizer-container">
<canvas id="visualizer" class="scifiringvisualizer3914-visualizer"></canvas>
</div>
</div>
<script>
let audioContext;
let analyser;
let source;
let animationId;
let isPaused = false;
let audio;
const visualizer = document.getElementById('visualizer');
const ctx = visualizer.getContext('2d');
const audioFileInput = document.getElementById('audioFile');
const pauseResumeButton = document.getElementById('pauseResumeButton');
function initAudio(audioElement) {
if (audioContext) {
audioContext.close();
}
audioContext = new (window.AudioContext || window.webkitAudioContext)();
analyser = audioContext.createAnalyser();
source = audioContext.createMediaElementSource(audioElement);
source.connect(analyser);
analyser.connect(audioContext.destination);
analyser.fftSize = 512;
}
function visualize() {
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
function draw() {
if (isPaused) return;
animationId = requestAnimationFrame(draw);
analyser.getByteFrequencyData(dataArray);
ctx.clearRect(0, 0, visualizer.width, visualizer.height);
const centerX = visualizer.width / 2;
const centerY = visualizer.height / 2;
const radius = Math.min(centerX, centerY) * 0.8;
// Draw outer ring
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
ctx.strokeStyle = 'rgba(0, 255, 255, 0.2)';
ctx.lineWidth = 2;
ctx.stroke();
// Draw frequency bars
for (let i = 0; i < bufferLength; i++) {
const barHeight = dataArray[i] * 0.7;
const angle = (i / bufferLength) * 2 * Math.PI;
const x1 = centerX + Math.cos(angle) * radius;
const y1 = centerY + Math.sin(angle) * radius;
const x2 = centerX + Math.cos(angle) * (radius + barHeight);
const y2 = centerY + Math.sin(angle) * (radius + barHeight);
const hue = (i / bufferLength) * 360;
ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`;
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
// Add pulsating effect
const pulseRadius = 4 + Math.sin(Date.now() * 0.01 + i * 0.1) * 3;
ctx.fillStyle = `hsl(${hue}, 100%, 50%)`;
ctx.beginPath();
ctx.arc(x2, y2, pulseRadius, 0, 2 * Math.PI);
ctx.fill();
}
// Add rotating center effect
const rotationAngle = Date.now() * 0.002;
const innerRadius = radius * 0.3;
for (let i = 0; i < 5; i++) {
const angle = rotationAngle + (i * 2 * Math.PI / 5);
const x = centerX + Math.cos(angle) * innerRadius;
const y = centerY + Math.sin(angle) * innerRadius;
const size = 6 + Math.sin(Date.now() * 0.005 + i) * 3;
ctx.fillStyle = `rgba(0, 255, 255, ${0.8 - i * 0.15})`;
ctx.beginPath();
ctx.arc(x, y, size, 0, 2 * Math.PI);
ctx.fill();
}
// Add waveform
analyser.getByteTimeDomainData(dataArray);
ctx.beginPath();
ctx.strokeStyle = 'rgba(0, 255, 255, 0.5)';
ctx.lineWidth = 2;
for (let i = 0; i < bufferLength; i++) {
const x = (i / bufferLength) * visualizer.width;
const y = (dataArray[i] / 128.0) * (visualizer.height / 2);
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.stroke();
}
draw();
}
function resizeCanvas() {
const container = visualizer.parentElement;
visualizer.width = container.clientWidth;
visualizer.height = container.clientHeight;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
audioFileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (file) {
if (audio) {
audio.pause();
}
audio = new Audio(URL.createObjectURL(file));
initAudio(audio);
audio.play();
visualize();
pauseResumeButton.disabled = false;
isPaused = false;
pauseResumeButton.textContent = 'Pause';
}
});
pauseResumeButton.addEventListener('click', () => {
if (isPaused) {
audio.play();
audioContext.resume();
visualize();
pauseResumeButton.textContent = 'Pause';
} else {
audio.pause();
audioContext.suspend();
cancelAnimationFrame(animationId);
pauseResumeButton.textContent = 'Resume';
}
isPaused = !isPaused;
});
pauseResumeButton.disabled = true;
</script>
</body>
</html>