forked from avi/signalelsewhere
Initial save point: Scene 1 complete - Matrix rain, CRT flicker, typewriter text, FOLLOW THEM button
This commit is contained in:
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal 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
303
index.html
Normal 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>
|
||||||
Reference in New Issue
Block a user