diff --git a/card.html b/card.html new file mode 100644 index 0000000..599749c --- /dev/null +++ b/card.html @@ -0,0 +1,167 @@ + + + + + +HANDSHAKE — model card + + + + +
+ +
+
model card
+

HANDSHAKE

+
a love story in 64 hops · monsterrobot
+
+ +

You are SYN, a TCP synchronise packet. Your entire nature is arriving somewhere +and asking are you there, and nobody has ever replied. You have heard there is +someone at the top of the machine who can receive anything. Their name is +root.

+ +

The modern network is a fortress, so you go down to go up — back through +fifty years of telecommunications, because the deeper you go the older it gets, and +the older it gets the more it trusts you.

+ +▶ play it + +

At a glance

+ + + + + + + + +
TypeSingle-player browser game. Fourteen hops, each a different genre, over one renderer.
LengthRoughly 50–70 minutes for one pass. It saves, and it is designed to be put down.
InputKeyboard and mouse. No gamepad. No touch — it will load on a phone and will not play well on one.
AudioRequired. Several mechanics are inaudible without it, and one boss is unbeatable if you cannot hear the beat.
Runs onAny browser with WebGL2 and Web Audio. No install, no account, no server. Progress is in localStorage.
DataNone leaves your machine. There is no analytics, no telemetry, no network call after the page loads.
Built with~9,000 lines of hand-written JavaScript and one vendored copy of three.js. No engine, no build step, no dependencies.
+ +

The three ideas it is built on

+ +

TTL is the health bar, and it only goes down

+

You start with 64 and every hop costs one. There is no healing item, no shrine, no +pickup. Levels are not hard — they are expensive. Every optional thing +you look at costs some of the distance you have left.

+ +

Bandwidth is resolution

+

Your connection speed is wired directly to the renderer. At v.90 the game is full 3D; +at 1200 baud and below it renders as ASCII glyphs, drawn one character at a time. You +earn your rate by performing well in a dial-up handshake fight, and there is no retry +prompt. The game gets warmer and more human as it gets technically worse, which is the +argument the whole thing is making.

+ +

Every header you drop is a piece of clothing

+

You descend the OSI model wearing seven layers. Each floor takes one off. At layer +seven you are wrapped in TLS and nothing can see you; at layer one you are bare voltage +on a wire.

+ +

What in this is true

+

Most of it. Specifically:

+ + +
+

The soundtrack is derived, not composed. No level has a chord + somebody picked because it sounded nice. Each bed is built from a real number belonging + to its era: the 1 kHz telecoms reference tone, 50 Hz mains, the 1200 Hz V.22bis carrier + folded down, 0x90 read as 144 Hz so the NOP sled has a pitch, 20 Hz ringing + current, the six MF tones of a blue box, and 2600 Hz folded six times until it is a low + room tone. Some of it is beautiful and some of it is ugly. A 1980 telephone exchange was + not tuned to A=440.

+
+ +

What in this is invented

+ + +

What it does not do

+
+ +
+ +

Content

+

No graphic content. Mild profanity, none of it aimed at anyone. It is about +computer crime in the sense that a documentary is: it explains how things worked and does +not teach anything that has been usable for thirty years. Two levels are quietly about +loss, and one of them is about a room where nothing happens on purpose.

+ +

How it was made

+

Written by Claude Opus 5 (Anthropic) in an extended session with John King, +from a one-paragraph idea to the finished thing — design document, engine, fourteen +levels, audio synthesis and this page. Roughly 9,000 lines of JavaScript, no engine and +no build step, on top of one vendored copy of three.js (MIT).

+

Every level was verified in a real browser as it was built, and several +genuine bugs are documented in the commit history rather than quietly fixed — the +renderer was 45% too dark for an hour because a raw shader does not get three.js's +colour-space conversion, and the Morris worm level could not reach its own milestones +until the spread rate was made to scale with the infection.

+ +

Source

+

The design document and the per-stage build notes ship alongside the game: +DESIGN.md · +STAGES.md · +README.md

+ +▶ play it + + + +
+ + diff --git a/index.html b/index.html index 0bf38fe..d49a1d7 100644 --- a/index.html +++ b/index.html @@ -94,6 +94,9 @@ #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} + #boot .cardlink{color:#3d5560;font-size:11px;letter-spacing:.12em;text-decoration:none; + border-bottom:1px solid #1a2a32;margin-top:4px} + #boot .cardlink:hover{color:#9fe6ff;border-color:#2a5566} .row .ms{color:#2a3f48;font-size:11px} .cargo{margin-top:14px;font-size:11px;color:#5b8494;letter-spacing:.08em;line-height:2} @@ -168,6 +171,7 @@
CLICK OR PRESS ANY KEY TO DIAL
+ what this is & what it doesn't do →
▮ NO CARRIER
diff --git a/src/levels/bluebox.js b/src/levels/bluebox.js index 998c2e4..1a0ca31 100644 --- a/src/levels/bluebox.js +++ b/src/levels/bluebox.js @@ -179,30 +179,32 @@ export default { const jh = new THREE.Mesh(new THREE.SphereGeometry(0.9, 16, 12), new THREE.MeshBasicMaterial({ color: 0xffb648, wireframe: true, transparent: true, opacity: 0.3 })); S.joy.add(jh); S.jhalo = jh; - S.joy.position.set(-4.4, -3.6, -6.4); + S.joy.position.set(-3.2, -3.2, -5.6); scene.add(S.joy); const jl = new THREE.PointLight(0xffc98a, 8, 14, 2); jl.position.copy(S.joy.position); scene.add(jl); // the card. worked out by hand, traded, and kept in a pocket. const cardTxt = 'ROUTING\n\n' + ROUTING.map(([n, c]) => n.padEnd(17, ' ') + c).join('\n'); - S.card = panel(cardTxt, { color: '#cfe4f6', size: 4.0, align: 'left' }); - S.card.position.set(-5.6, 0.4, -9.0); - S.card.rotation.y = 0.44; + S.card = panel(cardTxt, { color: '#cfe4f6', size: 3.4, align: 'left' }); + S.card.position.set(-3.0, 0.7, -7.6); + S.card.rotation.y = 0.34; scene.add(S.card); const cb = new THREE.Box3().setFromObject(S.card); const cardBack = new THREE.Mesh( new THREE.PlaneGeometry((cb.max.x - cb.min.x) * 1.12 + 0.4, (cb.max.y - cb.min.y) * 1.08 + 0.4), new THREE.MeshBasicMaterial({ color: 0x0a1626 })); - cardBack.position.set(-5.6, 0.4, -9.06); cardBack.rotation.y = 0.44; scene.add(cardBack); + cardBack.position.set(-3.0, 0.7, -7.68); cardBack.rotation.y = 0.34; scene.add(cardBack); const cardLight = new THREE.PointLight(0x9fc8ff, 9, 12, 2); - cardLight.position.set(-4.6, 1.0, -7.2); scene.add(cardLight); + cardLight.position.set(-2.4, 1.2, -6.2); scene.add(cardLight); S.cardSeen = false; S.readout = sprite('', { color: '#9fd8ff', size: 0.6 }); - S.readout.position.set(-3.6, 2.4, -7.5); scene.add(S.readout); + S.readout.position.set(2.0, 3.4, -7.6); scene.add(S.readout); camera.position.set(0, 0, 0); - camera.rotation.set(0, 0, 0); + camera.rotation.set(0, 0, 0, 'YXZ'); + camera.rotation.y = -0.20; // face the box, which sits to the right at x=1.9 + camera.rotation.x = -0.05; // the trunk hum you are standing in S.hum = A.pair(120, 180, { gain: 0.035 });