This commit is contained in:
2025-08-24 19:48:41 +01:00
parent 7fb8482d60
commit 0709c3b6d8

View File

@@ -2,26 +2,24 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>Boot Sequence — Wake up, Neo</title> <title>Boot Sequence — Incoming message…</title>
<style> <style>
:root { --bg:#000; --green:#33ff66; } :root { --bg:#000; --green:#33ff66; }
* { box-sizing: border-box; } * { box-sizing: border-box; }
html, body { height: 100%; } html, body { height: 100%; }
body { body {
margin: 0; background: var(--bg); color: var(--green); margin: 0; background: var(--bg); color: var(--green);
font: 16px/1.5 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font: clamp(14px, 2.2vw, 16px)/1.5 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
} }
#viewport { position: fixed; inset: 0; padding: 24px 16px; } #viewport { position: fixed; inset: 0; padding: 24px 16px; }
#screen { width:100%; height:100%; overflow:auto; white-space:pre-wrap; #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); 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, 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::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)); } 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); } body.crt { animation: crt-flicker 8s infinite steps(1); }
@keyframes crt-flicker { @keyframes crt-flicker {
0%,100% { filter:none; } 0%,100% { filter:none; }
@@ -29,7 +27,6 @@
4% { filter: brightness(.9) contrast(1.1); } 4% { filter: brightness(.9) contrast(1.1); }
6% { filter: brightness(1.1); } 6% { filter: brightness(1.1); }
} }
/* Quick glitch class toggled by JS */
.glitch { animation: glitch-burst .18s steps(1) 2; } .glitch { animation: glitch-burst .18s steps(1) 2; }
@keyframes glitch-burst { @keyframes glitch-burst {
0% { transform: translateY(0); filter: contrast(1.2) brightness(1.2); } 0% { transform: translateY(0); filter: contrast(1.2) brightness(1.2); }
@@ -41,67 +38,23 @@
.cursor.flash { animation: blink-fast .3s steps(1) infinite; } .cursor.flash { animation: blink-fast .3s steps(1) infinite; }
@keyframes blink { 50% { opacity: 0; } } @keyframes blink { 50% { opacity: 0; } }
@keyframes blink-fast { 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> </style>
</head> </head>
<body class="crt"> <body class="crt">
<div id="viewport"> <div id="viewport">
<div id="screen" role="log" aria-live="polite" aria-atomic="false"></div> <div id="screen" role="log" aria-live="polite" aria-atomic="false"></div>
</div> </div>
<div class="help">Click or press <kbd>Space</kbd> to fastforward · Press <kbd>M</kbd> to toggle sound</div>
<script> <script>
(() => { (() => {
const screen = document.getElementById('screen'); const screen = document.getElementById('screen');
/* ---------------- WebAudio: tiny synth ---------------- */ function randomHex(len=32){ return [...crypto.getRandomValues(new Uint8Array(Math.ceil(len/2)))].map(b=>b.toString(16).padStart(2,'0')).join('').slice(0,len); }
const AudioCtx = window.AudioContext || window.webkitAudioContext; function newline(){ screen.append(document.createElement('br')); screen.scrollTop = screen.scrollHeight; }
let ctx = null; // created on first user gesture function append(text=''){ screen.append(document.createTextNode(text)); newline(); }
let soundOn = true; function sleep(ms){ return new Promise(r => setTimeout(r, ms)); }
function bodyGlitch(){ document.body.classList.add('glitch'); setTimeout(()=> document.body.classList.remove('glitch'), 180); }
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 = [ const bootLines = [
'Phoenix BIOS v4.0 Release 6.0', 'Phoenix BIOS v4.0 Release 6.0',
'Copyright (C) 1985-2025 Phoenix Technologies Ltd.', 'Copyright (C) 1985-2025 Phoenix Technologies Ltd.',
@@ -128,67 +81,45 @@
'', '',
]; ];
function randomHex(len=32){ return [...crypto.getRandomValues(new Uint8Array(len/2))].map(b=>b.toString(16).padStart(2,'0')).join(''); } // Only 5 noise lines before establishing link
const noiseLines = Array.from({length:20}, () => `0x${randomHex(16)} ${randomHex(32)} ${randomHex(8)}`); const noiseLines = Array.from({length: 5}, () => `0x${randomHex(16)} ${randomHex(32)} ${randomHex(8)}`);
const lines = [...bootLines, ...noiseLines, '', '>>> establishing link…', '']; const lines = [...bootLines, ...noiseLines, '', '>>> establishing link…', ''];
let speed = 1; // 1 = normal, 3 = fast let speed = 1;
let aborted = false; 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){ async function typeLine(line){
for (let i=0; i<line.length; i++){ for (let i=0; i<line.length; i++){
if (aborted) return; if (aborted) return;
screen.append(document.createTextNode(line[i])); screen.append(document.createTextNode(line[i]));
typeClick();
screen.scrollTop = screen.scrollHeight; screen.scrollTop = screen.scrollHeight;
await sleep(speed>1 ? 3 : 12 + Math.random()*30); await sleep(speed>1 ? 3 : 12 + Math.random()*30);
} }
newline(); newline();
if (line.includes('[ OK ]')) okBeep(); if (line.startsWith('[WARN]')) bodyGlitch();
if (line.startsWith('[WARN]')) { warnBuzz(); bodyGlitch(); }
} }
function sleep(ms){ return new Promise(r => setTimeout(r, ms)); } document.addEventListener('click', () => { speed = 3; });
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(){ (async function run(){
await sleep(300); await sleep(200);
bootChirp();
for (const line of lines){ for (const line of lines){
if (aborted) return; if (aborted) return;
if (Math.random() < 0.15) { append(line); await sleep(60/speed); } if (Math.random() < 0.15) { append(line); await sleep(60/speed); }
else { await typeLine(line); } else { await typeLine(line); }
if (Math.random() < 0.07) bodyGlitch(); if (Math.random() < 0.07) bodyGlitch();
} }
await sleep(400); await sleep(300);
// Final message const final = 'Incoming message... Andy is a cock!';
const final = 'incoming messag... Andy is a cock!';
const span = document.createElement('span'); const span = document.createElement('span');
screen.append(span); screen.append(span);
for (let i=0; i<final.length; i++){ for (let i=0; i<final.length; i++){
span.textContent += final[i]; span.textContent += final[i];
screen.scrollTop = screen.scrollHeight; screen.scrollTop = screen.scrollHeight;
typeClick(); await sleep(110 / speed);
await sleep(120 / speed);
} }
finale();
const cursor = document.createElement('span'); const cursor = document.createElement('span');
cursor.className = 'cursor flash'; cursor.className = 'cursor flash';
screen.append(cursor); screen.append(cursor);