haxtory/README.md
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

108 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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, click any hop you've reached |
| `` ` `` | hop select (dev) |
| `ESC` | release the mouse |
## What's here
Thirteen 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` | 28 | 7-floor third-person descent. NX floors, rate gates, canaries, ASLR reshuffles on death, stateful FIREWALL boss |
| `timewait` | 9 | walking sim. beige room, 50 Hz hum, half-open connections in plastic chairs, FIN by the door |
| `war` | 1016 | EFnet twin-stick. netsplit riding, ops crowns, Sub7 body-snatch |
| `smash` | 1720 | 2D side-scroll `char buf[64]`. canary, return address, NOP sled, shellcode |
| `worm` | 2128 | Morris worm RTS with the 1-in-7 reinfection locked on. you lose by winning |
| `cuckoo` | 2934 | LBL 1986 terminal. connection time is visibility. you get caught |
| `board` | 3544 | the BBS. busy signal, ANSI painting at live baud, ratios, NUP gate, ANSI bomb, the sysop watching you type, FidoNet, playable LORD |
| `handshake` | 4550 | five-boss rhythm arena. dial tone → DTMF → ANSam → V.8bis → carrier |
| `copper` | 5158 | FP through duct, load coils, a Strowger cathedral, a lineman's handset |
| `bluebox` | 5963 | playable 13-button blue box. 2600, KP/MF/ST, tandem stacking, then SS7 |
| `climb` | 63→1 | on-rails reverse ascent. Spectre split-timeline, Rowhammer, Log4Shell |
| `root` | 64 | the room is empty. then the ACK comes back |
## 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.
## 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.
- **MTU fragmentation** — BBS cargo lowers your MTU and it's on the HUD, but nothing splits you into two packets yet. Add when a level has a corridor narrow enough to need it.
- **The 200 philes** — the collectible framework is live (`ctx.phile`, `HS.philes`) with ~20 written. The rest is a writing job, not a code job.
- **Trade Wars** — the second door game. LORD is real and playable; TW2002 is "closed for maintenance since 1994", which is both a joke and true.
- Levels are one hop's worth of content each, not the full 520 minutes the design doc describes. The genres, mechanics and set pieces are all in — the *duration* isn't.