Dvd Screensaver Simulator Apr 2026
.status span color: #8aaee0; background: #00000066; padding: 5px 14px; border-radius: 40px; backdrop-filter: blur(2px);
body background: #0a0f1e; min-height: 100vh; display: flex; justify-content: center; align-items: center; font-family: 'Segoe UI', 'Courier New', monospace, system-ui; padding: 20px;
.info-panel display: flex; justify-content: space-between; align-items: center; margin-top: 18px; gap: 20px; flex-wrap: wrap; background: rgba(10, 15, 30, 0.8); backdrop-filter: blur(4px); border-radius: 60px; padding: 8px 20px; border: 1px solid rgba(255, 255, 255, 0.2); dvd screensaver simulator
.reset-btn background: #2c2e3a; color: #f0c0a0;
.simulator-container background: #000000; border-radius: 32px; box-shadow: 0 20px 35px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.05); padding: 12px; No, we draw vector text + DVD logo effect
footer font-size: 0.7rem; text-align: center; margin-top: 14px; color: #5e6f8d; font-family: monospace;
button:hover background: #2c3e4e; transform: scale(0.97); l * (1 + s) : l +
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <title>DVD Screensaver Simulator | Classic Bouncing Logo</title> <style> * margin: 0; padding: 0; box-sizing: border-box; user-select: none; /* prevent accidental selection while watching */
// Dimensions (will handle resize properly) let width = 1000; let height = 600; // Logo parameters let logo = x: 180, y: 140, dx: 2.4, dy: 2.2, width: 120, height: 70 ; // Dynamic color (RGB) let currentColor = r: 255, g: 215, b: 0 ; // gold-ish start // Statistics let hitCount = 0; // total wall collisions (edge hit) let cornerPerfectCount = 0; // exactly corner collision (both x and y edges simultaneously) let animationId = null; let isPaused = false; // For smooth performance & frame independent? no need, just classic 60fps. // but we keep consistent // preload logo text as image? No, we draw vector text + DVD logo effect. // We'll draw a nice retro DVD rectangle with "DVD" text and shiny effect. // Helper: generate random vivid color (avoid dark / too dull) function randomVibrantColor() const hue = Math.random() * 360; // high saturation & medium-high lightness for vibrant look return r: 255, g: 215, b: 0 ; // but we'll do HSL -> easier // produce bright RGB from HSL (s=0.8, l=0.65) function getRandomBrightColor() const hue = Math.random() * 360; const sat = 0.75 + Math.random() * 0.2; const light = 0.6 + Math.random() * 0.25; const rgb = hslToRgb(hue, sat, light); return r: rgb[0], g: rgb[1], b: rgb[2] ; function hslToRgb(h, s, l) let r, g, b; if (s === 0) r = g = b = l; else const hue2rgb = (p, q, t) => if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1/6) return p + (q - p) * 6 * t; if (t < 1/2) return q; if (t < 2/3) return p + (q - p) * (2/3 - t) * 6; return p; ; const q = l < 0.5 ? l * (1 + s) : l + s - l * s; const p = 2 * l - q; r = hue2rgb(p, q, h / 360 + 1/3); g = hue2rgb(p, q, h / 360); b = hue2rgb(p, q, h / 360 - 1/3); return [Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)]; // Change color on any hit (wall collision) function changeLogoColor() const newCol = getRandomBrightColor(); currentColor.r = newCol.r; currentColor.g = newCol.g; currentColor.b = newCol.b; // special effect: corner flash (extra style) let cornerFlashTimer = 0; // frames remaining for corner glow effect // Update position and collision detection (returns if corner hit) function updatePosition() touchesRight) && (touchesTop // draw the DVD logo with gradient, bevel, text function drawLogo() const x = logo.x; const y = logo.y; const w = logo.width; const h = logo.height; // rounded rectangle background with current color and glossy effect ctx.save(); // drop shadow for depth ctx.shadowColor = "rgba(0,0,0,0.6)"; ctx.shadowBlur = 8; ctx.shadowOffsetX = 3; ctx.shadowOffsetY = 3; // main rounded rectangle ctx.beginPath(); ctx.roundRect(x, y, w, h, 14); ctx.fillStyle = `rgb($currentColor.r, $currentColor.g, $currentColor.b)`; ctx.fill(); // inner highlight / glossy bevel ctx.shadowBlur = 0; ctx.beginPath(); ctx.roundRect(x+2, y+2, w-4, h-4, 10); ctx.fillStyle = `rgba(255, 255, 255, 0.25)`; ctx.fill(); // top reflection gradient const grad = ctx.createLinearGradient(x, y, x, y + h*0.4); grad.addColorStop(0, 'rgba(255,255,255,0.5)'); grad.addColorStop(1, 'rgba(255,255,255,0)'); ctx.beginPath(); ctx.roundRect(x+2, y+2, w-4, h*0.45, 8); ctx.fillStyle = grad; ctx.fill(); // Border stroke with metallic effect ctx.beginPath(); ctx.roundRect(x, y, w, h, 14); ctx.lineWidth = 2.5; ctx.strokeStyle = `rgba(255, 255, 255, 0.7)`; ctx.stroke(); // TEXT "DVD" with retro style ctx.font = `bold $Math.floor(h * 0.5)px "Segoe UI", "Arial Black", "Impact", sans-serif`; ctx.shadowBlur = 3; ctx.shadowColor = "black"; ctx.fillStyle = "#FFFFFF"; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillText("DVD", x + w/2, y + h/2 + 2); // add tiny underline reflection ctx.font = `bold $Math.floor(h * 0.18)px monospace`; ctx.fillStyle = `rgba(0,0,0,0.5)`; ctx.fillText("VIDEO", x + w/2, y + h - 12); ctx.fillStyle = `rgba(255,255,240,0.9)`; ctx.fillText("VIDEO", x + w/2 - 1, y + h - 13); // If corner flash active, draw extra glowing aura if (cornerFlashTimer > 0) ctx.beginPath(); ctx.roundRect(x - 4, y - 4, w + 8, h + 8, 18); ctx.fillStyle = `rgba(255, 200, 80, 0.45)`; ctx.fill(); ctx.beginPath(); ctx.roundRect(x - 2, y - 2, w + 4, h + 4, 16); ctx.fillStyle = `rgba(255, 100, 0, 0.3)`; ctx.fill(); cornerFlashTimer--; ctx.restore(); // Additional pixel-perfect edges: optional subtle outline ctx.beginPath(); ctx.roundRect(x, y, w, h, 14); ctx.lineWidth = 1; ctx.strokeStyle = "rgba(0,0,0,0.4)"; ctx.stroke(); // draw background (grid + scanlines effect to emulate old CRT / DVD menu vibe) function drawBackground() // deep black with subtle radial gradient const grad = ctx.createLinearGradient(0, 0, width, height); grad.addColorStop(0, "#0a0c15"); grad.addColorStop(1, "#000000"); ctx.fillStyle = grad; ctx.fillRect(0, 0, width, height); // Grid lines (retro feel) ctx.strokeStyle = "rgba(30, 100, 200, 0.2)"; ctx.lineWidth = 1; for (let i = 0; i < width; i += 50) ctx.beginPath(); ctx.moveTo(i, 0); ctx.lineTo(i, height); ctx.stroke(); for (let i = 0; i < height; i += 50) ctx.beginPath(); ctx.moveTo(0, i); ctx.lineTo(width, i); ctx.stroke(); // faint scanlines effect ctx.fillStyle = "rgba(0, 0, 0, 0.08)"; for (let i = 0; i < height; i += 4) ctx.fillRect(0, i, width, 2); // Vignette const vignette = ctx.createRadialGradient(width/2, height/2, height*0.4, width/2, height/2, height*0.9); vignette.addColorStop(0, "rgba(0,0,0,0)"); vignette.addColorStop(1, "rgba(0,0,0,0.6)"); ctx.fillStyle = vignette; ctx.fillRect(0, 0, width, height); // corner signature style "DVD SCREENSAVER" ctx.font = "bold 14px monospace"; ctx.fillStyle = "#3f4b6e"; ctx.shadowBlur = 0; ctx.fillText("DVD SCREENSAVER SIMULATOR", 15, height - 12); ctx.fillStyle = "#6c7fa0"; ctx.fillText("∞", width - 30, height - 8); // update UI stats function updateStatsDisplay() document.getElementById('hitCount').innerText = hitCount; document.getElementById('cornerCount').innerText = cornerPerfectCount; // reset everything (position, stats, color, unpause) function resetSimulation() // reset position: center-ish but safe margins logo.x = (width / 2) - (logo.width / 2); logo.y = (height / 2) - (logo.height / 2); // random direction but ensure not zero logo.dx = (Math.random() > 0.5 ? 2.1 : -2.1) + (Math.random() * 0.8 - 0.4); logo.dy = (Math.random() > 0.5 ? 2.0 : -2.0) + (Math.random() * 0.8 - 0.4); if (Math.abs(logo.dx) < 1.2) logo.dx = logo.dx > 0 ? 1.8 : -1.8; if (Math.abs(logo.dy) < 1.2) logo.dy = logo.dy > 0 ? 1.6 : -1.6; // reset stats hitCount = 0; cornerPerfectCount = 0; cornerFlashTimer = 0; currentColor = r: 230, g: 180, b: 80 ; // warm amber // ensure position within bounds if (logo.x < 0) logo.x = 0; if (logo.y < 0) logo.y = 0; if (logo.x + logo.width > width) logo.x = width - logo.width; if (logo.y + logo.height > height) logo.y = height - logo.height; updateStatsDisplay(); // if paused, turn off pause to reset active state if (isPaused) isPaused = false; const pauseBtn = document.getElementById('togglePauseBtn'); pauseBtn.innerHTML = "⏸️ Pause"; // resize canvas handler (maintains ratio but we actually fixed dimensions, but we respect container's displayed size) // however canvas resolution stays 1000x600 for pixel perfect, but we also handle window resize for physical display // but collision uses internal width/height so it's consistent. function handleCanvasSize() // animation loop (draw, update) function animate() if (!isPaused) updatePosition(); // move and handle collisions & stats & color changes // drawing phase drawBackground(); drawLogo(); // draw little notification if paused if (isPaused) ctx.font = "bold 32px 'Segoe UI', system-ui"; ctx.fillStyle = "rgba(255,255,210,0.85)"; ctx.shadowBlur = 6; ctx.shadowColor = "black"; ctx.textAlign = "center"; ctx.fillText("⏸ PAUSED", width/2, height/2 - 40); ctx.font = "18px monospace"; ctx.fillStyle = "#aabbdd"; ctx.fillText("click ▶ Resume", width/2, height/2 + 30); ctx.textAlign = "left"; animationId = requestAnimationFrame(animate); // init event listeners, proper startup function init() // set canvas internal resolution canvas.width = width; canvas.height = height; handleCanvasSize(); window.addEventListener('resize', () => handleCanvasSize()); // reset to good starting position logo.x = (width / 2) - (logo.width / 2); logo.y = (height / 2) - (logo.height / 2); logo.dx = 2.3; logo.dy = 2.1; // make sure inside boundaries if (logo.x < 0) logo.x = 12; if (logo.y < 0) logo.y = 12; if (logo.x + logo.width > width) logo.x = width - logo.width - 5; if (logo.y + logo.height > height) logo.y = height - logo.height - 5; hitCount = 0; cornerPerfectCount = 0; cornerFlashTimer = 0; currentColor = r: 255, g: 100, b: 50 ; // orange-red start updateStatsDisplay(); // Buttons const pauseBtn = document.getElementById('togglePauseBtn'); const resetBtn = document.getElementById('resetBtn'); pauseBtn.addEventListener('click', () => isPaused = !isPaused; pauseBtn.innerHTML = isPaused ? "▶️ Resume" : "⏸️ Pause"; // small additional: if resume, no extra changes ); resetBtn.addEventListener('click', () => resetSimulation(); // if reset while paused, also update button text if (isPaused) isPaused = false; pauseBtn.innerHTML = "⏸️ Pause"; ); // start animation animate(); // Helper: canvas roundRect if (!CanvasRenderingContext2D.prototype.roundRect) CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) if (w < 2 * r) r = w / 2; if (h < 2 * r) r = h / 2; this.moveTo(x+r, y); this.lineTo(x+w-r, y); this.quadraticCurveTo(x+w, y, x+w, y+r); this.lineTo(x+w, y+h-r); this.quadraticCurveTo(x+w, y+h, x+w-r, y+h); this.lineTo(x+r, y+h); this.quadraticCurveTo(x, y+h, x, y+h-r); this.lineTo(x, y+r); this.quadraticCurveTo(x, y, x+r, y); return this; ; // start everything init(); )(); </script> </body> </html>