Go to file
type-two 10b6c8fdf5 Seven floors, seven jobs
THE STACK had seven layers that all played identically with a different
colour of danger, which is not a layer, it is a reskin. Each floor now
asks you to do the thing that layer actually does:

  7 APPLICATION   plain. the only polite floor.
  6 PRESENTATION  transcode. the same bytes in two encodings, RAW and
                  BASE64, and only one set of platforms is real at a time.
                  Q switches representation, in mid-air.
  5 SESSION       keep it alive. a platform you have stood on begins
                  closing the moment you leave it. standing still is the
                  mistake.
  4 TRANSPORT     reassemble. they arrived out of order; only the next
                  platform in sequence is solid, and the sequence has
                  nothing to do with where they are in the room.
  3 NETWORK       route. every platform is a router and every router costs
                  a hop out of five. overrun it and the floor ASLR
                  reshuffles under you.
  2 DATA LINK     listen before transmit. shared media, each platform busy
                  on its own cycle, and landing on a busy one collides —
                  with real exponential backoff, 0.35s then 0.7 then 1.4.
  1 PHYSICAL      be a signal. no platforms at all: forty-six segments of
                  a travelling wave you ride, attenuating down the wire.

The canaries and NX floors are retired — the hazard was the only idea each
floor had, and now the verb is.

Fixed in the rebuild: the miss floor was getting a lit edge strip like
every other platform, and it is ninety metres across, so it rendered as a
bright blue disc filling half the screen.

66 philes. All 13 hops swept clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 17:51:24 +10:00
.claude HANDSHAKE — 13 playable hops, one renderer 2026-08-02 15:34:58 +10:00
src Seven floors, seven jobs 2026-08-02 17:51:24 +10:00
vendor HANDSHAKE — 13 playable hops, one renderer 2026-08-02 15:34:58 +10:00
.gitignore HANDSHAKE — 13 playable hops, one renderer 2026-08-02 15:34:58 +10:00
DESIGN.md HANDSHAKE — 13 playable hops, one renderer 2026-08-02 15:34:58 +10:00
index.html Philes you can read, a save, and MTU that costs you something 2026-08-02 15:46:18 +10:00
README.md Philes you can read, a save, and MTU that costs you something 2026-08-02 15:46:18 +10:00
serve.py HANDSHAKE — 13 playable hops, one renderer 2026-08-02 15:34:58 +10:00
STAGES.md Seven floors, seven jobs 2026-08-02 17:51:24 +10:00

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.

Run it

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

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 resolutionsrc/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 clothingmakeSyn() in 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 — 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 — 40 written in src/philes.js, all readable with P. Adding the next 160 is a writing job, not a code job: put the text in that file, call ctx.phile('id') wherever it should be found.
  • 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.