const toggle = document.getElementById('toggle'); const track = document.getElementById('track'); const knob = document.getElementById('knob'); const rays = document.getElementById('rays'); const stateLabel = document.getElementById('state-label'); const themeRoot = document.getElementById('themeRoot'); let isDay = true; const RAY_COUNT = 8; for (let i = 0; i < RAY_COUNT; i++) { const r = document.createElement('div'); r.className = 'ray'; r.style.transform = `rotate(${i * (360/RAY_COUNT)}deg)`; rays.appendChild(r); } function applyState() { if (isDay) { track.className = 'track day'; knob.className = 'knob day'; toggle.setAttribute('aria-checked', 'true'); themeRoot.classList.remove('night'); } else { track.className = 'track night'; knob.className = 'knob night'; toggle.setAttribute('aria-checked', 'false'); themeRoot.classList.add('night'); } } toggle.addEventListener('click', () => { isDay = !isDay; applyState(); }); toggle.addEventListener('keydown', e => { if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); isDay = !isDay; applyState(); } }); function copyClass() { navigator.clipboard.writeText(classOut.textContent).catch(() => {}); } // just some fun. console.log("%c👀 hi there!", "font-size:20px") function cureClick() { if (document.getElementById('cure-canvas')) return; const el = document.getElementById('interest'); el.textContent = "Actually... I'm feeling much better now!" startWater(); isDay = false; applyState(); setTimeout(() => { el.textContent = 'interest'; stopWater(); }, 10000); } let cureCanvas, cureCtx, cureAnimFrame; function startWater() { cureCanvas = document.createElement('canvas'); cureCanvas.id = 'cure-canvas'; cureCanvas.width = window.innerWidth; cureCanvas.height = window.innerHeight; Object.assign(cureCanvas.style, { position: 'fixed', top: 0, left: 0, pointerEvents: 'none', zIndex: -1, // behind everything opacity: '0', transition: 'opacity 1.4s ease' }); document.body.appendChild(cureCanvas); cureCtx = cureCanvas.getContext('2d'); requestAnimationFrame(() => { cureCanvas.style.opacity = '1'; }); const W = cureCanvas.width; const H = cureCanvas.height; // waterline sits at 38% from the top const waterlineY = H * 0.38; // floating particles below the surface const particles = Array.from({ length: 55 }, () => ({ x: Math.random() * W, y: waterlineY + 20 + Math.random() * (H - waterlineY - 20), r: 1 + Math.random() * 2.5, speedX: (Math.random() - 0.5) * 0.3, speedY: -0.08 - Math.random() * 0.18, // slow upward drift opacity: 0.08 + Math.random() * 0.18 })); function drawFrame(ts) { cureCtx.clearRect(0, 0, W, H); // ── sky above waterline (very subtle, mostly transparent) ────────── const sky = cureCtx.createLinearGradient(0, 0, 0, waterlineY); sky.addColorStop(0, 'rgba(190, 230, 255, 0.06)'); sky.addColorStop(1, 'rgba(190, 230, 255, 0.01)'); cureCtx.fillStyle = sky; cureCtx.fillRect(0, 0, W, waterlineY); // ── water body below waterline ───────────────────────────────────── const water = cureCtx.createLinearGradient(0, waterlineY, 0, H); water.addColorStop(0, 'rgba(80, 190, 220, 0.28)'); water.addColorStop(0.35, 'rgba(40, 140, 190, 0.22)'); water.addColorStop(0.7, 'rgba(20, 90, 150, 0.20)'); water.addColorStop(1, 'rgba(10, 50, 110, 0.22)'); cureCtx.fillStyle = water; cureCtx.fillRect(0, waterlineY, W, H - waterlineY); // ── caustic shimmer streaks ──────────────────────────────────────── const numStreaks = 10; for (let i = 0; i < numStreaks; i++) { const phase = (ts * 0.0004 + i * 0.63) % 1; const x = (i / numStreaks) * W + Math.sin(ts * 0.0008 + i) * 60; const yTop = waterlineY + 10 + Math.sin(ts * 0.001 + i * 1.3) * 18; const yBot = yTop + 40 + Math.sin(ts * 0.0007 + i * 0.9) * 30; const streak = cureCtx.createLinearGradient(x, yTop, x + 6, yBot); streak.addColorStop(0, 'rgba(200, 245, 255, 0)'); streak.addColorStop(0.4, `rgba(200, 245, 255, ${0.05 + 0.06 * Math.sin(ts * 0.002 + i)})`); streak.addColorStop(1, 'rgba(200, 245, 255, 0)'); cureCtx.fillStyle = streak; cureCtx.fillRect(x, yTop, 5, yBot - yTop); } // ── floating particles ───────────────────────────────────────────── particles.forEach(p => { p.x += p.speedX; p.y += p.speedY; // reset when reaching the surface if (p.y < waterlineY + 5) { p.y = H - 10; p.x = Math.random() * W; } if (p.x < 0) p.x = W; if (p.x > W) p.x = 0; const depth = (p.y - waterlineY) / (H - waterlineY); // 0 = surface, 1 = bottom cureCtx.beginPath(); cureCtx.arc(p.x, p.y, p.r, 0, Math.PI * 2); cureCtx.fillStyle = `rgba(180, 240, 255, ${p.opacity * (0.4 + 0.6 * (1 - depth))})`; cureCtx.fill(); }); // ── animated surface waves (2 layers, different speeds & amplitudes) ─ function drawWaveLine(amp, freq, speed, lineW, alpha) { cureCtx.beginPath(); for (let x = 0; x <= W; x += 3) { const y = waterlineY + Math.sin(x * freq + ts * speed) * amp + Math.sin(x * freq * 1.7 + ts * speed * 0.6) * (amp * 0.4); x === 0 ? cureCtx.moveTo(x, y) : cureCtx.lineTo(x, y); } cureCtx.strokeStyle = `rgba(210, 245, 255, ${alpha})`; cureCtx.lineWidth = lineW; cureCtx.stroke(); } // primary wave drawWaveLine(7, 0.012, 0.0018, 2.0, 0.70); // secondary wave (slightly offset phase) drawWaveLine(4, 0.018, 0.0024, 1.2, 0.40); // micro ripple drawWaveLine(2, 0.030, 0.0032, 0.7, 0.25); // ── foam/highlight on the crest ──────────────────────────────────── cureCtx.beginPath(); for (let x = 0; x <= W; x += 3) { const y = waterlineY + Math.sin(x * 0.012 + ts * 0.0018) * 7 + Math.sin(x * 0.020 + ts * 0.0024) * 3; x === 0 ? cureCtx.moveTo(x, y - 1) : cureCtx.lineTo(x, y - 1); } cureCtx.strokeStyle = 'rgba(255, 255, 255, 0.18)'; cureCtx.lineWidth = 1; cureCtx.stroke(); if (cureCanvas) cureAnimFrame = requestAnimationFrame(drawFrame); } cureAnimFrame = requestAnimationFrame(drawFrame); } function stopWater() { if (!cureCanvas) return; cureCanvas.style.opacity = '0'; cancelAnimationFrame(cureAnimFrame); setTimeout(() => { cureCanvas.remove(); cureCanvas = null; }, 1400); } function draculaClick() { if (document.getElementById('lightning-canvas')) return; const el = document.getElementById('interest'); el.textContent = 'children of the night'; startLightning(); isDay = false; applyState(); setTimeout(() => { el.textContent = 'interest'; stopLightning(); }, 10000); } let lightningCanvas, lightningCtx, lightningInterval, lightningFlashTimeout; function startLightning() { lightningCanvas = document.createElement('canvas'); lightningCanvas.id = 'lightning-canvas'; lightningCanvas.width = window.innerWidth; lightningCanvas.height = window.innerHeight; Object.assign(lightningCanvas.style, { position: 'fixed', top: 0, left: 0, pointerEvents: 'none', zIndex: 9999, opacity: '0', transition: 'opacity 0.3s ease' }); document.body.appendChild(lightningCanvas); lightningCtx = lightningCanvas.getContext('2d'); requestAnimationFrame(() => { lightningCanvas.style.opacity = '1'; }); function drawBolt(x1, y1, x2, y2, roughness, depth) { if (depth === 0) { lightningCtx.beginPath(); lightningCtx.moveTo(x1, y1); lightningCtx.lineTo(x2, y2); lightningCtx.stroke(); return; } const mx = (x1 + x2) / 2 + (Math.random() - 0.5) * roughness; const my = (y1 + y2) / 2 + (Math.random() - 0.5) * roughness; drawBolt(x1, y1, mx, my, roughness / 2, depth - 1); drawBolt(mx, my, x2, y2, roughness / 2, depth - 1); // random fork branch if (depth > 2 && Math.random() < 0.4) { const fx = mx + (Math.random() - 0.3) * roughness * 2; const fy = my + Math.random() * roughness * 2; lightningCtx.save(); lightningCtx.globalAlpha *= 0.5; drawBolt(mx, my, fx, fy, roughness / 2, depth - 2); lightningCtx.restore(); } } function flashStrike() { lightningCtx.clearRect(0, 0, lightningCanvas.width, lightningCanvas.height); const numBolts = 1 + Math.floor(Math.random() * 3); for (let i = 0; i < numBolts; i++) { const startX = lightningCanvas.width * (0.1 + Math.random() * 0.8); const endX = startX + (Math.random() - 0.5) * 300; const color = Math.random() < 0.6 ? `rgba(220, 180, 255, ${0.6 + Math.random() * 0.4})` // purple : `rgba(255, 255, 255, ${0.7 + Math.random() * 0.3})`; // white lightningCtx.strokeStyle = color; lightningCtx.lineWidth = 0.5 + Math.random() * 2; lightningCtx.shadowColor = color; lightningCtx.shadowBlur = 18 + Math.random() * 30; lightningCtx.globalAlpha = 1; drawBolt(startX, 0, endX, lightningCanvas.height * (0.4 + Math.random() * 0.5), 120, 7); } // ambient flash on the body document.body.style.transition = 'background 0.05s'; document.body.style.background = `rgba(80, 0, 120, 0.08)`; setTimeout(() => { document.body.style.background = ''; }, 80); // fade the bolt out lightningFlashTimeout = setTimeout(() => { lightningCtx.clearRect(0, 0, lightningCanvas.width, lightningCanvas.height); }, 80 + Math.random() * 120); } // irregular strike cadence — like real lightning function scheduleStrike() { flashStrike(); const delay = 600 + Math.random() * 2200; lightningInterval = setTimeout(scheduleStrike, delay); } scheduleStrike(); } function stopLightning() { if (!lightningCanvas) return; lightningCanvas.style.opacity = '0'; clearTimeout(lightningInterval); clearTimeout(lightningFlashTimeout); setTimeout(() => { lightningCanvas.remove(); lightningCanvas = null; }, 800); } function matrixClick() { const sub = document.getElementById('interest-sub'); sub.style.display = "none" const matrixsub = document.getElementById('matrix-hidden'); matrixsub.style.display = "block" setTimeout(() => { matrixsub.style.display = "none" sub.style.display = "block" }, 15000); } function bladerunnerClick() { if (document.getElementById('rain-canvas')) return; // already running, do nothing const el = document.getElementById('interest'); el.textContent = 'tears in rain'; startRain(); isDay = false applyState(); setTimeout(() => { el.textContent = "interest"; stopRain(); }, 10000); } let rainCanvas, rainCtx, rainInterval; function startRain() { rainCanvas = document.createElement('canvas'); rainCanvas.id = 'rain-canvas'; rainCanvas.width = window.innerWidth; rainCanvas.height = window.innerHeight; document.body.appendChild(rainCanvas); rainCtx = rainCanvas.getContext('2d'); requestAnimationFrame(() => { rainCanvas.style.opacity = '1'; }); const drops = Array.from({ length: 120 }, () => ({ x: Math.random() * rainCanvas.width, y: Math.random() * rainCanvas.height, speed: 8 + Math.random() * 7, length: 12 + Math.random() * 20, opacity: 0.2 + Math.random() * 0.4 })); rainInterval = setInterval(() => { rainCtx.clearRect(0, 0, rainCanvas.width, rainCanvas.height); drops.forEach(drop => { rainCtx.beginPath(); rainCtx.moveTo(drop.x, drop.y); rainCtx.lineTo(drop.x - 2, drop.y + drop.length); rainCtx.strokeStyle = `rgba(174, 214, 241, ${drop.opacity})`; rainCtx.lineWidth = 3; rainCtx.stroke(); drop.y += drop.speed; if (drop.y > rainCanvas.height) { drop.y = -drop.length; drop.x = Math.random() * rainCanvas.width; } }); }, 30); } function stopRain() { if (!rainCanvas) return; rainCanvas.style.opacity = '0'; setTimeout(() => { clearInterval(rainInterval); rainCanvas.remove(); rainCanvas = null; }, 800); } function unhideGame() { const alicegameSection = document.getElementById('alice'); const gameSection = document.getElementById('tetris'); if (alicegameSection.style.display == 'none') { alicegameSection.style.display = 'block'; } else { alicegameSection.style.display = 'none'; } gameSection.style.display = 'none'; } function unhideTetris() { const alicegameSection = document.getElementById('alice'); const gameSection = document.getElementById('tetris'); if (gameSection.style.display == 'none') { gameSection.style.display = 'block'; } else { gameSection.style.display = 'none'; } alicegameSection.style.display = 'none'; } function dragonClick() { const existing = document.getElementById('dragon-grid'); if (existing) { existing.remove(); return; } const grid = document.createElement('canvas'); grid.id = 'dragon-grid'; grid.width = window.innerWidth; grid.height = window.innerHeight; grid.style.transition = 'opacity 1s ease'; document.body.appendChild(grid); const ctx = grid.getContext('2d'); const img = new Image(); img.src = '/static/images/dragon.png'; img.onload = () => { const size = 250; const cols = Math.ceil(grid.width / size); const rows = Math.ceil(grid.height / size); for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { ctx.drawImage(img, c * size, r * size, size, size); } } setTimeout(() => { grid.style.opacity = '0'; setTimeout(() => grid.remove(), 1000); }, 300); }; } function unhide42() { const alicegameSection = document.getElementById('alice'); const tetrisSection = document.getElementById('tetris'); const experiencesection = document.getElementById('experience'); const educationsection = document.getElementById('education'); const automationsection = document.getElementById('automation'); const interestssection = document.getElementById('interests'); const opinionsection = document.getElementById('opinion'); const interestlistsection = document.getElementById('interestlist'); const hiddenssection = document.getElementById('secret'); if (hiddenssection.style.display == 'none') { hiddenssection.style.display = 'block'; } else { hiddenssection.style.display = 'none'; } alicegameSection.style.display = 'none'; tetrisSection.style.display = 'none'; experiencesection.style.display = 'none'; educationsection.style.display = 'none'; automationsection.style.display = 'none'; interestssection.style.display = 'none'; opinionsection.style.display = 'none'; interestlistsection.style.display = 'none'; }