Back to Limb Race
SOURCE / PINNED RELEASE

Made of little things.

Limb Race

Release
6a8ae4fc626e…
Author-recorded commit
008c01ee6454…
License
LICENSE
Author’s source reference
nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-c2869f12ad8

Archive hash verified: 7be8492d1f89b891…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

docs/game-usage.md
# Limb Race

Draw limbs around the red hub on the white square pad, then choose **Let it roll**. Every stroke connects to the hub. A motor spins the drawing; ground contact, gravity, friction and the shape determine how it moves. Undo removes the last drawing edit. Clear starts fresh. Runner and Star are accessible alternatives to drawing with a pointer.

Choose from ten courses using the flag control. The original four are followed by six harder courses, with a numbered, scrollable list and terrain previews. **Classic** locks your shape for the run. **Live Draw** lets you clear, redraw or swap shapes while moving. Use the **Rigid / Bendy** control before starting. Bendy strokes flex under contact and spring back; their bent geometry also collides with the track. Material locks for the run, including Live Draw. Personal rankings separate every course, editing mode and material. The original four courses have shared boards with drawings; the six new challenge courses currently keep personal records only. Races stop after three minutes; pause, hidden tabs and lost focus stop the simulation clock. Restart keeps the current drawing.

Finished runs appear in **Your runs**, fastest first. Tap a run to inspect its limbs and reuse them. Live Draw records its initial shape and up to 23 recent completed-stroke snapshots; the slider is a design history, not a physics replay. The local history is bounded to 40 races and the host storage budget, so oldest races may be removed. The optional host storage is scoped to this creation/build/viewer; it is not a cloud backup.

**Share time** submits a finished run on an original course to its shared board. Challenge courses save the time and limbs in **Your runs** and explain this when you open Shared. Shared entries identify the host-authenticated session actor, with an editable display name. A connected profile can supply the name; the label is not a verified profile claim. These are casual client-reported scores. The service keeps each session actor's best score for each course/mode/material.

Shared entries now include the finish drawing. Tap a racer to inspect and reuse their limbs. Live Draw shares the final shape; its full bounded edit history stays in personal records. Times and drawings update together only for a better personal best. No fake racers or demonstration times are inserted. Preview uses isolated local backend data. Public boards are registered by the host’s authorized publication workflow.

The score provider is pinned in `napplet.json` and the generated public context copied to `soy-backend.json` (see below). Sixteen versioned boards use lowest milliseconds. Change board IDs if physics or course rules change after release. The app has no hard NAP requirements: without CVM, local racing works; without storage, progress lasts for the session. Optional failures are surfaced inline.

Step It Up has real 95-unit vertical risers; High Society has 105-unit ledges and sheer drops. Ordinary wheels cannot clear these courses. Reach over the lip with long legs; the square drawing area allows longer diagonals. Meadow Run and Dune Dash remain rolling courses. There is no horizontal drive force: a clockwise rotational motor, gravity and contact move the racer. The motor acceleration is now capped at 120 rad/s², down from 480.

Physics-v2 boards and local history are separate from the initial rules. When a v2 store is absent, the old drawing and name are carried over; old times remain in the untouched v1 storage key and do not enter the new rankings. Personal history stays local.

## Development

The workspace is already installed; do not re-scaffold. Start `soyli dev --port 0 --no-open` and open its printed URL. Stop that process after previewing.

- `pnpm verify`: guidance, physics/data regression tests, TypeScript and single-file build.
- `pnpm test:sharing`: Bun-based real-host test with two temporary identities and an isolated local storage relay; blocks public network traffic, restores config and stops its own preview. Run without another preview watching this project.
- `pnpm test:conformance`: upstream reference-shell checks.
- `soyli check`: actual host artifact check.
- `node tests/host-smoke.mjs <preview-url>`: hosted gameplay, local shared scores and layout/theme fixtures.
- `node tests/improvements-smoke.mjs <preview-url>`: square corner drawing, Bendy ledge completion and separate material rankings.
- `node tests/challenges-smoke.mjs <preview-url>`: challenge completion, personal records, persistence and the ten-course picker in both themes.
- `pnpm test:optional`: full race without injected host capabilities.
- `node tests/mobile-smoke.mjs <preview-url>`: emulated touch drawing, Live Draw, pause and track switching.

The browser tests use Chromium from the installed conformance toolchain. They size the actual iframe explicitly, overriding the preview manager's 300px minimum and obscuring host chrome for captures. Theme fixtures send the standard `theme.changed` event from the parent test page because the local manager advertises a fixed light palette. These fixtures do not claim the manager has a dark-theme toggle.

### Public backend configuration

`soy-backend.json` is a byte-for-byte public copy of the CLI-generated backend context. The game imports this root-level copy because soyLI 0.19.0 rejects every `.napplet-space/` path in its shareable source scan, including the generated public context. Private runtime state stays ignored. After `soyli backend init`, a provider change, or adopting this source under another creator, refresh the copy before building:

```sh
cp .napplet-space/soy-backend.json soy-backend.json
pnpm build
```

Do not handwrite the namespace. Commit the refreshed public copy with the corresponding board configuration.

## Challenge courses

| Course | Difficulty | What changes |
| --- | --- | --- |
| 05 · Sawtooth | Expert | Six uneven teeth with different heights and sloping descents. |
| 06 · Pinpoint | Expert | Five narrow pillars, with 60–100-unit landing surfaces. |
| 07 · Trench Trouble | Expert | Four deep trenches with 125-unit exits and widening gaps. |
| 08 · Skyline | Extreme | 125–130-unit walls followed by sheer drops. |
| 09 · Ridge Runner | Extreme | Sloped approaches, short resting ledges and 125–130-unit climbs. |
| 10 · The Gauntlet | Extreme | A 6,000-unit combination of pillars, trenches, ramps and high walls. |

All six are verified finishable with a single valid drawing in both Rigid and Bendy. The basic Runner gets stuck on each. Difficulty depends on the drawing: a successful pair of limbs can still perform very differently from course to course. Live Draw remains available everywhere. The camera follows vertical movement so high climbs and trench landings stay visible.

The existing physics and original courses are unchanged, so physics-v2 times remain comparable. The score service permits 16 configured boards, already occupied by the original four courses × two modes × two materials. New courses therefore retain personal times and drawings without trying to submit to a nonexistent shared board.


## Build and share levels

Open the course picker and choose **Create a level**. Add flat ground, slopes, steps, drops or trenches. Select a piece in the preview or numbered strip, edit its width/height, then Apply. Reorder or remove pieces to shape the course. Levels allow 1–24 pieces; the editor keeps terrain within the playable vertical range. **Test track** races the current draft. A draft can be difficult or impossible; publishing does not certify that it is finishable.

**Your levels** holds up to 20 local drafts. **Community** browses recent public levels. Play one, or Remix it into a new draft under your own identity. Publish requires connecting your Nostr identity in the host and approving its publication prompt. Owners can reopen **Edit published**, publish updates, or unpublish. Unpublishing hides the current listing with a public tombstone; old copies may remain elsewhere. Failed or denied publication keeps your draft and offers retry. If a level changed elsewhere, reopen its latest version or make a copy.

Custom-level race times stay personal: the service cannot dynamically create a board for every level, and the existing 16-board limit remains. Every personal custom run stores the actual terrain and revision, so changed courses never share rankings. Local drafts/history are browser/host-scoped, not cloud backups. Public levels use the stable creation scope, so published records survive compatible new builds; preview and release libraries are separate.

Shared drawings require `soy.boards.v2`; public levels require the host’s `soy.app-data/1` policy. Missing enhancements produce an explanation while local racing and editing still work. New drawing boards use the `-draw1` suffix because their attachment schema is immutable. Old board times are not migrated into these boards.


The level library explicitly selects the first runtime relay declared in `napplet.json` (currently `wss://relay.napplet.soy`). The host must also permit that destination; changing a hint cannot grant write access. This separates level storage from the temporary score transport that soyLI 0.20.0 puts first in local preview. Without an explicit destination, that transport times out on NIP-78 publication. Tests temporarily select a loopback storage relay and restore the original config afterward. Public service persistence and deployed-host support still require release verification.