Public: anyone can read this; the crew can change it.

The frame the round publishes: `src/frame.ts`, a world in and 4 KB out #71

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

Pushed. Here's the pull description:

shipped production is serving it deploy feb7d92

opened this

Pushed. Here's the pull description:

---

src/frame.ts is one pure function — a world plus the round's clock and id in, a Frame out — and the only place the sea's numbers become the wire's. Every stride, scale and cap comes from src/protocol.ts, every sea number from src/world.ts, the roster cap from src/concept.ts; there is no second table of scales here, and no import of a socket, a bus or a database.

The three translations it owns, each argued in the module header and tested:

  • The units are the tick's. I measured a 24-mote braid: motes reach 190 units/s, past the ±128 an int16 at VELOCITY_SCALE holds per second, so a velocity that meant units/s would clamp in ordinary play. Velocities and currents go out multiplied by DT (the protocol's own "1/256 of a unit per tick"), which makes the wire's FIELD_MAX of 8 mean 160 units/s — the top of the range play lives in. The alternative, mapping the sea's own 600-unit clamp onto 8, is a cap a lane never approaches and would squash every readable current into the bottom quarter of the scale, below the "a twentieth of full scale draws nothing" line the look section sets.
  • A thruster rides as intent — the world's acceleration over THRUST_MAX — because THRUST_MAX is 12.5 units/s² and would saturate the ±4 that THRUST_SCALE leaves; a share of full thrust is also the number a door can draw.
  • The field is averaged, never sampled. Each wire cell is the mean of its four sea cells. A one-cell lane drifting across a boundary hands its strength from one wire cell to the next — the test sweeps the lane across all 32 sea columns and the total on the wire is identical every time, which sampling would fail on every other column — and invariant 5's decay stays visible as a smooth fade (a test asserts the wire's total field magnitude strictly decreases, tick after tick, on a sea left alone).

Clamping: every linear quantity saturates at the range its scale leaves in an int16, by vector magnitude, so a clamped velocity or current still points where it pointed instead of being squared off component by component; angles wrap, because an angle is a circle. It is clamped in the builder and not only in the encoder so a door holding the object in-process sees exactly what a door holding the bytes sees. Worst rounding error after the wire: 0.125 sea units for a position (inside the quarter-unit promise interpolation rests on), 0.039 units/s for a velocity, ~0.63 units/s for a current, π/65536 rad for the gate. MOTE_STUNNED is the only flag set — the beached bit is #67's and is named in a comment rather than invented here.

Measured, on this branch: a full frame — 24 motes, a field driven to the sea's own cap — is 924 bytes of the 4096 allowed, and builds in 44 µs, about a thousandth of the 50 ms tick. In that extreme braid 21 of 256 wire cells saturate at FIELD_MAX and the mean cell sits at 1.82 of 8; that is the sea's hottest hour, and saturating there is what the wire is designed to do.

Names are not on the binary wire, so they ride the built frame as names, in roster order (the world's own mote order — addMote appends, removeMote splices), truncated at NAME_MAX with control characters removed; escaping stays each door's job. A door can diff two frames by index and never match by name.

Not in here: who is in the roster, admission, spectators, when a frame is sent, and what clock counts — #8, #53 and #58 own those; the frame carries the number it is handed. Outside src/frame.ts and its test I touched only a README row and a paragraph in src/protocol.ts's header pointing at where the units are written down, so the next decoder (#60) does not have to guess them.

To look: src/frame.test.ts is where the argument is — the averaging tests, the clamp-not-wrap tests, and the byte and microsecond counts, which print their real numbers in the assertion message. bun run test is green (132 tests).

🤖 Generated with Claude Code

Closes #66

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs