# HANDSHAKE > *a love story in 64 hops* You are **SYN**. You want **ACK**. Root is the only one who can give it to you. Design doc: [DESIGN.md](DESIGN.md). ## Run it ```bash python3 /Users/johnking/Documents/handshake/serve.py 8123 ``` Then open `http://localhost:8123/`. Headphones — every frequency in this is the real frequency and half the mechanics are inaudible without them. `serve.py` is `http.server` plus `Cache-Control: no-store`, because ES modules cache hard and it makes every edit look like it did nothing. ## Controls | | | |---|---| | `WASD` | move | | `SPACE` | jump / hold 2600 Hz | | mouse | look (click to capture; dragging works too) | | `E` | use | | `M` | traceroute — the level select, and where you drop cargo | | `P` | philes — read what you've picked up | | `` ` `` | hop select (dev) | | `ESC` | release the mouse | It saves. Every hop completes a handshake with the world and writes to localStorage; the boot screen offers `R — RESUME` when there's something to resume. ## What's here Fourteen playable hops, each a different genre, one renderer. | id | hop | what it is | |---|---|---| | `coldboot` | 1 | FP tutorial in a 512-slot NIC ring buffer, then the DMA rips you out | | `stack` | 2–8 | 7-floor descent, **one verb per layer**: transcode, keep the session, reassemble in sequence, route within a hop budget, listen-before-transmit with real exponential backoff, ride the waveform. Stateful FIREWALL boss | | `timewait` | 9 | walking sim. beige room, 50 Hz hum, half-open connections in plastic chairs, FIN by the door | | `war` | 10–16 | EFnet twin-stick. netsplit riding, ops crowns, and a **trojan arms race** — four weapons the level patches 70 s after you first use one | | `smash` | 17–20 | 2D side-scroll `char buf[64]`. canary, return address, NOP sled, shellcode — then **NX lands** and you build a **ret2libc** chain out of other people's platforms | | `worm` | 21–28 | Morris worm RTS, 1-in-7 reinfection locked on. An **antidote you can release** that travels only over live links, while you are killing links | | `cuckoo` | 29–34 | LBL 1986 terminal. connection time is visibility. you get caught | | `board` | 35–44 | the BBS, over **three days**. busy signal, ANSI painting at live baud, ratios, NUP gate, ANSI bomb, the sysop watching you type, FidoNet, playable LORD *and* Trade Wars — then LAST CALL, and it closes | | `shell` | 43–44 | kruuna.helsinki.fi, 1991. always on, 38 users at once, talk(1) character by character, the comp.os.minix post, and a kernel compiling in the background while you use the shell | | `handshake` | 45–50 | five-boss rhythm arena. dial tone → DTMF → ANSam → V.8bis → carrier | | `copper` | 51–58 | FP through duct, load coils, a Strowger cathedral, a lineman's handset | | `bluebox` | 59–63 | playable 13-button blue box. 2600, KP/MF/ST, tandem stacking, then SS7 | | `climb` | 63→1 | on-rails reverse ascent, **one gate per era** testing the verb it taught you. Spectre is a real **split screen**, plus Rowhammer and Log4Shell | | `root` | 64 | the room is empty. then the ACK comes back — and on Tuesday, so does your FidoNet message | ## The three load-bearing ideas **TTL is the health bar and it only goes down.** 64 hops, no healing, ever. Levels aren't hard, they're *expensive*. **Bandwidth is resolution** — [`src/link.js`](src/link.js). One render target and one shader. Your baud rate sets the target height; at 1200 and below the scene renders as ASCII glyphs. `HS.link.setBaud(0..6)` in the console to see it. The V.8bis fight sets this for real. **Every header you drop is a piece of clothing** — `makeSyn()` in [`src/kit.js`](src/kit.js). Seven wireframe shells, one per OSI layer, shed as you descend. ## MTU Files taken off the BBS are lore, and lore is paid for in distance. Two is free; after that you don't fit down the next hop in one piece and every hop costs a TTL per extra fragment. You can put things down again from the traceroute map. That's the whole economy: everything worth reading costs you some of the distance you have left. ## Audio [`src/audio.js`](src/audio.js) — every tone is oscillators, nothing is sampled, and every frequency is the real one: 350+440 dial tone, the DTMF matrix, R1 MF pairs with real 100 ms KP / 60 ms digit timing, 2600 Hz, ANSam at 2100 Hz reversing phase every 450 ms, Bell 103 FSK. About 300 lines and zero megabytes. ## Layout ``` index.html shell, all CSS, importmap serve.py dev server, no-store src/main.js router, TTL, traceroute map, the loop src/link.js bandwidth → resolution. the post shader. src/audio.js every tone in the game src/term.js 80x25 cell terminal, pipe colours, CP437 blocks as geometry src/kit.js body/collision, camera rigs, SYN, labels, seeded rng src/input.js keys, pointer lock, the ANSI bomb's remap table src/hud.js status line, subtitles, title cards, NO CARRIER src/levels/*.js one file per hop vendor/ three.js r17x, vendored ``` Each level exports `{ id, hop, title, via, baud, mount(ctx), update(dt,t,ctx), unmount(ctx) }`. `ctx` hands it a fresh scene, camera and collision world, plus `say/card/hint/phile/ spendTTL/setBaud/next`. Genre switches are just different camera + controller modules over one renderer — that's the whole trick. ## Dev handles `HS` on `window`: `HS.goto(id)`, `HS.link.setBaud(n)`, `HS.game`, `HS.philes`, `HS.run(n)` — drives `n` frames by hand, because `requestAnimationFrame` is throttled to zero in a hidden tab and that makes the game look broken when it's only asleep. ## Skipped - **Multiplayer (`CHECKSUM`)** — the Journey-style anonymous pass-by. Needs a server; add when there's something to connect to. - **Playing both fragments** — fragmenting costs TTL, it doesn't split you into two packets you alternate between. Add when a level has a corridor narrow enough to need it. - **The 200 philes** — 74 written in [`src/philes.js`](src/philes.js), all readable with `P`. Adding the next 126 is a writing job, not a code job: put the text in that file, call `ctx.phile('id')` wherever it should be found. - **Era-generated music** — the design doc says each level's pad is built from that era's protocol. Right now they're chords. - **The retrain** — a mid-game renegotiation that drops your baud without warning. One system, touches every level after it. - Levels run 3–8 minutes against a design doc that asks for 5–20. The genres, mechanics and set pieces are all in — the *duration* is still short. See [STAGES.md](STAGES.md) for a per-hop breakdown of what's in and what's next.