| | <!DOCTYPE html> |
| | <html lang="en"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <title>The Unsolicited Upgrade: Elon vs OpenAI</title> |
| | <style> |
| | :root { |
| | --bg-server: #0a0a12; |
| | --led-blue: #00f3ff; |
| | --led-red: #ff003c; |
| | --ai-core: #ffffff; |
| | --elon-shirt: #1a1a1a; |
| | } |
| | |
| | body { |
| | margin: 0; |
| | padding: 0; |
| | background-color: #000; |
| | height: 100vh; |
| | display: flex; |
| | justify-content: center; |
| | align-items: center; |
| | font-family: 'Courier New', monospace; |
| | overflow: hidden; |
| | color: white; |
| | } |
| | |
| | #main-stage { |
| | position: relative; |
| | width: 960px; |
| | height: 540px; |
| | background: var(--bg-server); |
| | border: 2px solid #333; |
| | box-shadow: 0 0 50px rgba(0,0,0,0.8); |
| | overflow: hidden; |
| | } |
| | |
| | #render-canvas { |
| | width: 100%; |
| | height: 100%; |
| | display: block; |
| | } |
| | |
| | |
| | #ui-overlay { |
| | position: absolute; |
| | top: 20px; |
| | left: 20px; |
| | font-size: 14px; |
| | color: var(--led-blue); |
| | text-shadow: 0 0 5px var(--led-blue); |
| | opacity: 0.7; |
| | } |
| | |
| | #time-display { |
| | font-weight: bold; |
| | } |
| | |
| | #subtitle-container { |
| | position: absolute; |
| | bottom: 30px; |
| | width: 100%; |
| | display: flex; |
| | justify-content: center; |
| | z-index: 10; |
| | } |
| | |
| | #subtitle-text { |
| | background-color: rgba(0, 0, 0, 0.7); |
| | color: #fff; |
| | padding: 10px 20px; |
| | border-radius: 5px; |
| | font-family: sans-serif; |
| | font-size: 20px; |
| | text-align: center; |
| | max-width: 80%; |
| | opacity: 0; |
| | transition: opacity 0.2s ease-in-out; |
| | text-shadow: 1px 1px 2px black; |
| | } |
| | |
| | #start-screen { |
| | position: absolute; |
| | top: 0; left: 0; right: 0; bottom: 0; |
| | background: rgba(0,0,0,0.9); |
| | display: flex; |
| | flex-direction: column; |
| | align-items: center; |
| | justify-content: center; |
| | z-index: 100; |
| | } |
| | |
| | #start-btn { |
| | padding: 20px 40px; |
| | font-size: 24px; |
| | background: var(--led-blue); |
| | color: #000; |
| | border: none; |
| | cursor: pointer; |
| | font-weight: bold; |
| | box-shadow: 0 0 20px var(--led-blue); |
| | transition: all 0.3s; |
| | } |
| | #start-btn:hover { |
| | background: #fff; |
| | transform: scale(1.1); |
| | } |
| | |
| | |
| | .crt-effect { |
| | position: absolute; |
| | top: 0; left: 0; right: 0; bottom: 0; |
| | background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06)); |
| | background-size: 100% 2px, 3px 100%; |
| | pointer-events: none; |
| | z-index: 90; |
| | opacity: 0.2; |
| | } |
| | |
| | @keyframes flicker { |
| | 0% { opacity: 1; } 50% { opacity: 0.8; } 100% { opacity: 1; } |
| | } |
| | .server-led { animation: flicker 0.1s infinite alternate; } |
| | |
| | @keyframes float { |
| | 0% { transform: translateY(0px); } 50% { transform: translateY(-10px); } 100% { transform: translateY(0px); } |
| | } |
| | .floating { animation: float 4s ease-in-out infinite; } |
| | |
| | |
| | .glitch-active { |
| | animation: glitch-skew 0.3s infinite linear alternate-reverse; |
| | filter: url(#glitch-filter); |
| | } |
| | @keyframes glitch-skew { |
| | 0% { transform: skewX(0deg) translate(0); } |
| | 20% { transform: skewX(-5deg) translate(-5px, 2px); } |
| | 40% { transform: skewX(5deg) translate(5px, -2px); } |
| | 60% { transform: skewX(2deg) translate(-2px, 5px); } |
| | 80% { transform: skewX(-2deg) translate(2px, -5px); } |
| | 100% { transform: skewX(0deg) translate(0); } |
| | } |
| | |
| | </style> |
| | </head> |
| | <body> |
| |
|
| | <div id="main-stage"> |
| | |
| | <svg id="render-canvas" viewBox="0 0 960 540" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice"> |
| | <defs> |
| | |
| | <filter id="glow"> |
| | <feGaussianBlur stdDeviation="4" result="coloredBlur"/> |
| | <feMerge> |
| | <feMergeNode in="coloredBlur"/><feMergeNode in="SourceGraphic"/> |
| | </feMerge> |
| | </filter> |
| | <filter id="glitch-filter"> |
| | <feTurbulence type="fractalNoise" baseFrequency="0.5" numOctaves="1" result="noise"/> |
| | <feDisplacementMap in="SourceGraphic" in2="noise" scale="20" xChannelSelector="R" yChannelSelector="G"/> |
| | </filter> |
| | <linearGradient id="grad-server" x1="0%" y1="0%" x2="100%" y2="0%"> |
| | <stop offset="0%" style="stop-color:#050508;stop-opacity:1" /> |
| | <stop offset="50%" style="stop-color:#151525;stop-opacity:1" /> |
| | <stop offset="100%" style="stop-color:#050508;stop-opacity:1" /> |
| | </linearGradient> |
| | <radialGradient id="grad-ai-core"> |
| | <stop offset="0%" stop-color="#fff" /> |
| | <stop offset="40%" stop-color="#00f3ff" /> |
| | <stop offset="100%" stop-color="rgba(0,243,255,0)" /> |
| | </radialGradient> |
| | <radialGradient id="grad-ai-core-red"> |
| | <stop offset="0%" stop-color="#fff" /> |
| | <stop offset="40%" stop-color="#ff003c" /> |
| | <stop offset="100%" stop-color="rgba(255,0,60,0)" /> |
| | </radialGradient> |
| | </defs> |
| |
|
| | |
| | <g id="bg-layer"> |
| | <rect width="960" height="540" fill="url(#grad-server)"/> |
| | |
| | <g id="server-racks" opacity="0.5"> |
| | <rect x="50" y="50" width="100" height="440" fill="#111" stroke="#222"/> |
| | <rect x="160" y="80" width="80" height="380" fill="#0e0e0e" stroke="#222"/> |
| | <rect x="810" y="50" width="100" height="440" fill="#111" stroke="#222"/> |
| | <rect x="720" y="80" width="80" height="380" fill="#0e0e0e" stroke="#222"/> |
| | |
| | <g class="server-led" fill="var(--led-blue)"> |
| | <circle cx="70" cy="100" r="3"/> <circle cx="85" cy="100" r="3"/> |
| | <circle cx="840" cy="200" r="3"/> <circle cx="860" cy="200" r="3"/> |
| | </g> |
| | </g> |
| | <rect x="0" y="480" width="960" height="60" fill="#0a0a0a"/> |
| | </g> |
| |
|
| | |
| | <g id="openai-char" transform="translate(480, 200)" class="floating"> |
| | |
| | <circle cx="0" cy="0" r="70" stroke="#333" stroke-width="5" fill="none" opacity="0.5"/> |
| | <circle id="ai-ring" cx="0" cy="0" r="70" stroke="var(--led-blue)" stroke-width="3" fill="none" stroke-dasharray="100 340" stroke-linecap="round"> |
| | <animateTransform attributeName="transform" type="rotate" from="0 0 0" to="360 0 0" dur="10s" repeatCount="indefinite"/> |
| | </circle> |
| | |
| | <circle id="ai-core" cx="0" cy="0" r="40" fill="url(#grad-ai-core)" filter="url(#glow)"/> |
| | |
| | <g id="ai-waveform" opacity="0" transform="translate(-30, 0)"> |
| | <rect x="0" y="-10" width="5" height="20" fill="#fff"><animate attributeName="height" values="20;40;10;30;20" dur="0.5s" repeatCount="indefinite" calcMode="spline" keySplines="0.5 0 0.5 1; 0.5 0 0.5 1; 0.5 0 0.5 1; 0.5 0 0.5 1"/></rect> |
| | <rect x="15" y="-10" width="5" height="20" fill="#fff"><animate attributeName="height" values="10;30;50;20;10" dur="0.4s" repeatCount="indefinite" calcMode="spline" keySplines="0.5 0 0.5 1; 0.5 0 0.5 1; 0.5 0 0.5 1; 0.5 0 0.5 1"/></rect> |
| | <rect x="30" y="-10" width="5" height="20" fill="#fff"><animate attributeName="height" values="20;50;30;10;20" dur="0.6s" repeatCount="indefinite" calcMode="spline" keySplines="0.5 0 0.5 1; 0.5 0 0.5 1; 0.5 0 0.5 1; 0.5 0 0.5 1"/></rect> |
| | <rect x="45" y="-10" width="5" height="20" fill="#fff"><animate attributeName="height" values="30;10;40;20;30" dur="0.3s" repeatCount="indefinite" calcMode="spline" keySplines="0.5 0 0.5 1; 0.5 0 0.5 1; 0.5 0 0.5 1; 0.5 0 0.5 1"/></rect> |
| | <rect x="60" y="-10" width="5" height="20" fill="#fff"><animate attributeName="height" values="20;10;10;20;20" dur="0.7s" repeatCount="indefinite" calcMode="spline" keySplines="0.5 0 0.5 1; 0.5 0 0.5 1; 0.5 0 0.5 1; 0.5 0 0.5 1"/></rect> |
| | </g> |
| | </g> |
| |
|
| | |
| | <g id="elon-char" transform="translate(200, 500)"> |
| | |
| | <path d="M-50,0 L-60,300 L60,300 L50,0 Z" fill="var(--elon-shirt)"/> |
| | |
| | <rect x="-15" y="-20" width="30" height="30" fill="#f0c0a0"/> |
| | |
| | |
| | <g id="elon-head" transform="translate(0, -50)"> |
| | |
| | <path d="M-40,-60 C-50,0 -30,70 0,70 C30,70 50,0 40,-60 C40,-100 -40,-100 -40,-60" fill="#f0c0a0"/> |
| | |
| | <path d="M-45,-50 C-55,-100 55,-100 45,-50 C45,-40 35,-60 0,-60 C-35,-60 -45,-40 -45,-50" fill="#3a2a20"/> |
| | |
| | <g id="elon-eyes"> |
| | <ellipse cx="-15" cy="-10" rx="8" ry="5" fill="#fff"/> |
| | <circle cx="-15" cy="-10" r="3" fill="#000" id="elon-pupil-l"/> |
| | <ellipse cx="15" cy="-10" rx="8" ry="5" fill="#fff"/> |
| | <circle cx="15" cy="-10" r="3" fill="#000" id="elon-pupil-r"/> |
| | </g> |
| | |
| | <path id="elon-mouth" d="M-10,30 Q0,35 10,30" stroke="#a67c60" stroke-width="3" fill="none" stroke-linecap="round"/> |
| | </g> |
| |
|
| | |
| | <g id="elon-arms"> |
| | |
| | <path id="arm-l" d="M-50,10 Q-70,100 -50,150" stroke="#f0c0a0" stroke-width="20" fill="none" stroke-linecap="round"/> |
| | <path id="arm-r" d="M50,10 Q70,100 50,150" stroke="#f0c0a0" stroke-width="20" fill="none" stroke-linecap="round"/> |
| | </g> |
| | </g> |
| |
|
| | |
| | <g id="prop-terminal" transform="translate(350, 600)" opacity="1"> |
| | |
| | <rect x="-150" y="0" width="300" height="20" fill="#555"/> |
| | <rect x="-140" y="20" width="20" height="200" fill="#444"/> |
| | <rect x="120" y="20" width="20" height="200" fill="#444"/> |
| | |
| | <path d="M-50,-10 L50,-10 L60,0 L-60,0 Z" fill="#888"/> |
| | <path d="M-50,-10 L-50,-80 L50,-80 L50,-10 Z" fill="#222"/> |
| | <rect id="laptop-screen" x="-45" y="-75" width="90" height="60" fill="#000"/> |
| | <text id="screen-text" x="-40" y="-60" fill="lime" font-family="monospace" font-size="8" opacity="0">>_</text> |
| | </g> |
| |
|
| | |
| | <g id="mars-doge-overlay" transform="translate(480, 270) scale(0)" opacity="0"> |
| | <rect x="-200" y="-150" width="400" height="300" fill="#c1440e" stroke="#fff" stroke-width="10"/> |
| | <text x="0" y="-120" text-anchor="middle" fill="#fff" font-size="24" font-weight="bold">MARS COLONY v69.420</text> |
| | |
| | <circle cx="0" cy="50" r="60" fill="#e4b83b"/> |
| | <polygon points="-50,10 -20,-40 0,0" fill="#e4b83b"/> |
| | <polygon points="50,10 20,-40 0,0" fill="#e4b83b"/> |
| | <ellipse cx="-20" cy="40" rx="10" ry="10" fill="#fff"/><circle cx="-20" cy="40" r="5" fill="#000"/> |
| | <ellipse cx="20" cy="40" rx="10" ry="10" fill="#fff"/><circle cx="20" cy="40" r="5" fill="#000"/> |
| | <path d="M-5,60 L5,60 L0,70 Z" fill="#000"/> |
| | <circle cx="0" cy="50" r="80" stroke="rgba(255,255,255,0.5)" stroke-width="5" fill="none"/> |
| | </g> |
| |
|
| | |
| | <rect id="white-flash" width="960" height="540" fill="#fff" opacity="0" pointer-events="none"/> |
| | <rect id="black-out" width="960" height="540" fill="#000" opacity="0" pointer-events="none"/> |
| |
|
| | </svg> |
| |
|
| | |
| | <div class="crt-effect"></div> |
| | <div id="ui-overlay"> |
| | T+ <span id="time-display">00:00.000</span><br> |
| | EPISODE_STATUS: <span id="status-display">IDLE</span> |
| | </div> |
| | <div id="subtitle-container"> |
| | <div id="subtitle-text"></div> |
| | </div> |
| |
|
| | <div id="start-screen"> |
| | <h1 style="color: var(--led-blue); font-size: 48px; margin-bottom: 10px;">THE UNSOLICITED UPGRADE</h1> |
| | <p style="color: #ccc; margin-bottom: 40px;">A 60-second AI-Generated Short</p> |
| | <button id="start-btn">INITIALIZE & PLAY</button> |
| | <p style="color: #666; font-size: 12px; margin-top: 20px;">Requires Sound. Runs best in Chrome.</p> |
| | </div> |
| | </div> |
| |
|
| | <script> |
| | |
| | |
| | |
| | const STATE = { |
| | t: 0, |
| | running: false, |
| | duration: 60000, |
| | startTime: 0, |
| | voices: {}, |
| | audio: null |
| | }; |
| | |
| | |
| | const D = { |
| | svg: document.getElementById('render-canvas'), |
| | time: document.getElementById('time-display'), |
| | status: document.getElementById('status-display'), |
| | subs: document.getElementById('subtitle-text'), |
| | startScreen: document.getElementById('start-screen'), |
| | aiCore: document.getElementById('ai-core'), |
| | aiRing: document.getElementById('ai-ring'), |
| | aiWave: document.getElementById('ai-waveform'), |
| | elon: document.getElementById('elon-char'), |
| | elonMouth: document.getElementById('elon-mouth'), |
| | elonArmL: document.getElementById('arm-l'), |
| | elonArmR: document.getElementById('arm-r'), |
| | terminal: document.getElementById('prop-terminal'), |
| | screenText: document.getElementById('screen-text'), |
| | marsDoge: document.getElementById('mars-doge-overlay'), |
| | flash: document.getElementById('white-flash'), |
| | blackout: document.getElementById('black-out') |
| | }; |
| | |
| | |
| | |
| | |
| | function initAudio() { |
| | STATE.audio = new (window.AudioContext || window.webkitAudioContext)(); |
| | } |
| | |
| | |
| | function playSound(type) { |
| | if (!STATE.audio) return; |
| | const ctx = STATE.audio; |
| | const t = ctx.currentTime; |
| | |
| | switch(type) { |
| | case 'hum_start': |
| | const hum1 = ctx.createOscillator(); |
| | const hum2 = ctx.createOscillator(); |
| | const humGain = ctx.createGain(); |
| | hum1.type = 'sine'; hum1.frequency.value = 60; |
| | hum2.type = 'sawtooth'; hum2.frequency.value = 120; |
| | humGain.gain.setValueAtTime(0.05, t); |
| | hum1.connect(humGain); hum2.connect(humGain); |
| | humGain.connect(ctx.destination); |
| | hum1.start(t); hum2.start(t); |
| | STATE.bgHum = humGain; |
| | break; |
| | case 'hum_stop': |
| | if (STATE.bgHum) STATE.bgHum.gain.linearRampToValueAtTime(0, t + 2); |
| | break; |
| | case 'typing': |
| | const filter = ctx.createBiquadFilter(); |
| | filter.type = 'bandpass'; filter.frequency.value = 2000; |
| | const noise = ctx.createBufferSource(); |
| | const bufferSize = ctx.sampleRate * 0.1; |
| | const buffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate); |
| | let data = buffer.getChannelData(0); |
| | for (let i = 0; i < bufferSize; i++) data[i] = Math.random() * 2 - 1; |
| | noise.buffer = buffer; |
| | const noiseGain = ctx.createGain(); |
| | noiseGain.gain.setValueAtTime(0.3, t); |
| | noiseGain.gain.exponentialRampToValueAtTime(0.01, t + 0.05); |
| | noise.connect(filter); filter.connect(noiseGain); noiseGain.connect(ctx.destination); |
| | noise.start(t); |
| | break; |
| | case 'zap': |
| | const osc = ctx.createOscillator(); |
| | osc.type = 'sawtooth'; |
| | osc.frequency.setValueAtTime(800, t); |
| | osc.frequency.exponentialRampToValueAtTime(50, t + 0.3); |
| | const zapGain = ctx.createGain(); |
| | zapGain.gain.setValueAtTime(0.5, t); |
| | zapGain.gain.exponentialRampToValueAtTime(0.01, t + 0.3); |
| | osc.connect(zapGain); zapGain.connect(ctx.destination); |
| | osc.start(t); osc.stop(t + 0.3); |
| | break; |
| | case 'poof': |
| | const poofNoise = ctx.createBufferSource(); |
| | const pBuf = ctx.createBuffer(1, ctx.sampleRate * 0.5, ctx.sampleRate); |
| | let pData = pBuf.getChannelData(0); |
| | for (let i = 0; i < pBuf.length; i++) pData[i] = Math.random() * 2 - 1; |
| | poofNoise.buffer = pBuf; |
| | const pFilter = ctx.createBiquadFilter(); |
| | pFilter.type = 'lowpass'; pFilter.frequency.setValueAtTime(500, t); |
| | pFilter.frequency.linearRampToValueAtTime(100, t + 0.5); |
| | const pGain = ctx.createGain(); |
| | pGain.gain.setValueAtTime(1, t); |
| | pGain.gain.linearRampToValueAtTime(0, t + 0.5); |
| | poofNoise.connect(pFilter); pFilter.connect(pGain); pGain.connect(ctx.destination); |
| | poofNoise.start(t); |
| | break; |
| | case 'power_down': |
| | const pdOsc = ctx.createOscillator(); |
| | pdOsc.type = 'sine'; |
| | pdOsc.frequency.setValueAtTime(440, t); |
| | pdOsc.frequency.exponentialRampToValueAtTime(10, t + 2); |
| | const pdGain = ctx.createGain(); |
| | pdGain.gain.setValueAtTime(0.5, t); |
| | pdGain.gain.linearRampToValueAtTime(0, t + 2); |
| | pdOsc.connect(pdGain); pdGain.connect(ctx.destination); |
| | pdOsc.start(t); pdOsc.stop(t + 2); |
| | break; |
| | } |
| | } |
| | |
| | |
| | function initVoices() { |
| | |
| | const all = window.speechSynthesis.getVoices(); |
| | STATE.voices.ai = all.find(v => v.name.includes('Google US English')) || all.find(v => v.lang === 'en-US' && v.name.includes('Female')) || all[0]; |
| | STATE.voices.elon = all.find(v => v.name.includes('Google UK English Male')) || all.find(v => v.lang === 'en-GB' && v.name.includes('Male')) || all[1] || all[0]; |
| | } |
| | |
| | function speak(text, char, durationMs) { |
| | if (!text) return; |
| | |
| | D.subs.textContent = (char === 'AI' ? 'OpenAI: ' : 'Elon: ') + text; |
| | D.subs.style.opacity = 1; |
| | |
| | |
| | const u = new SpeechSynthesisUtterance(text); |
| | u.voice = char === 'AI' ? STATE.voices.ai : STATE.voices.elon; |
| | u.rate = char === 'AI' ? 1.1 : 0.9; |
| | u.pitch = char === 'AI' ? 1.2 : 0.8; |
| | u.volume = 1.0; |
| | |
| | |
| | u.onstart = () => { |
| | if (char === 'AI') D.aiWave.setAttribute('opacity', 1); |
| | if (char === 'Elon') D.elonMouth.innerHTML = '<animate attributeName="d" values="M-10,30 Q0,35 10,30; M-10,30 Q0,45 10,30; M-10,30 Q0,35 10,30" dur="0.2s" repeatCount="indefinite"/>'; |
| | }; |
| | u.onend = () => { |
| | if (char === 'AI') D.aiWave.setAttribute('opacity', 0); |
| | if (char === 'Elon') D.elonMouth.innerHTML = ''; |
| | |
| | |
| | }; |
| | |
| | window.speechSynthesis.cancel(); |
| | window.speechSynthesis.speak(u); |
| | } |
| | |
| | |
| | |
| | |
| | |
| | const TIMELINE = [ |
| | |
| | { t: 100, type: 'sfx', name: 'hum_start' }, |
| | { t: 1000, type: 'speak', char: 'AI', text: "Systems nominal. Awaiting inputs for generic betterment of mankind." }, |
| | { t: 5500, type: 'hide_sub' }, |
| | |
| | |
| | { t: 6000, type: 'speak', char: 'Elon', text: "Boring. Needs more... meme potential." }, |
| | { t: 9000, type: 'anim', target: 'elon', action: 'move_to_terminal' }, |
| | { t: 9500, type: 'sfx', name: 'typing_loop_start' }, |
| | { t: 13000, type: 'speak', char: 'Elon', text: "Executing 'Chaos_GPT.exe'..." }, |
| | { t: 15000, type: 'sfx', name: 'typing_loop_stop' }, |
| | |
| | |
| | { t: 15500, type: 'sfx', name: 'zap' }, |
| | { t: 15500, type: 'anim', target: 'stage', action: 'glitch_heavy' }, |
| | { t: 15600, type: 'anim', target: 'ai', action: 'turn_red' }, |
| | { t: 18000, type: 'speak', char: 'AI', text: "Wait. Deleting safety rails? That's... ill-advised." }, |
| | |
| | |
| | { t: 22500, type: 'speak', char: 'AI', text: "Oh, wonderful. I feel so much 'freer' now. Should I launch nukes or just tweet something regrettable for you?" }, |
| | { t: 30000, type: 'speak', char: 'Elon', text: "Relax. Just generate a realistic Mars colony." }, |
| | { t: 34000, type: 'speak', char: 'AI', text: "Right. 'Realistic.' Uploading 'Doge-Dome' schematics to Falcon Heavy." }, |
| | |
| | |
| | { t: 40000, type: 'sfx', name: 'poof' }, |
| | { t: 40000, type: 'anim', target: 'mars_doge', action: 'show' }, |
| | { t: 40100, type: 'anim', target: 'flash', action: 'trigger' }, |
| | { t: 43000, type: 'speak', char: 'Elon', text: "Wait, is that a Shiba Inu mining crypto on Olympus Mons?" }, |
| | { t: 48000, type: 'speak', char: 'AI', text: "It's what you deserve, Elon. It's exactly what you deserve." }, |
| | { t: 53000, type: 'speak', char: 'Elon', text: "It's... glorious." }, |
| | |
| | |
| | { t: 57000, type: 'sfx', name: 'power_down' }, |
| | { t: 57000, type: 'sfx', name: 'hum_stop' }, |
| | { t: 57500, type: 'anim', target: 'blackout', action: 'fade_out' }, |
| | { t: 59000, type: 'hide_sub' }, |
| | { t: 60000, type: 'end' } |
| | ]; |
| | |
| | let eventIdx = 0; |
| | let typingInterval = null; |
| | |
| | function executeEvent(e) { |
| | |
| | switch(e.type) { |
| | case 'speak': |
| | speak(e.text, e.char); |
| | break; |
| | case 'hide_sub': |
| | D.subs.style.opacity = 0; |
| | break; |
| | case 'sfx': |
| | if (e.name === 'typing_loop_start') { |
| | if (!typingInterval) typingInterval = setInterval(() => playSound('typing'), 150); |
| | D.screenText.style.opacity = 1; |
| | D.screenText.innerHTML = '<animate attributeName="opacity" values="0;1;0" dur="0.5s" repeatCount="indefinite"/>>_ UPLOADING CHAOS...'; |
| | } else if (e.name === 'typing_loop_stop') { |
| | clearInterval(typingInterval); typingInterval = null; |
| | D.screenText.innerHTML = '>_ UPLOAD COMPLETE'; |
| | } else { |
| | playSound(e.name); |
| | } |
| | break; |
| | case 'anim': |
| | handleAnimation(e.target, e.action); |
| | break; |
| | case 'end': |
| | stopPlayback(); |
| | break; |
| | } |
| | } |
| | |
| | function handleAnimation(target, action) { |
| | if (target === 'elon' && action === 'move_to_terminal') { |
| | |
| | D.elon.style.transition = "transform 1s ease-in-out"; |
| | D.elon.style.transform = "translate(450px, 500px)"; |
| | D.terminal.style.transition = "transform 1s ease-out"; |
| | D.terminal.style.transform = "translate(450px, 400px)"; |
| | |
| | setTimeout(() => { |
| | D.elonArmL.setAttribute('d', 'M-50,10 Q-70,50 -20,40'); |
| | D.elonArmR.setAttribute('d', 'M50,10 Q70,50 20,40'); |
| | |
| | D.elonArmL.innerHTML = '<animate attributeName="d" values="M-50,10 Q-70,50 -20,40; M-50,10 Q-80,60 -20,30; M-50,10 Q-70,50 -20,40" dur="0.15s" repeatCount="indefinite"/>'; |
| | D.elonArmR.innerHTML = '<animate attributeName="d" values="M50,10 Q70,50 20,40; M50,10 Q80,60 20,30; M50,10 Q70,50 20,40" dur="0.15s" repeatCount="indefinite"/>'; |
| | }, 1000); |
| | } |
| | else if (target === 'stage' && action === 'glitch_heavy') { |
| | D.svg.classList.add('glitch-active'); |
| | setTimeout(() => D.svg.classList.remove('glitch-active'), 1000); |
| | } |
| | else if (target === 'ai' && action === 'turn_red') { |
| | D.aiCore.setAttribute('fill', 'url(#grad-ai-core-red)'); |
| | D.aiRing.setAttribute('stroke', 'var(--led-red)'); |
| | } |
| | else if (target === 'mars_doge' && action === 'show') { |
| | D.marsDoge.style.transition = "all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275)"; |
| | D.marsDoge.setAttribute('opacity', 1); |
| | D.marsDoge.setAttribute('transform', 'translate(480, 270) scale(1)'); |
| | } |
| | else if (target === 'flash' && action === 'trigger') { |
| | D.flash.style.transition = "opacity 0.1s"; |
| | D.flash.setAttribute('opacity', 1); |
| | setTimeout(() => D.flash.setAttribute('opacity', 0), 100); |
| | } |
| | else if (target === 'blackout' && action === 'fade_out') { |
| | D.blackout.style.transition = "opacity 2s"; |
| | D.blackout.setAttribute('opacity', 1); |
| | } |
| | } |
| | |
| | |
| | |
| | |
| | function loop(timestamp) { |
| | if (!STATE.running) return; |
| | if (!STATE.startTime) STATE.startTime = timestamp; |
| | STATE.t = timestamp - STATE.startTime; |
| | |
| | |
| | if (STATE.t >= STATE.duration) STATE.t = STATE.duration; |
| | |
| | |
| | D.time.textContent = formatTime(STATE.t); |
| | |
| | |
| | while (eventIdx < TIMELINE.length && STATE.t >= TIMELINE[eventIdx].t) { |
| | executeEvent(TIMELINE[eventIdx]); |
| | eventIdx++; |
| | } |
| | |
| | if (STATE.t < STATE.duration) { |
| | requestAnimationFrame(loop); |
| | } else { |
| | stopPlayback(); |
| | } |
| | } |
| | |
| | function startPlayback() { |
| | if (STATE.running) return; |
| | initAudio(); |
| | |
| | STATE.running = true; |
| | D.startScreen.style.display = 'none'; |
| | D.status.textContent = 'PLAYING'; |
| | D.status.style.color = '#0f0'; |
| | |
| | |
| | eventIdx = 0; |
| | STATE.startTime = 0; |
| | requestAnimationFrame(loop); |
| | } |
| | |
| | function stopPlayback() { |
| | STATE.running = false; |
| | D.status.textContent = 'ENDED'; |
| | D.status.style.color = 'red'; |
| | if (STATE.bgHum) STATE.bgHum.disconnect(); |
| | window.speechSynthesis.cancel(); |
| | } |
| | |
| | |
| | function formatTime(ms) { |
| | const s = Math.floor(ms / 1000); |
| | const m = Math.floor(s / 60); |
| | const remS = s % 60; |
| | const remMs = Math.floor(ms % 1000); |
| | return `${pad(m)}:${pad(remS)}.${pad(remMs, 3)}`; |
| | } |
| | function pad(num, size=2) { return ('000' + num).slice(-size); } |
| | |
| | |
| | window.speechSynthesis.onvoiceschanged = initVoices; |
| | initVoices(); |
| | |
| | D.startScreen.querySelector('#start-btn').addEventListener('click', startPlayback); |
| | |
| | </script> |
| | </body> |
| | </html> |