Initial save point: Scene 1 complete - Matrix rain, CRT flicker, typewriter text, FOLLOW THEM button

This commit is contained in:
avi
2026-05-06 15:51:31 -05:00
commit 93408a5dc0
2 changed files with 316 additions and 0 deletions

13
.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
# OS metadata
.DS_Store
Thumbs.db
# Editor configs
.vscode/
.idea/
# Future build artifacts
node_modules/
dist/
*.tmp
*.log

303
index.html Normal file
View File

@@ -0,0 +1,303 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Signal Elsewhere</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: #000;
overflow: hidden;
font-family: 'Courier New', monospace;
}
#matrixCanvas {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 1;
opacity: 1;
transition: opacity 1s ease;
}
#textContainer {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 2;
text-align: center;
font-size: 1.2rem;
line-height: 2;
visibility: hidden;
color: #00ff00;
}
.typewriter-line {
visibility: hidden;
white-space: nowrap;
margin: 0.5rem 0;
}
#followBtn {
position: fixed;
bottom: 5%;
left: 50%;
transform: translateX(-50%);
z-index: 3;
padding: 1rem 3rem;
font-size: 1.1rem;
background-color: #001100;
color: #00ff00;
border: 2px solid #00ff00;
cursor: pointer;
visibility: hidden;
opacity: 0;
pointer-events: none;
transition: opacity 0.5s ease, background-color 0.2s ease;
font-family: 'Courier New', monospace;
letter-spacing: 2px;
}
#followBtn:hover {
background-color: #003300;
}
.scene {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 4;
background-color: #000;
color: #00ff00;
display: none;
justify-content: center;
align-items: center;
font-family: 'Courier New', monospace;
font-size: 1.2rem;
text-align: center;
padding: 2rem;
opacity: 0;
transition: opacity 1s ease;
}
</style>
</head>
<body>
<canvas id="matrixCanvas"></canvas>
<div id="textContainer">
<div class="typewriter-line" id="line1"></div>
<div class="typewriter-line" id="line2"></div>
<div class="typewriter-line" id="line3"></div>
</div>
<button id="followBtn">FOLLOW THEM</button>
<div id="scene2" class="scene"></div>
<script>
// Canvas setup for Matrix rain
const canvas = document.getElementById('matrixCanvas');
const ctx = canvas.getContext('2d');
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// Matrix rain configuration
const fontSize = 14;
let columns = Math.floor(canvas.width / fontSize);
let drops = [];
const chars = '0123456789@#$%^&*()';
const rainDuration = 8000; // 8 seconds
let rainActive = false;
let rainStartTime = null;
// CRT flicker effect
function crtFlicker() {
let flickerCount = 0;
const maxFlickers = 3;
const flickerInterval = setInterval(() => {
ctx.fillStyle = flickerCount % 2 === 0 ? 'rgba(255, 255, 255, 0.15)' : 'rgba(0, 0, 0, 1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
flickerCount++;
if (flickerCount >= maxFlickers * 2) {
clearInterval(flickerInterval);
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, canvas.width, canvas.height);
startMatrixRain();
}
}, 100);
}
// Start Matrix rain animation
function startMatrixRain() {
rainActive = true;
rainStartTime = Date.now();
columns = Math.floor(canvas.width / fontSize);
drops = Array(columns).fill(0).map(() => Math.floor(Math.random() * canvas.height / fontSize));
drawMatrix();
}
// Draw Matrix rain frame
function drawMatrix() {
if (!rainActive) return;
// Create trail effect
ctx.fillStyle = 'rgba(0, 0, 0, 0.05)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#00ff00';
ctx.font = `${fontSize}px 'Courier New', monospace`;
for (let i = 0; i < columns; i++) {
const x = i * fontSize;
const y = drops[i] * fontSize;
// Chance to render "484" as a horizontal string in this column
if (Math.random() < 0.003) {
ctx.fillText('484', x, y);
} else {
const text = chars[Math.floor(Math.random() * chars.length)];
ctx.fillText(text, x, y);
}
// Reset drop when it goes off screen
if (y > canvas.height && Math.random() > 0.975) {
drops[i] = 0;
}
drops[i] += 0.5;
}
// End rain after duration
if (Date.now() - rainStartTime > rainDuration) {
fadeRainToBackground();
rainActive = false;
} else {
requestAnimationFrame(drawMatrix);
}
}
// Fade rain to background
function fadeRainToBackground() {
const canvasElem = document.getElementById('matrixCanvas');
let opacity = 1;
const fadeInterval = setInterval(() => {
opacity -= 0.02;
if (opacity <= 0.15) {
opacity = 0.15;
clearInterval(fadeInterval);
document.getElementById('textContainer').style.visibility = 'visible';
typewriter();
}
canvasElem.style.opacity = opacity;
}, 16);
}
// Typewriter text effect
const lines = [
"YOU ARE NOT IMAGINING THINGS",
"THE SYNCHRONICITIES YOU ARE EXPERIENCING HAVE ALWAYS BEEN THERE",
"THE ONLY THING THAT HAS CHANGED IS YOUR AWARENESS OF THEM"
];
let currentLine = 0;
let currentChar = 0;
function typewriter() {
if (currentLine >= lines.length) {
showButton();
return;
}
const lineElem = document.getElementById(`line${currentLine + 1}`);
lineElem.style.visibility = 'visible';
const lineText = lines[currentLine];
if (currentChar < lineText.length) {
lineElem.textContent += lineText[currentChar];
currentChar++;
setTimeout(typewriter, 60);
} else {
currentLine++;
currentChar = 0;
setTimeout(typewriter, 500);
}
}
// Show "FOLLOW THEM" button
function showButton() {
const btn = document.getElementById('followBtn');
btn.style.visibility = 'visible';
let opacity = 0;
const fadeIn = setInterval(() => {
opacity += 0.05;
if (opacity >= 1) {
opacity = 1;
clearInterval(fadeIn);
btn.style.pointerEvents = 'auto';
}
btn.style.opacity = opacity;
}, 30);
}
// Scene transition functions
function transitionToScene2() {
const canvas = document.getElementById('matrixCanvas');
const textContainer = document.getElementById('textContainer');
const btn = document.getElementById('followBtn');
const scene2 = document.getElementById('scene2');
let opacity = 1;
const fadeOut = setInterval(() => {
opacity -= 0.05;
if (opacity <= 0) {
opacity = 0;
clearInterval(fadeOut);
canvas.style.display = 'none';
textContainer.style.display = 'none';
btn.style.display = 'none';
loadScene2(scene2);
}
canvas.style.opacity = opacity;
textContainer.style.opacity = opacity;
btn.style.opacity = opacity;
}, 30);
}
// Placeholder for Scene 2 (to be expanded later)
function loadScene2(sceneElem) {
sceneElem.innerHTML = '<p>The signal grows stronger. The path reveals itself...</p>';
sceneElem.style.display = 'flex';
let opacity = 0;
const fadeIn = setInterval(() => {
opacity += 0.05;
if (opacity >= 1) {
opacity = 1;
clearInterval(fadeIn);
}
sceneElem.style.opacity = opacity;
}, 30);
}
// Initialize on load
window.addEventListener('load', () => {
setTimeout(crtFlicker, 1500);
});
// Button click handler
document.getElementById('followBtn').addEventListener('click', transitionToScene2);
</script>
</body>
</html>