haxtory/index.html
type-two 696b0c65e8 HANDSHAKE — 13 playable hops, one renderer
You are SYN. You want ACK. Root is the only one who can give it to you.

Three load-bearing ideas, all working:
  - TTL is the health bar and it only goes down. 64 hops, no healing.
  - Bandwidth is resolution. One render target, one shader; at 1200 baud
    and below the 3D scene renders as ASCII glyphs.
  - Every header you drop is a piece of clothing. Seven wireframe shells,
    one per OSI layer, shed on the way down.

Every frequency is the real frequency, synthesised, nothing sampled:
350+440 dial tone, the DTMF matrix, R1 MF pairs at real KP/digit timing,
2600 Hz, ANSam at 2100 reversing phase every 450 ms, Bell 103 FSK.

Thirteen hops, each a different genre over one renderer — FP tutorial,
OSI descent platformer, walking sim, EFnet twin-stick, 2D buffer overflow,
Morris worm RTS, LBL terminal stealth, a whole BBS with a playable LORD,
the five-boss handshake arena, the copper, a playable blue box, the climb,
and an empty room with an ACK in it.

No engine, no build step, one vendored three.js.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 15:34:58 +10:00

148 lines
8.1 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>HANDSHAKE — a love story in 64 hops</title>
<style>
:root{
--ink:#c9f4ff; --dim:#4d6b78; --hot:#ff4a5e; --warm:#ffb648; --green:#63ffa2;
--mono: ui-monospace, Menlo, "DejaVu Sans Mono", Consolas, monospace;
}
*{box-sizing:border-box}
html,body{margin:0;padding:0;height:100%;overflow:hidden;background:#000;color:var(--ink);
font-family:var(--mono);-webkit-font-smoothing:antialiased;}
canvas{display:block;position:fixed;inset:0;width:100%;height:100%;}
#term{z-index:2;display:none;image-rendering:pixelated;background:#000;}
.layer{position:fixed;inset:0;z-index:5;pointer-events:none;}
/* ── status line ───────────────────────────────────────────── */
#hud{position:fixed;top:0;left:0;right:0;z-index:6;pointer-events:none;
display:flex;gap:22px;padding:12px 16px;font-size:12px;letter-spacing:.16em;
color:var(--dim);text-shadow:0 0 12px #000,0 1px 0 #000;transition:opacity .4s;}
#ttl{color:var(--ink);min-width:66px}
#ttl.warn{color:var(--warm)} #ttl.crit{color:var(--hot);animation:pulse 1s steps(2) infinite}
@keyframes pulse{50%{opacity:.35}}
#link{color:#7fdcff}
#extra{margin-left:auto;color:#3d5560}
/* ── subtitles ─────────────────────────────────────────────── */
#sub{position:fixed;left:50%;bottom:8.5vh;transform:translateX(-50%);z-index:7;
max-width:62ch;text-align:center;font-size:15px;line-height:1.65;color:#e8f9ff;
text-shadow:0 2px 18px #000,0 0 40px #000;opacity:0;transition:opacity .35s;
pointer-events:none;letter-spacing:.02em;}
#sub.on{opacity:1}
#sub b{display:block;color:#6fe0ff;font-size:11px;letter-spacing:.28em;
margin-bottom:6px;font-weight:600;text-transform:uppercase;}
#sub i{color:#8fb6c4;font-style:normal;opacity:.8}
/* ── hints ─────────────────────────────────────────────────── */
#hint{position:fixed;right:16px;bottom:14px;z-index:6;font-size:11px;line-height:1.9;
color:#3f5d69;text-align:right;letter-spacing:.1em;opacity:0;transition:opacity .4s;
pointer-events:none;text-shadow:0 1px 0 #000;}
#hint k{color:#9fe6ff;background:#0d1a20;border:1px solid #1e3a45;border-radius:3px;
padding:1px 6px;margin-right:6px;font-size:10px;}
/* ── title cards ───────────────────────────────────────────── */
#card{position:fixed;inset:0;z-index:9;display:flex;flex-direction:column;
align-items:center;justify-content:center;gap:14px;opacity:0;pointer-events:none;
transition:opacity .7s;background:radial-gradient(ellipse at center,#0000 30%,#000c 100%);}
#card.on{opacity:1}
#card-t{font-size:min(9vw,62px);letter-spacing:.34em;color:#eafcff;font-weight:700;
text-shadow:0 0 60px #1a5c78,0 0 8px #000;margin-left:.34em;}
#card-s{font-size:12px;letter-spacing:.4em;color:#5b8494;text-transform:uppercase;}
#card.warm #card-t{color:#ffd9a8;text-shadow:0 0 60px #7a3c0c}
#card.warm #card-s{color:#9a6b40}
#card.phos #card-t{color:#63ffa2;text-shadow:0 0 60px #0a6b34;font-size:min(7vw,48px)}
#card.phos #card-s{color:#2f8f57}
/* ── NO CARRIER ────────────────────────────────────────────── */
#flash{position:fixed;inset:0;z-index:11;display:flex;align-items:center;justify-content:center;
background:#000;opacity:0;pointer-events:none;transition:opacity .18s;
font-size:min(8vw,54px);letter-spacing:.3em;color:#ff4a5e;}
#flash.on{opacity:1}
#flash.nc{color:#ff4a5e;text-shadow:0 0 40px #5c000c}
#flash.est{color:#63ffa2;text-shadow:0 0 60px #0a6b34}
/* below the title cards, above everything that is actually being played */
#fade{position:fixed;inset:0;z-index:8;background:#000;opacity:1;pointer-events:none;}
/* ── traceroute map ────────────────────────────────────────── */
#map{position:fixed;inset:0;z-index:12;display:none;align-items:center;justify-content:center;
background:#000000ee;backdrop-filter:blur(2px);}
.trace{font-size:13px;line-height:1.9;min-width:min(720px,92vw);}
.trace .hdr{color:#4d6b78;margin-bottom:14px;letter-spacing:.05em}
.trace .ftr{color:#3d5560;margin-top:16px;font-size:11px;letter-spacing:.1em}
.trace .dim{color:#2a3f48}
.row{display:flex;gap:16px;color:#243740;padding:1px 8px;border-radius:3px;}
.row.seen{color:#7fa8b8;cursor:pointer}
.row.seen:hover{background:#0c1a20;color:#c9f4ff}
.row.here{color:#ffd9a8}
.row.here .nm::after{content:' ←'}
.row .n{color:#3d5560;min-width:2.5em}
.row .nm{min-width:22ch;letter-spacing:.1em}
.row .via{color:#3d5560;flex:1;font-size:11px}
.row .ms{color:#2a3f48;font-size:11px}
/* ── boot ──────────────────────────────────────────────────── */
#boot{position:fixed;inset:0;z-index:20;display:flex;flex-direction:column;
align-items:center;justify-content:center;gap:22px;background:#000;cursor:pointer;}
#boot h1{font-size:min(12vw,88px);letter-spacing:.32em;margin:0 0 0 .32em;font-weight:700;
color:#eafcff;text-shadow:0 0 80px #17627f;}
#boot .tag{color:#4d6b78;letter-spacing:.36em;font-size:11px;text-transform:uppercase}
#boot .go{color:#63ffa2;letter-spacing:.28em;font-size:12px;margin-top:18px}
#boot .keys{color:#2a3f48;font-size:11px;line-height:2;text-align:center;letter-spacing:.1em}
#boot .keys k{color:#5b8494}
#cd{color:#ff4a5e;font-size:10px;letter-spacing:.3em;transition:opacity .2s}
/* ── dark ink, for the levels that are white ───────────────── */
body.ink-dark #hud{color:#8a9aa6;text-shadow:0 1px 0 #fff}
body.ink-dark #ttl{color:#14232c}
body.ink-dark #link{color:#1a6d8e}
body.ink-dark #extra{color:#9aacb8}
body.ink-dark #sub{color:#101c24;text-shadow:0 1px 16px #fff,0 0 40px #fff}
body.ink-dark #sub b{color:#1a6d8e}
body.ink-dark #sub i{color:#54707e}
body.ink-dark #hint{color:#8fa1ad;text-shadow:0 1px 0 #fff}
body.ink-dark #hint k{color:#14232c;background:#e8f0f5;border-color:#c2d2dc}
/* ── dev ───────────────────────────────────────────────────── */
#dev{position:fixed;left:12px;bottom:12px;z-index:13;display:none;max-width:360px}
#dev button{font-family:var(--mono);font-size:10px;background:#0b1519;color:#7fa8b8;
border:1px solid #1e3a45;border-radius:3px;padding:3px 7px;margin:2px;cursor:pointer;letter-spacing:.08em}
#dev button:hover{background:#12252d;color:#c9f4ff}
#dev .baud{margin-top:6px;color:#3d5560;font-size:10px}
</style>
</head>
<body>
<canvas id="gl"></canvas>
<canvas id="term"></canvas>
<div id="hud"><span id="ttl">TTL 64</span><span id="hop">hop 01/64</span><span id="link">v.90</span><span id="extra"></span></div>
<div id="sub"></div>
<div id="hint"></div>
<div id="card"><div id="card-t"></div><div id="card-s"></div></div>
<div id="flash"></div>
<div id="fade"></div>
<div id="map"></div>
<div id="dev"></div>
<div id="boot">
<h1>HANDSHAKE</h1>
<div class="tag">a love story in 64 hops</div>
<div class="keys">
<k>WASD</k> move &nbsp; <k>SPACE</k> jump &nbsp; <k>MOUSE</k> look &nbsp; <k>E</k> use<br>
<k>M</k> traceroute &nbsp; <k>ESC</k> release mouse &nbsp; <k>`</k> hop select<br><br>
headphones. every frequency in this is the real frequency.
</div>
<div class="go">CLICK OR PRESS ANY KEY TO DIAL</div>
<div id="cd">▮ NO CARRIER</div>
</div>
<script type="importmap">
{ "imports": { "three": "./vendor/three.module.js" } }
</script>
<script type="module" src="./src/main.js"></script>
</body>
</html>