Sinfonía Orquestal Interactiva
- samuel gaitan
- 29 ago
- 8 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>Sinfonía Orquestal Interactiva</title>
<style>
body {
margin: 0;
padding: 0;
font-family: 'Georgia', serif;
color: #fff;
overflow: hidden;
height: 100vh;
}
.orchestra-container {
position: relative;
width: 100%;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.stage {
position: relative;
width: 90%;
height: 70%;
background: radial-gradient(ellipse at center, rgba(255,215,0,0.1), transparent);
border-radius: 50% 50% 0 0;
display: flex;
align-items: center;
justify-content: space-around;
padding: 40px;
box-shadow: 0 -20px 40px rgba(255,215,0,0.3);
}
.instrument {
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
transition: all 0.3s ease;
padding: 20px;
border-radius: 15px;
background: rgba(255,255,255,0.05);
backdrop-filter: blur(10px);
border: 1px solid rgba(255,255,255,0.1);
position: relative;
}
.instrument:hover {
transform: translateY(-10px) scale(1.05);
box-shadow: 0 20px 40px rgba(255,215,0,0.4);
background: rgba(255,255,255,0.1);
}
.instrument.playing {
animation: pulse 0.8s ease-in-out infinite alternate;
box-shadow: 0 0 30px rgba(255,215,0,0.8);
}
@keyframes pulse {
from {
transform: scale(1);
box-shadow: 0 0 20px rgba(255,215,0,0.6);
}
to {
transform: scale(1.15);
box-shadow: 0 0 40px rgba(255,215,0,1);
}
}
.instrument-icon {
font-size: 4rem;
margin-bottom: 15px;
filter: drop-shadow(0 0 10px rgba(255,215,0,0.5));
transition: all 0.3s ease;
}
.instrument.playing .instrument-icon {
animation: bounce 0.8s ease-in-out infinite;
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-15px); }
}
.instrument-name {
font-size: 1.2rem;
font-weight: bold;
text-shadow: 0 0 10px rgba(255,215,0,0.7);
}
.status {
position: absolute;
bottom: -10px;
left: 50%;
transform: translateX(-50%);
font-size: 0.8rem;
opacity: 0;
transition: opacity 0.3s ease;
}
.instrument.playing .status {
opacity: 1;
}
.controls {
position: absolute;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 20px;
z-index: 100;
}
.control-btn {
padding: 15px 30px;
border: none;
border-radius: 25px;
color: white;
font-size: 1rem;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
}
.control-btn:hover {
transform: translateY(-3px);
box-shadow: 0 8px 25px rgba(238,90,36,0.4);
}
.control-btn.active {
}
.volume-control {
position: absolute;
top: 30px;
right: 30px;
display: flex;
align-items: center;
gap: 10px;
background: rgba(255,255,255,0.1);
padding: 15px;
border-radius: 15px;
backdrop-filter: blur(10px);
}
.volume-slider {
width: 100px;
height: 5px;
background: rgba(255,255,255,0.3);
border-radius: 5px;
outline: none;
cursor: pointer;
appearance: none;
}
.volume-slider::-webkit-slider-thumb {
appearance: none;
width: 15px;
height: 15px;
background: #ffd700;
border-radius: 50%;
cursor: pointer;
}
.title {
position: absolute;
top: 30px;
left: 50%;
transform: translateX(-50%);
font-size: 2.5rem;
font-weight: bold;
text-align: center;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: 0 0 20px rgba(255,215,0,0.5);
}
.note-visualizer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1;
}
.note {
position: absolute;
width: 12px;
height: 12px;
border-radius: 50%;
opacity: 0;
animation: float 2.5s ease-out forwards;
box-shadow: 0 0 10px rgba(255,215,0,0.8);
}
@keyframes float {
0% {
opacity: 1;
transform: translateY(0) scale(0.3);
}
30% {
opacity: 1;
transform: translateY(-50px) scale(1);
}
100% {
opacity: 0;
transform: translateY(-150px) scale(0.3);
}
}
.audio-status {
position: absolute;
top: 100px;
left: 30px;
background: rgba(0,0,0,0.5);
padding: 10px;
border-radius: 10px;
font-size: 0.9rem;
}
</style>
</head>
<body>
<div class="orchestra-container">
<h1 class="title">🎼 Sinfonía Orquestal 🎼</h1>
<div class="audio-status" id="audioStatus">
🔇 Audio listo - Haz clic para activar
</div>
<div class="volume-control">
<span>🔊</span>
<input type="range" class="volume-slider" min="0" max="1" step="0.1" value="0.4" id="volumeControl">
<span id="volumeDisplay">40%</span>
</div>
<div class="stage">
<div class="instrument" data-instrument="violin">
<div class="instrument-icon">🎻</div>
<div class="instrument-name">Violín</div>
<div class="status">♪ Tocando...</div>
</div>
<div class="instrument" data-instrument="piano">
<div class="instrument-icon">🎹</div>
<div class="instrument-name">Piano</div>
<div class="status">♫ Sonando...</div>
</div>
<div class="instrument" data-instrument="guitar">
<div class="instrument-icon">🎸</div>
<div class="instrument-name">Guitarra</div>
<div class="status">♬ Vibrando...</div>
</div>
</div>
<div class="controls">
<button class="control-btn" id="playBtn">▶️ Reproducir Sinfonía</button>
<button class="control-btn" id="stopBtn">⏹️ Detener</button>
<button class="control-btn" id="soloBtn">🎵 Modo Solo</button>
<button class="control-btn" id="testBtn">🔧 Probar Audio</button>
</div>
<div class="note-visualizer" id="noteVisualizer"></div>
</div>
<script>
// Variables globales
let audioContext = null;
let isPlaying = false;
let currentVolume = 0.4;
let symphonyTimeouts = [];
let soloMode = false;
let audioInitialized = false;
// Frecuencias de notas musicales optimizadas
const notes = {
// Octava 3 (graves)
C3: 130.81, D3: 146.83, E3: 164.81, F3: 174.61,
G3: 196.00, A3: 220.00, B3: 246.94,
// Octava 4 (medias)
C4: 261.63, D4: 293.66, E4: 329.63, F4: 349.23,
G4: 392.00, A4: 440.00, B4: 493.88,
// Octava 5 (agudas)
C5: 523.25, D5: 587.33, E5: 659.25, F5: 698.46,
G5: 783.99, A5: 880.00, B5: 987.77
};
// Configuración mejorada de instrumentos
const instrumentConfig = {
violin: {
ascending: ['G4', 'A4', 'B4', 'C5', 'D5', 'E5', 'F5', 'G5'],
descending: ['G5', 'F5', 'E5', 'D5', 'C5', 'B4', 'A4', 'G4'],
waveType: 'sawtooth',
baseVolume: 0.25,
attack: 0.1,
decay: 0.3,
sustain: 0.6,
release: 0.8
},
piano: {
ascending: ['C4', 'D4', 'E4', 'F4', 'G4', 'A4', 'B4', 'C5'],
descending: ['C5', 'B4', 'A4', 'G4', 'F4', 'E4', 'D4', 'C4'],
waveType: 'triangle',
baseVolume: 0.3,
attack: 0.05,
decay: 0.4,
sustain: 0.4,
release: 1.2
},
guitar: {
ascending: ['E3', 'F3', 'G3', 'A3', 'B3', 'C4', 'D4', 'E4'],
descending: ['E4', 'D4', 'C4', 'B3', 'A3', 'G3', 'F3', 'E3'],
waveType: 'square',
baseVolume: 0.2,
attack: 0.02,
decay: 0.2,
sustain: 0.5,
release: 0.6
}
};
// Inicializar contexto de audio mejorado
function initAudioContext() {
try {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
if (audioContext.state === 'suspended') {
audioContext.resume();
}
audioInitialized = true;
updateAudioStatus('🔊 Audio activado y listo');
return true;
} catch (error) {
console.error('Error inicializando audio:', error);
updateAudioStatus('❌ Error de audio');
return false;
}
}
// Actualizar estado del audio
function updateAudioStatus(message) {
const status = document.getElementById('audioStatus');
if (status) {
status.textContent = message;
}
}
// Crear sonido realista de instrumento
function createInstrumentSound(frequency, config, duration = 1000) {
if (!audioContext || !audioInitialized) {
console.log('Audio no inicializado');
return null;
}
try {
const currentTime = audioContext.currentTime;
// Oscilador principal
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
const filterNode = audioContext.createBiquadFilter();
// Configurar oscilador
oscillator.type = config.waveType;
oscillator.frequency.setValueAtTime(frequency, currentTime);
// Configurar filtro para suavizar el sonido
filterNode.type = 'lowpass';
filterNode.frequency.setValueAtTime(frequency * 4, currentTime);
filterNode.Q.setValueAtTime(1, currentTime);
// Configurar envolvente ADSR realista
const totalVolume = currentVolume * config.baseVolume;
gainNode.gain.setValueAtTime(0, currentTime);
gainNode.gain.linearRampToValueAtTime(totalVolume, currentTime + config.attack);
gainNode.gain.linearRampToValueAtTime(totalVolume * config.sustain, currentTime + config.attack + config.decay);
gainNode.gain.setValueAtTime(totalVolume * config.sustain, currentTime + duration/1000 - config.release);
gainNode.gain.linearRampToValueAtTime(0, currentTime + duration/1000);
// Conectar nodos
oscillator.connect(filterNode);
filterNode.connect(gainNode);
gainNode.connect(audioContext.destination);
// Reproducir
oscillator.start(currentTime);
oscillator.stop(currentTime + duration/1000);
return { oscillator, gainNode, filterNode };
} catch (error) {
console.error('Error creando sonido:', error);
return null;
}
}
// Agregar armónicos para enriquecer el sonido
function addHarmonics(baseFreq, config, duration, harmonicVolume = 0.1) {
if (!audioContext) return;
// Agregar segunda y tercera armónica
const harmonics = [2, 3, 4];
harmonics.forEach((harmonic, index) => {
setTimeout(() => {
if (audioContext) {
const harmonicFreq = baseFreq * harmonic;
const harmonicConfig = {...config, baseVolume: config.baseVolume * harmonicVolume / harmonic};
createInstrumentSound(harmonicFreq, harmonicConfig, duration * 0.7);
}
}, index * 50);
});
}
// Reproducir nota de instrumento específico
function playInstrumentNote(instrument, noteName, duration = 1000) {
if (!audioInitialized) {
updateAudioStatus('⚠️ Haz clic en "Probar Audio" primero');
return;
}
const frequency = notes[noteName];
const config = instrumentConfig[instrument];
if (frequency && config) {
// Sonido principal
const sound = createInstrumentSound(frequency, config, duration);
if (sound) {
// Agregar armónicos según el instrumento
if (instrument === 'violin') {
addHarmonics(frequency, config, duration, 0.15);
} else if (instrument === 'guitar') {
addHarmonics(frequency, config, duration, 0.12);
}
// Activar animación visual
const instrumentEl = document.querySelector(`[data-instrument="${instrument}"]`);
instrumentEl.classList.add('playing');
setTimeout(() => {
instrumentEl.classList.remove('playing');
}, duration * 0.8);
// Crear visualización
createNoteVisualization(instrument, noteName);
}
}
}
// Reproducir melodía completa de un instrumento
function playMelody(instrument, pattern, startDelay = 0) {
const config = instrumentConfig[instrument];
const noteSequence = config[pattern];
updateAudioStatus(`🎵 Tocando ${instrument} - patrón ${pattern}`);
noteSequence.forEach((noteName, index) => {
const timeout = setTimeout(() => {
if (isPlaying) {
playInstrumentNote(instrument, noteName, 1200);
}
}, startDelay + (index * 1000));
symphonyTimeouts.push(timeout);
});
}
// Crear visualización mejorada de notas
function createNoteVisualization(instrument, noteName) {
const visualizer = document.getElementById('noteVisualizer');
const note = document.createElement('div');
note.className = 'note';
// Posiciones específicas por instrumento
const positions = {
violin: { left: '25%', color: '#ff6b9d' },
piano: { left: '50%', color: '#4ecdc4' },
guitar: { left: '75%', color: '#ffe66d' }
};
const pos = positions[instrument];
note.style.left = pos.left;
note.style.bottom = '20%';
note.style.background = `radial-gradient(circle, ${pos.color}, transparent)`;
note.style.boxShadow = `0 0 15px ${pos.color}`;
// Agregar texto de nota
note.textContent = noteName;
note.style.fontSize = '10px';
note.style.textAlign = 'center';
note.style.lineHeight = '12px';
note.style.color = '#fff';
note.style.fontWeight = 'bold';
visualizer.appendChild(note);
setTimeout(() => {
if (note.parentNode) {
note.parentNode.removeChild(note);
}
}, 2500);
}
// Reproducir sinfonía completa con mejores tiempos
function playSymphony() {
if (!isPlaying || !audioInitialized) return;
updateAudioStatus('🎼 Reproduciendo sinfonía completa...');
// Secuencia musical mejorada
const symphonySequence = [
// Introducción - Violin solo
{ instrument: 'violin', pattern: 'ascending', delay: 0 },
{ instrument: 'violin', pattern: 'descending', delay: 9000 },
// Desarrollo - Piano se une
{ instrument: 'piano', pattern: 'ascending', delay: 18000 },
{ instrument: 'violin', pattern: 'ascending', delay: 19000 },
// Clímax - Guitarra entra
{ instrument: 'guitar', pattern: 'ascending', delay: 36000 },
{ instrument: 'piano', pattern: 'descending', delay: 36500 },
{ instrument: 'violin', pattern: 'ascending', delay: 37000 },
// Final - Armonía descendente
{ instrument: 'violin', pattern: 'descending', delay: 54000 },
{ instrument: 'piano', pattern: 'descending', delay: 55000 },
{ instrument: 'guitar', pattern: 'descending', delay: 56000 }
];
symphonySequence.forEach(({ instrument, pattern, delay }) => {
const timeout = setTimeout(() => {
if (isPlaying) {
playMelody(instrument, pattern);
}
}, delay);
symphonyTimeouts.push(timeout);
});
// Programar siguiente ciclo
const cycleTimeout = setTimeout(() => {
if (isPlaying) {
playSymphony();
}
}, 72000);
symphonyTimeouts.push(cycleTimeout);
}
// Detener todos los sonidos
function stopAllSounds() {
isPlaying = false;
// Limpiar timeouts
symphonyTimeouts.forEach(timeout => clearTimeout(timeout));
symphonyTimeouts = [];
// Remover animaciones
document.querySelectorAll('.instrument').forEach(inst => {
inst.classList.remove('playing');
});
updateAudioStatus('⏹️ Música detenida');
}
// Probar audio individual
function testAudio() {
if (!initAudioContext()) {
updateAudioStatus('❌ No se pudo inicializar el audio');
return;
}
updateAudioStatus('🔧 Probando instrumentos...');
// Probar cada instrumento
setTimeout(() => playInstrumentNote('violin', 'A4', 800), 200);
setTimeout(() => playInstrumentNote('piano', 'C4', 800), 1200);
setTimeout(() => playInstrumentNote('guitar', 'G3', 800), 2200);
setTimeout(() => {
updateAudioStatus('✅ Prueba completada - Audio funcionando');
}, 3500);
}
// Event listeners
document.addEventListener('DOMContentLoaded', function() {
const playBtn = document.getElementById('playBtn');
const stopBtn = document.getElementById('stopBtn');
const soloBtn = document.getElementById('soloBtn');
const testBtn = document.getElementById('testBtn');
const volumeControl = document.getElementById('volumeControl');
const volumeDisplay = document.getElementById('volumeDisplay');
const instruments = document.querySelectorAll('.instrument');
// Botón de reproducción
playBtn.addEventListener('click', function() {
if (!initAudioContext()) {
updateAudioStatus('❌ Error: Haz clic en "Probar Audio" primero');
return;
}
isPlaying = true;
playBtn.classList.add('active');
stopBtn.classList.remove('active');
if (!soloMode) {
playSymphony();
} else {
updateAudioStatus('🎵 Modo solo activo - Haz clic en los instrumentos');
}
});
// Botón de parar
stopBtn.addEventListener('click', function() {
stopAllSounds();
playBtn.classList.remove('active');
stopBtn.classList.add('active');
});
// Modo solo
soloBtn.addEventListener('click', function() {
soloMode = !soloMode;
soloBtn.classList.toggle('active');
soloBtn.textContent = soloMode ? '🎼 Modo Orquesta' : '🎵 Modo Solo';
if (soloMode) {
stopAllSounds();
updateAudioStatus('🎵 Modo solo - Haz clic en instrumentos para tocar');
}
});
// Botón de prueba
testBtn.addEventListener('click', function() {
testAudio();
});
// Control de volumen
volumeControl.addEventListener('input', function() {
currentVolume = parseFloat(this.value);
volumeDisplay.textContent = Math.round(currentVolume * 100) + '%';
});
// Instrumentos individuales (modo solo)
instruments.forEach(instrument => {
instrument.addEventListener('click', function() {
if (!audioInitialized) {
updateAudioStatus('⚠️ Primero haz clic en "Probar Audio"');
return;
}
if (soloMode || !isPlaying) {
const instrumentType = this.dataset.instrument;
const config = instrumentConfig[instrumentType];
// Alternar entre patrones
const pattern = Math.random() > 0.5 ? 'ascending' : 'descending';
updateAudioStatus(`🎵 Tocando ${instrumentType} - ${pattern}`);
// Tocar secuencia completa
config[pattern].forEach((noteName, index) => {
setTimeout(() => {
playInstrumentNote(instrumentType, noteName, 800);
}, index * 600);
});
}
});
// Efecto hover con sonido suave
instrument.addEventListener('mouseenter', function() {
if (audioInitialized && !isPlaying) {
const instrumentType = this.dataset.instrument;
const config = instrumentConfig[instrumentType];
const randomNote = config.ascending[Math.floor(Math.random() * config.ascending.length)];
playInstrumentNote(instrumentType, randomNote, 400);
}
});
});
// Auto-inicializar audio en primera interacción
document.addEventListener('click', function autoInit() {
if (!audioInitialized) {
initAudioContext();
}
}, { once: true });
// Inicialización automática del volumen
volumeDisplay.textContent = Math.round(currentVolume * 100) + '%';
});
// Efectos visuales mejorados
function createBackgroundTwinkle() {
const container = document.querySelector('.orchestra-container');
const twinkle = document.createElement('div');
twinkle.style.cssText = `
position: absolute;
width: 3px;
height: 3px;
background: radial-gradient(circle, #ffd700, transparent);
border-radius: 50%;
pointer-events: none;
opacity: 0;
left: ${Math.random() * 100}%;
top: ${Math.random() * 100}%;
animation: twinkleEffect 3s ease-in-out;
`;
container.appendChild(twinkle);
setTimeout(() => {
if (twinkle.parentNode) {
twinkle.parentNode.removeChild(twinkle);
}
}, 3000);
}
// Agregar estilos de animación para efectos
const additionalStyles = document.createElement('style');
additionalStyles.textContent = `
@keyframes twinkleEffect {
0%, 100% { opacity: 0; transform: scale(0); }
50% { opacity: 1; transform: scale(1); }
}
`;
document.head.appendChild(additionalStyles);
// Crear efectos de fondo
setInterval(createBackgroundTwinkle, 1500);
// Debug: Mostrar información de audio
window.debugAudio = function() {
console.log('Estado del audio:', {
audioContext: !!audioContext,
audioInitialized: audioInitialized,
isPlaying: isPlaying,
currentVolume: currentVolume,
contextState: audioContext ? audioContext.state : 'No creado'
});
};
</script>
</body>
</html>




Comentarios