text-wave / index.html
MarkTheArtist's picture
Create create an animated. Visualization of twith multiple. Lines repeating the same text. move in a wave, multiple controls for wave form, color, speed, etc - Initial Deployment
a9f7415 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Text Wave Visualizer</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js"></script>
<style>
.wave-container {
perspective: 1000px;
transform-style: preserve-3d;
}
.wave-line {
position: relative;
transform-origin: center;
will-change: transform;
}
.controls-panel {
backdrop-filter: blur(10px);
background-color: rgba(255, 255, 255, 0.15);
}
@media (max-width: 768px) {
.controls-panel {
position: static;
width: 100%;
margin-top: 20px;
}
}
</style>
</head>
<body class="bg-gradient-to-br from-gray-900 to-indigo-900 min-h-screen text-white overflow-x-hidden">
<div class="container mx-auto px-4 py-8">
<header class="text-center mb-8">
<h1 class="text-4xl md:text-5xl font-bold mb-2 bg-clip-text text-transparent bg-gradient-to-r from-purple-400 to-pink-600">
Text Wave Visualizer
</h1>
<p class="text-gray-300 max-w-2xl mx-auto">
Create mesmerizing wave patterns with customizable text, colors, and motion parameters
</p>
</header>
<div class="flex flex-col lg:flex-row gap-8">
<!-- Main Visualization Area -->
<div class="flex-1 relative h-[500px] overflow-hidden rounded-xl border border-gray-700 shadow-2xl bg-gray-800/30">
<div id="waveContainer" class="wave-container w-full h-full flex flex-col justify-center items-center p-4"></div>
</div>
<!-- Controls Panel -->
<div class="controls-panel lg:w-80 p-6 rounded-xl border border-gray-700 shadow-lg">
<h2 class="text-xl font-semibold mb-4 flex items-center gap-2">
<i class="fas fa-sliders-h"></i> Wave Controls
</h2>
<div class="space-y-6">
<!-- Text Input -->
<div>
<label class="block text-sm font-medium mb-1">Text Content</label>
<input type="text" id="textInput" value="WAVE"
class="w-full px-3 py-2 bg-gray-800/50 border border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
</div>
<!-- Color Controls -->
<div>
<label class="block text-sm font-medium mb-1">Color Scheme</label>
<div class="flex gap-2">
<button data-color="gradient-purple" class="color-btn flex-1 h-8 rounded bg-gradient-to-r from-purple-500 to-pink-500"></button>
<button data-color="gradient-blue" class="color-btn flex-1 h-8 rounded bg-gradient-to-r from-blue-400 to-teal-400"></button>
<button data-color="gradient-orange" class="color-btn flex-1 h-8 rounded bg-gradient-to-r from-orange-400 to-red-500"></button>
<button data-color="gradient-green" class="color-btn flex-1 h-8 rounded bg-gradient-to-r from-green-400 to-cyan-400"></button>
</div>
</div>
<!-- Wave Type -->
<div>
<label class="block text-sm font-medium mb-1">Wave Type</label>
<select id="waveType" class="w-full px-3 py-2 bg-gray-800/50 border border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500">
<option value="sine">Sine Wave</option>
<option value="square">Square Wave</option>
<option value="triangle">Triangle Wave</option>
<option value="sawtooth">Sawtooth Wave</option>
</select>
</div>
<!-- Numerical Controls -->
<div class="grid grid-cols-2 gap-4">
<div>
<label for="amplitude" class="block text-sm font-medium mb-1">Amplitude</label>
<input type="range" id="amplitude" min="5" max="100" value="30"
class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer">
<span id="amplitudeValue" class="text-xs text-gray-400">30</span>
</div>
<div>
<label for="frequency" class="block text-sm font-medium mb-1">Frequency</label>
<input type="range" id="frequency" min="0.1" max="2" step="0.1" value="0.5"
class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer">
<span id="frequencyValue" class="text-xs text-gray-400">0.5</span>
</div>
<div>
<label for="speed" class="block text-sm font-medium mb-1">Speed</label>
<input type="range" id="speed" min="1" max="10" value="3"
class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer">
<span id="speedValue" class="text-xs text-gray-400">3</span>
</div>
<div>
<label for="lineCount" class="block text-sm font-medium mb-1">Lines</label>
<input type="range" id="lineCount" min="3" max="20" value="8"
class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer">
<span id="lineCountValue" class="text-xs text-gray-400">8</span>
</div>
</div>
<!-- Advanced Toggles -->
<div class="space-y-2">
<div class="flex items-center">
<input type="checkbox" id="randomizePhase" class="w-4 h-4 text-purple-600 bg-gray-800 border-gray-700 rounded focus:ring-purple-500">
<label for="randomizePhase" class="ml-2 text-sm">Random Phase</label>
</div>
<div class="flex items-center">
<input type="checkbox" id="show3D" checked class="w-4 h-4 text-purple-600 bg-gray-800 border-gray-700 rounded focus:ring-purple-500">
<label for="show3D" class="ml-2 text-sm">3D Perspective</label>
</div>
</div>
<!-- Preset Buttons -->
<div class="flex gap-2">
<button id="resetBtn" class="flex-1 py-2 px-3 bg-gray-700 hover:bg-gray-600 rounded-md text-sm transition">
<i class="fas fa-redo mr-1"></i> Reset
</button>
<button id="randomBtn" class="flex-1 py-2 px-3 bg-purple-700 hover:bg-purple-600 rounded-md text-sm transition">
<i class="fas fa-random mr-1"></i> Randomize
</button>
</div>
</div>
</div>
</div>
<div class="mt-8 text-center text-gray-400 text-sm">
<p>Click and drag to rotate the wave in 3D space</p>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// DOM Elements
const waveContainer = document.getElementById('waveContainer');
const textInput = document.getElementById('textInput');
const waveType = document.getElementById('waveType');
const amplitude = document.getElementById('amplitude');
const frequency = document.getElementById('frequency');
const speed = document.getElementById('speed');
const lineCount = document.getElementById('lineCount');
const randomizePhase = document.getElementById('randomizePhase');
const show3D = document.getElementById('show3D');
const resetBtn = document.getElementById('resetBtn');
const randomBtn = document.getElementById('randomBtn');
const colorBtns = document.querySelectorAll('.color-btn');
// Display values
const amplitudeValue = document.getElementById('amplitudeValue');
const frequencyValue = document.getElementById('frequencyValue');
const speedValue = document.getElementById('speedValue');
const lineCountValue = document.getElementById('lineCountValue');
// State
let animationId;
let time = 0;
let lines = [];
let isDragging = false;
let lastX = 0;
let lastY = 0;
let rotateX = 0;
let rotateY = 0;
let currentColorScheme = 'gradient-purple';
// Initialize
createWaveLines();
updateDisplayValues();
setupEventListeners();
animate();
// Functions
function createWaveLines() {
waveContainer.innerHTML = '';
lines = [];
const count = parseInt(lineCount.value);
const text = textInput.value || 'WAVE';
for (let i = 0; i < count; i++) {
const line = document.createElement('div');
line.className = 'wave-line text-center mb-1';
line.style.fontSize = `${Math.max(12, 24 - (i * 0.5))}px`;
line.textContent = text.repeat(15);
// Set initial color based on position
updateLineColor(line, i, count);
waveContainer.appendChild(line);
lines.push({
element: line,
phase: randomizePhase.checked ? Math.random() * Math.PI * 2 : 0
});
}
}
function updateLineColor(line, index, total) {
const ratio = index / total;
if (currentColorScheme === 'gradient-purple') {
line.style.background = `linear-gradient(90deg, rgba(192, 132, 252, ${1 - ratio * 0.7}), rgba(236, 72, 153, ${1 - ratio * 0.7}))`;
line.style.webkitBackgroundClip = 'text';
line.style.backgroundClip = 'text';
line.style.color = 'transparent';
}
else if (currentColorScheme === 'gradient-blue') {
line.style.background = `linear-gradient(90deg, rgba(96, 165, 250, ${1 - ratio * 0.7}), rgba(45, 212, 191, ${1 - ratio * 0.7}))`;
line.style.webkitBackgroundClip = 'text';
line.style.backgroundClip = 'text';
line.style.color = 'transparent';
}
else if (currentColorScheme === 'gradient-orange') {
line.style.background = `linear-gradient(90deg, rgba(251, 146, 60, ${1 - ratio * 0.7}), rgba(239, 68, 68, ${1 - ratio * 0.7}))`;
line.style.webkitBackgroundClip = 'text';
line.style.backgroundClip = 'text';
line.style.color = 'transparent';
}
else if (currentColorScheme === 'gradient-green') {
line.style.background = `linear-gradient(90deg, rgba(74, 222, 128, ${1 - ratio * 0.7}), rgba(34, 211, 238, ${1 - ratio * 0.7}))`;
line.style.webkitBackgroundClip = 'text';
line.style.backgroundClip = 'text';
line.style.color = 'transparent';
}
}
function updateWave() {
const amp = parseInt(amplitude.value);
const freq = parseFloat(frequency.value);
const spd = parseInt(speed.value);
const type = waveType.value;
lines.forEach((line, i) => {
const element = line.element;
const phase = line.phase;
const yOffset = i * 5 - (parseInt(lineCount.value) * 5) / 2;
let offset = 0;
const timeFactor = time * spd * 0.01;
if (type === 'sine') {
offset = Math.sin(timeFactor * freq + phase) * amp;
} else if (type === 'square') {
offset = Math.sign(Math.sin(timeFactor * freq + phase)) * amp;
} else if (type === 'triangle') {
offset = (2 * amp / Math.PI) * Math.asin(Math.sin(timeFactor * freq + phase));
} else if (type === 'sawtooth') {
offset = (2 * amp / Math.PI) * Math.atan(Math.tan((timeFactor * freq + phase) / 2));
}
element.style.transform = `translateX(${offset}px) translateY(${yOffset}px)`;
element.style.opacity = 0.7 + (0.3 * (i / lines.length));
});
if (show3D.checked) {
waveContainer.style.transform = `rotateX(${rotateY}deg) rotateY(${rotateX}deg)`;
} else {
waveContainer.style.transform = 'none';
}
}
function animate() {
time++;
updateWave();
animationId = requestAnimationFrame(animate);
}
function updateDisplayValues() {
amplitudeValue.textContent = amplitude.value;
frequencyValue.textContent = frequency.value;
speedValue.textContent = speed.value;
lineCountValue.textContent = lineCount.value;
}
function setupEventListeners() {
// Input event listeners
textInput.addEventListener('input', createWaveLines);
waveType.addEventListener('change', () => time = 0);
amplitude.addEventListener('input', updateDisplayValues);
frequency.addEventListener('input', updateDisplayValues);
speed.addEventListener('input', updateDisplayValues);
lineCount.addEventListener('input', function() {
updateDisplayValues();
createWaveLines();
});
// Checkbox listeners
randomizePhase.addEventListener('change', function() {
lines.forEach(line => {
line.phase = this.checked ? Math.random() * Math.PI * 2 : 0;
});
});
show3D.addEventListener('change', function() {
waveContainer.style.transform = this.checked ?
`rotateX(${rotateY}deg) rotateY(${rotateX}deg)` : 'none';
});
// Button listeners
resetBtn.addEventListener('click', function() {
textInput.value = 'WAVE';
waveType.value = 'sine';
amplitude.value = 30;
frequency.value = 0.5;
speed.value = 3;
lineCount.value = 8;
randomizePhase.checked = false;
show3D.checked = true;
rotateX = 0;
rotateY = 0;
currentColorScheme = 'gradient-purple';
updateDisplayValues();
createWaveLines();
// Reset color buttons active state
colorBtns.forEach(btn => {
btn.classList.remove('ring-2', 'ring-white');
});
document.querySelector(`[data-color="gradient-purple"]`).classList.add('ring-2', 'ring-white');
});
randomBtn.addEventListener('click', function() {
waveType.value = ['sine', 'square', 'triangle', 'sawtooth'][Math.floor(Math.random() * 4)];
amplitude.value = Math.floor(Math.random() * 96) + 5;
frequency.value = (Math.random() * 1.9 + 0.1).toFixed(1);
speed.value = Math.floor(Math.random() * 9) + 1;
lineCount.value = Math.floor(Math.random() * 17) + 3;
randomizePhase.checked = Math.random() > 0.5;
show3D.checked = Math.random() > 0.3;
// Random color scheme
const colors = ['gradient-purple', 'gradient-blue', 'gradient-orange', 'gradient-green'];
currentColorScheme = colors[Math.floor(Math.random() * colors.length)];
// Update active color button
colorBtns.forEach(btn => {
btn.classList.remove('ring-2', 'ring-white');
if (btn.dataset.color === currentColorScheme) {
btn.classList.add('ring-2', 'ring-white');
}
});
updateDisplayValues();
createWaveLines();
});
// Color buttons
colorBtns.forEach(btn => {
btn.addEventListener('click', function() {
currentColorScheme = this.dataset.color;
colorBtns.forEach(b => b.classList.remove('ring-2', 'ring-white'));
this.classList.add('ring-2', 'ring-white');
createWaveLines();
});
// Set initial active button
if (btn.dataset.color === currentColorScheme) {
btn.classList.add('ring-2', 'ring-white');
}
});
// Mouse interaction for 3D rotation
waveContainer.addEventListener('mousedown', function(e) {
if (!show3D.checked) return;
isDragging = true;
lastX = e.clientX;
lastY = e.clientY;
});
document.addEventListener('mousemove', function(e) {
if (!isDragging || !show3D.checked) return;
const deltaX = e.clientX - lastX;
const deltaY = e.clientY - lastY;
rotateX += deltaX * 0.5;
rotateY -= deltaY * 0.5;
lastX = e.clientX;
lastY = e.clientY;
});
document.addEventListener('mouseup', function() {
isDragging = false;
});
// Touch support
waveContainer.addEventListener('touchstart', function(e) {
if (!show3D.checked) return;
isDragging = true;
lastX = e.touches[0].clientX;
lastY = e.touches[0].clientY;
e.preventDefault();
});
document.addEventListener('touchmove', function(e) {
if (!isDragging || !show3D.checked) return;
const deltaX = e.touches[0].clientX - lastX;
const deltaY = e.touches[0].clientY - lastY;
rotateX += deltaX * 0.5;
rotateY -= deltaY * 0.5;
lastX = e.touches[0].clientX;
lastY = e.touches[0].clientY;
e.preventDefault();
});
document.addEventListener('touchend', function() {
isDragging = false;
});
}
});
</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 <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=MarkTheArtist/text-wave" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>