201 lines
8.3 KiB
HTML
201 lines
8.3 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>Boot Sequence — Wake up, Neo</title>
|
||
<style>
|
||
:root { --bg:#000; --green:#33ff66; }
|
||
* { box-sizing: border-box; }
|
||
html, body { height: 100%; }
|
||
body {
|
||
margin: 0; background: var(--bg); color: var(--green);
|
||
font: 16px/1.5 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||
}
|
||
#viewport { position: fixed; inset: 0; padding: 24px 16px; }
|
||
#screen { width:100%; height:100%; overflow:auto; white-space:pre-wrap;
|
||
text-shadow: 0 0 10px rgba(51,255,102,.4), 0 0 20px rgba(51,255,102,.2), 0 0 2px rgba(51,255,102,.6);
|
||
}
|
||
/* CRT scanlines + bloom */
|
||
body.crt::before, body.crt::after { content:""; position: fixed; left:0; right:0; pointer-events:none; }
|
||
body.crt::before { top:0; bottom:0; background: linear-gradient(rgba(0,0,0,0) 50%, rgba(0,255,120,0.05) 50%); background-size:100% 4px; mix-blend-mode: overlay; }
|
||
body.crt::after { top:-2px; bottom:-2px; background: radial-gradient(circle at 50% 20%, rgba(0,255,153,0.12), rgba(0,0,0,0) 35%), linear-gradient(to bottom, rgba(255,255,255,0.02), rgba(0,0,0,0) 30%, rgba(0,0,0,0) 70%, rgba(255,255,255,0.02)); }
|
||
|
||
/* Subtle global flicker */
|
||
body.crt { animation: crt-flicker 8s infinite steps(1); }
|
||
@keyframes crt-flicker {
|
||
0%,100% { filter:none; }
|
||
2% { filter: brightness(1.2) contrast(1.05) saturate(1.1); }
|
||
4% { filter: brightness(.9) contrast(1.1); }
|
||
6% { filter: brightness(1.1); }
|
||
}
|
||
/* Quick glitch class toggled by JS */
|
||
.glitch { animation: glitch-burst .18s steps(1) 2; }
|
||
@keyframes glitch-burst {
|
||
0% { transform: translateY(0); filter: contrast(1.2) brightness(1.2); }
|
||
50% { transform: translateY(1px); filter: contrast(1.5) brightness(0.8); }
|
||
100% { transform: translateY(0); filter:none; }
|
||
}
|
||
|
||
.cursor { display:inline-block; width:.7ch; height:1.05em; vertical-align:-0.15em; margin-left:2px; background: currentColor; animation: blink 1s steps(1) infinite; }
|
||
.cursor.flash { animation: blink-fast .3s steps(1) infinite; }
|
||
@keyframes blink { 50% { opacity: 0; } }
|
||
@keyframes blink-fast { 50% { opacity: 0; } }
|
||
|
||
.help { position: fixed; bottom: 10px; right: 12px; font-size: 12px; opacity:.6 }
|
||
.muted { opacity: .4 }
|
||
kbd { border:1px solid currentColor; padding:1px 4px; border-radius:3px; font-size:.9em }
|
||
a { color: inherit; }
|
||
</style>
|
||
</head>
|
||
<body class="crt">
|
||
<div id="viewport">
|
||
<div id="screen" role="log" aria-live="polite" aria-atomic="false"></div>
|
||
</div>
|
||
<div class="help">Click or press <kbd>Space</kbd> to fast‑forward · Press <kbd>M</kbd> to toggle sound</div>
|
||
|
||
<script>
|
||
(() => {
|
||
const screen = document.getElementById('screen');
|
||
|
||
/* ---------------- WebAudio: tiny synth ---------------- */
|
||
const AudioCtx = window.AudioContext || window.webkitAudioContext;
|
||
let ctx = null; // created on first user gesture
|
||
let soundOn = true;
|
||
|
||
function ensureAudio() {
|
||
if (!ctx) ctx = new AudioCtx();
|
||
if (ctx.state === 'suspended') ctx.resume();
|
||
}
|
||
|
||
function tone({freq=440, type='square', dur=0.03, vol=0.02}){
|
||
if (!soundOn || !ctx) return;
|
||
const t0 = ctx.currentTime;
|
||
const osc = ctx.createOscillator();
|
||
const gain = ctx.createGain();
|
||
osc.type = type; osc.frequency.value = freq;
|
||
gain.gain.setValueAtTime(0, t0);
|
||
gain.gain.linearRampToValueAtTime(vol, t0 + 0.002);
|
||
gain.gain.exponentialRampToValueAtTime(0.0005, t0 + Math.max(0.01, dur));
|
||
osc.connect(gain).connect(ctx.destination);
|
||
osc.start(t0); osc.stop(t0 + dur + 0.02);
|
||
}
|
||
|
||
function noise({dur=0.12, vol=0.02}){
|
||
if (!soundOn || !ctx) return;
|
||
const bufferSize = Math.floor(ctx.sampleRate * dur);
|
||
const buffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate);
|
||
const data = buffer.getChannelData(0);
|
||
for (let i=0; i<bufferSize; i++) data[i] = (Math.random()*2-1) * 0.8;
|
||
const src = ctx.createBufferSource();
|
||
const gain = ctx.createGain();
|
||
gain.gain.value = vol;
|
||
src.buffer = buffer;
|
||
src.connect(gain).connect(ctx.destination);
|
||
src.start();
|
||
}
|
||
|
||
function typeClick(){ if (Math.random() < 0.6) tone({freq:120+Math.random()*120, dur:0.015, vol:0.015}); }
|
||
function okBeep(){ tone({freq:880, dur:0.04, vol:0.03}); }
|
||
function warnBuzz(){ noise({dur:0.18, vol:0.03}); tone({freq:140, type:'sawtooth', dur:0.08, vol:0.02}); }
|
||
function bootChirp(){ tone({freq:440, dur:0.06, vol:0.03}); tone({freq:660, dur:0.06, vol:0.03}); }
|
||
function finale(){ [392,523,784].forEach((f,i)=> setTimeout(()=> tone({freq:f, dur:0.08, vol:0.03}), i*90)); }
|
||
|
||
/* ---------------- Visuals & content ---------------- */
|
||
const bootLines = [
|
||
'Phoenix BIOS v4.0 Release 6.0',
|
||
'Copyright (C) 1985-2025 Phoenix Technologies Ltd.',
|
||
'CPU: ZION-9 3.60GHz (4 CPUs) ~3.6GHz',
|
||
'Memory Test: 65536K OK',
|
||
'Detecting IDE Drives ...',
|
||
'Primary Master: ST3250820AS 250GB',
|
||
'Primary Slave : TSSTcorp CDDVDW SH-222',
|
||
'S.M.A.R.T. Status: OK',
|
||
'Initializing USB Controllers ... Done.',
|
||
'Enabling Shadow RAM ... Done.',
|
||
'Booting from device: /dev/ttyS0',
|
||
'',
|
||
'Loading kernel v0.1.37 ....................... OK',
|
||
'Mounting / (ext4) read-only .................. OK',
|
||
'Starting init ',
|
||
'[ OK ] Started udev kernel device manager.',
|
||
'[ OK ] Reached target Local File Systems.',
|
||
'[ OK ] Reached target Network (online).',
|
||
'[ OK ] Started OpenSSH Daemon.',
|
||
'[ OK ] Started Matrix Link.',
|
||
'[WARN] entropy pool low, seeding...',
|
||
'[ OK ] Decrypting payload...',
|
||
'',
|
||
];
|
||
|
||
function randomHex(len=32){ return [...crypto.getRandomValues(new Uint8Array(len/2))].map(b=>b.toString(16).padStart(2,'0')).join(''); }
|
||
const noiseLines = Array.from({length:20}, () => `0x${randomHex(16)} ${randomHex(32)} ${randomHex(8)}`);
|
||
const lines = [...bootLines, ...noiseLines, '', '>>> establishing link…', ''];
|
||
|
||
let speed = 1; // 1 = normal, 3 = fast
|
||
let aborted = false;
|
||
|
||
function newline(){ screen.append(document.createElement('br')); screen.scrollTop = screen.scrollHeight; }
|
||
|
||
function append(text=''){
|
||
screen.append(document.createTextNode(text));
|
||
newline();
|
||
}
|
||
|
||
async function typeLine(line){
|
||
for (let i=0; i<line.length; i++){
|
||
if (aborted) return;
|
||
screen.append(document.createTextNode(line[i]));
|
||
typeClick();
|
||
screen.scrollTop = screen.scrollHeight;
|
||
await sleep(speed>1 ? 3 : 12 + Math.random()*30);
|
||
}
|
||
newline();
|
||
if (line.includes('[ OK ]')) okBeep();
|
||
if (line.startsWith('[WARN]')) { warnBuzz(); bodyGlitch(); }
|
||
}
|
||
|
||
function sleep(ms){ return new Promise(r => setTimeout(r, ms)); }
|
||
function bodyGlitch(){ document.body.classList.add('glitch'); setTimeout(()=> document.body.classList.remove('glitch'), 180); }
|
||
|
||
// fast-forward & sound controls
|
||
const fast = () => { speed = 3; };
|
||
document.addEventListener('click', () => { ensureAudio(); fast(); });
|
||
document.addEventListener('keydown', (e) => {
|
||
if (e.code === 'Space') { e.preventDefault(); ensureAudio(); speed = speed===1?3:1; }
|
||
if (e.key === 'Escape') { aborted = true; }
|
||
if (e.key.toLowerCase() === 'm') { soundOn = !soundOn; document.querySelector('.help').classList.toggle('muted', !soundOn); }
|
||
});
|
||
|
||
(async function run(){
|
||
await sleep(300);
|
||
bootChirp();
|
||
for (const line of lines){
|
||
if (aborted) return;
|
||
if (Math.random() < 0.15) { append(line); await sleep(60/speed); }
|
||
else { await typeLine(line); }
|
||
if (Math.random() < 0.07) bodyGlitch();
|
||
}
|
||
await sleep(400);
|
||
|
||
// Final message
|
||
const final = 'incoming messag... Andy is a cock!';
|
||
const span = document.createElement('span');
|
||
screen.append(span);
|
||
for (let i=0; i<final.length; i++){
|
||
span.textContent += final[i];
|
||
screen.scrollTop = screen.scrollHeight;
|
||
typeClick();
|
||
await sleep(120 / speed);
|
||
}
|
||
finale();
|
||
const cursor = document.createElement('span');
|
||
cursor.className = 'cursor flash';
|
||
screen.append(cursor);
|
||
screen.scrollTop = screen.scrollHeight;
|
||
})();
|
||
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html> |