Karaoke de poemas de amor en html
- samuel gaitan
- 2 sept
- 6 Min. de lectura
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Karaoke de Poemas de Amor</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
min-height: 100vh;
color: white;
padding: 20px;
}
.container {
max-width: 1000px;
margin: 0 auto;
text-align: center;
}
h1 {
font-size: 2.5em;
margin-bottom: 30px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.voice-selector {
background: rgba(255,255,255,0.1);
padding: 20px;
border-radius: 15px;
margin-bottom: 30px;
backdrop-filter: blur(10px);
}
.voice-controls {
display: flex;
flex-wrap: wrap;
gap: 15px;
justify-content: center;
align-items: center;
margin-bottom: 20px;
}
select, input[type="range"] {
padding: 10px;
border: none;
border-radius: 8px;
background: rgba(255,255,255,0.9);
color: #333;
font-size: 1em;
}
select {
min-width: 200px;
}
.range-container {
display: flex;
align-items: center;
gap: 10px;
}
.range-label {
font-size: 0.9em;
min-width: 80px;
}
.karaoke-display {
background: rgba(255,255,255,0.95);
color: #333;
padding: 40px;
border-radius: 20px;
margin: 30px 0;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
min-height: 300px;
display: flex;
flex-direction: column;
justify-content: center;
}
.poem-title {
font-size: 1.8em;
color: #667eea;
margin-bottom: 30px;
font-weight: bold;
}
.poem-text {
font-size: 1.4em;
line-height: 2;
white-space: pre-line;
}
.current-line {
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
font-weight: bold;
transform: scale(1.1);
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
}
.sung-line {
color: #888;
opacity: 0.7;
}
.upcoming-line {
color: #333;
opacity: 0.5;
}
.controls {
display: flex;
gap: 20px;
justify-content: center;
flex-wrap: wrap;
margin: 20px 0;
}
.btn {
padding: 15px 30px;
border: none;
border-radius: 50px;
font-size: 1.1em;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
color: white;
font-weight: bold;
}
.btn-play {
}
.btn-pause {
}
.btn-stop {
}
.btn-restart {
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0,0,0,0.3);
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.poem-selector {
margin: 20px 0;
}
.poem-selector select {
font-size: 1.1em;
padding: 15px;
min-width: 300px;
}
.status {
background: rgba(255,255,255,0.1);
padding: 10px 20px;
border-radius: 25px;
margin: 10px 0;
font-size: 1.1em;
}
.progress-bar {
width: 100%;
height: 8px;
background: rgba(255,255,255,0.3);
border-radius: 4px;
overflow: hidden;
margin: 20px 0;
}
.progress-fill {
height: 100%;
width: 0%;
transition: width 0.5s ease;
}
@media (max-width: 768px) {
.voice-controls {
flex-direction: column;
gap: 10px;
}
.controls {
flex-direction: column;
align-items: center;
}
.btn {
width: 200px;
}
h1 {
font-size: 2em;
}
.karaoke-display {
padding: 20px;
}
}
</style>
</head>
<body>
<div class="container">
<h1>🎤 Karaoke de Poemas de Amor 🎤</h1>
<div class="voice-selector">
<h3>Configuración de Voz</h3>
<div class="voice-controls">
<select id="voiceSelect">
<option value="">Cargando voces...</option>
</select>
<div class="range-container">
<span class="range-label">Velocidad:</span>
<input type="range" id="rateSlider" min="0.5" max="2" step="0.1" value="1">
<span id="rateValue">1.0</span>
</div>
<div class="range-container">
<span class="range-label">Tono:</span>
<input type="range" id="pitchSlider" min="0.5" max="2" step="0.1" value="1">
<span id="pitchValue">1.0</span>
</div>
<div class="range-container">
<span class="range-label">Volumen:</span>
<input type="range" id="volumeSlider" min="0" max="1" step="0.1" value="1">
<span id="volumeValue">1.0</span>
</div>
</div>
</div>
<div class="poem-selector">
<select id="poemSelect">
<option value="0">Te Amo Más Que...</option>
<option value="1">Eres Más Hermosa Que...</option>
<option value="2">Carta de Amor Eterna</option>
<option value="3">Mi Corazón Te Habla</option>
</select>
</div>
<div class="karaoke-display" id="karaokeDisplay">
<div class="poem-title" id="poemTitle">Selecciona un poema para comenzar</div>
<div class="poem-text" id="poemText">Presiona PLAY para iniciar el karaoke</div>
</div>
<div class="progress-bar">
<div class="progress-fill" id="progressFill"></div>
</div>
<div class="status" id="status">Listo para comenzar</div>
<div class="controls">
<button class="btn btn-play" id="playBtn" onclick="startKaraoke()">▶️ PLAY</button>
<button class="btn btn-pause" id="pauseBtn" onclick="pauseKaraoke()" disabled>⏸️ PAUSA</button>
<button class="btn btn-stop" id="stopBtn" onclick="stopKaraoke()" disabled>⏹️ STOP</button>
<button class="btn btn-restart" id="restartBtn" onclick="restartKaraoke()">🔄 REINICIAR</button>
</div>
</div>
<script>
// Banco de poemas
const poems = [
{
title: "Te Amo Más Que...",
lines: [
"Te amo más que las estrellas aman la noche",
"Más que el océano ama la luna llena",
"Te amo más que las flores aman la primavera",
"Más que el corazón ama cada pena",
"",
"Eres mi respirar, mi despertar",
"Eres la razón de mi existir",
"Te amo más que la vida misma",
"Más que todo lo que pueda decir",
"",
"En cada latido llevas mi nombre",
"En cada suspiro vives tú",
"Te amo más que el infinito",
"Más que todo lo que fue y será"
]
},
{
title: "Eres Más Hermosa Que...",
lines: [
"Eres más hermosa que el amanecer",
"Que pinta el cielo de colores mil",
"Más bella que la rosa más perfecta",
"Que florece en el jardín de abril",
"",
"Tus ojos brillan más que las estrellas",
"Tu sonrisa ilumina mi camino",
"Eres más hermosa que la poesía",
"Más pura que el amor más cristalino",
"",
"En tu belleza encuentro mi refugio",
"En tu mirada veo mi hogar",
"Eres más hermosa que mis sueños",
"Más perfecta que todo lo que hay"
]
},
{
title: "Carta de Amor Eterna",
lines: [
"Si pudiera escribir en las nubes",
"Todas las palabras que guardé",
"El cielo se llenaría de te amo",
"De todo lo que nunca expresé",
"",
"Eres la carta que siempre quise escribir",
"Eres el verso que quiero recitar",
"En cada palabra encuentro tu esencia",
"En cada línea, tu forma de amar",
"",
"Esta carta no tiene final",
"Como mi amor que es eternal",
"Eres mi poema favorito",
"Mi historia más especial"
]
},
{
title: "Mi Corazón Te Habla",
lines: [
"Escucha lo que mi corazón te dice",
"Sin palabras pero con emoción",
"Cada latido lleva tu nombre",
"Cada suspiro, mi devoción",
"",
"Mi alma te busca en cada despertar",
"Mis sueños te dibujan cada noche",
"Eres la melodía que me calma",
"El abrazo que siempre me arrope",
"",
"Que mi corazón sea tu refugio",
"Que mis brazos sean tu hogar",
"Porque amarte es mi propósito",
"Mi razón para despertar"
]
}
];
// Variables globales
let currentPoem = 0;
let currentLine = 0;
let isPlaying = false;
let isPaused = false;
let speechSynthesis = window.speechSynthesis;
let voices = [];
let utterance = null;
let karaokeInterval = null;
let lineTimeout = null;
// Elementos del DOM
const voiceSelect = document.getElementById('voiceSelect');
const rateSlider = document.getElementById('rateSlider');
const pitchSlider = document.getElementById('pitchSlider');
const volumeSlider = document.getElementById('volumeSlider');
const rateValue = document.getElementById('rateValue');
const pitchValue = document.getElementById('pitchValue');
const volumeValue = document.getElementById('volumeValue');
const poemSelect = document.getElementById('poemSelect');
const karaokeDisplay = document.getElementById('karaokeDisplay');
const poemTitle = document.getElementById('poemTitle');
const poemText = document.getElementById('poemText');
const status = document.getElementById('status');
const progressFill = document.getElementById('progressFill');
const playBtn = document.getElementById('playBtn');
const pauseBtn = document.getElementById('pauseBtn');
const stopBtn = document.getElementById('stopBtn');
// Cargar voces disponibles
function loadVoices() {
voices = speechSynthesis.getVoices();
voiceSelect.innerHTML = '';
if (voices.length === 0) {
voiceSelect.innerHTML = '<option value="">No hay voces disponibles</option>';
return;
}
// Filtrar voces en español
const spanishVoices = voices.filter(voice =>
voice.lang.includes('es') || voice.lang.includes('ES')
);
if (spanishVoices.length > 0) {
spanishVoices.forEach((voice, index) => {
const option = document.createElement('option');
option.value = voice.name;
option.textContent = `${voice.name} (${voice.lang})`;
voiceSelect.appendChild(option);
});
} else {
// Si no hay voces en español, mostrar todas
voices.forEach((voice, index) => {
const option = document.createElement('option');
option.value = voice.name;
option.textContent = `${voice.name} (${voice.lang})`;
voiceSelect.appendChild(option);
});
}
}
// Event listeners para los sliders
rateSlider.addEventListener('input', (e) => {
rateValue.textContent = e.target.value;
});
pitchSlider.addEventListener('input', (e) => {
pitchValue.textContent = e.target.value;
});
volumeSlider.addEventListener('input', (e) => {
volumeValue.textContent = e.target.value;
});
// Cambiar poema
poemSelect.addEventListener('change', (e) => {
currentPoem = parseInt(e.target.value);
displayPoem();
stopKaraoke();
});
// Mostrar poema
function displayPoem() {
const poem = poems[currentPoem];
poemTitle.textContent = poem.title;
let poemHTML = '';
poem.lines.forEach((line, index) => {
const className = index < currentLine ? 'sung-line' :
index === currentLine ? 'current-line' : 'upcoming-line';
poemHTML += `<div class="${className}">${line || ' '}</div>`;
});
poemText.innerHTML = poemHTML;
// Actualizar barra de progreso
const progress = (currentLine / poem.lines.length) * 100;
progressFill.style.width = `${progress}%`;
}
// Iniciar karaoke
function startKaraoke() {
if (isPaused) {
// Reanudar desde donde se pausó
isPaused = false;
continueKaraoke();
} else {
// Iniciar desde el principio
currentLine = 0;
isPlaying = true;
displayPoem();
speakCurrentLine();
}
updateButtons();
status.textContent = 'Reproduciendo...';
}
// Pausar karaoke
function pauseKaraoke() {
isPaused = true;
isPlaying = false;
speechSynthesis.pause();
clearTimeout(lineTimeout);
updateButtons();
status.textContent = 'Pausado';
}
// Continuar karaoke
function continueKaraoke() {
isPlaying = true;
speechSynthesis.resume();
updateButtons();
status.textContent = 'Reproduciendo...';
}
// Detener karaoke
function stopKaraoke() {
isPlaying = false;
isPaused = false;
speechSynthesis.cancel();
clearTimeout(lineTimeout);
currentLine = 0;
displayPoem();
updateButtons();
status.textContent = 'Detenido';
}
// Reiniciar karaoke
function restartKaraoke() {
stopKaraoke();
currentLine = 0;
displayPoem();
startKaraoke();
}
// Hablar línea actual
function speakCurrentLine() {
const poem = poems[currentPoem];
if (currentLine >= poem.lines.length) {
// Fin del poema
isPlaying = false;
isPaused = false;
updateButtons();
status.textContent = 'Poema completado';
return;
}
const line = poem.lines[currentLine];
if (line.trim() === '') {
// Línea vacía, pasar a la siguiente
currentLine++;
displayPoem();
lineTimeout = setTimeout(() => {
if (isPlaying && !isPaused) {
speakCurrentLine();
}
}, 1000);
return;
}
// Crear nueva instancia de SpeechSynthesisUtterance
utterance = new SpeechSynthesisUtterance(line);
// Configurar voz
const selectedVoice = voices.find(voice => voice.name === voiceSelect.value);
if (selectedVoice) {
utterance.voice = selectedVoice;
}
utterance.rate = parseFloat(rateSlider.value);
utterance.pitch = parseFloat(pitchSlider.value);
utterance.volume = parseFloat(volumeSlider.value);
utterance.onend = () => {
if (isPlaying && !isPaused) {
currentLine++;
displayPoem();
lineTimeout = setTimeout(() => {
if (isPlaying && !isPaused) {
speakCurrentLine();
}
}, 500);
}
};
utterance.onerror = (event) => {
console.error('Error en síntesis de voz:', event);
status.textContent = 'Error en la reproducción';
};
speechSynthesis.speak(utterance);
}
// Actualizar botones
function updateButtons() {
playBtn.disabled = isPlaying && !isPaused;
pauseBtn.disabled = !isPlaying || isPaused;
stopBtn.disabled = !isPlaying && !isPaused;
}
// Inicialización
function init() {
loadVoices();
displayPoem();
updateButtons();
// Recargar voces cuando estén disponibles
if (speechSynthesis.onvoiceschanged !== undefined) {
speechSynthesis.onvoiceschanged = loadVoices;
}
}
// Inicializar cuando la página esté cargada
window.addEventListener('load', init);
// Limpiar al cerrar la página
window.addEventListener('beforeunload', () => {
speechSynthesis.cancel();
});
</script>
</body>
</html>




Comentarios