SOURCE / PINNED RELEASE
Made of little things.
Safe Sand · Material studies
- Release
- 9101a9cd9fb4…
- Author-recorded commit
- 75d98e1b8edb…
- License
- LICENSE
- Author’s source reference
- nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-7682e55a791
Archive hash verified: 6c8ddabeb31100e1…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
# Verification — 2026-11-12
Final HTML SHA-256:
`f111dcd2239ca35ead3b56bc6013279f7108c34d141c196e72ac13f8199b3c25`
Self-contained HTML: 96,468 bytes. All simulation shaders, rendering, controls
and local audio synthesis are bundled. No external runtime assets or app-owned
network/storage/signing surfaces were found in the boundary scan. No required
NAP domains and no relay escape hatches. Optional storage and media use SDK helpers; optional identity retires the audio
session when account scope changes.
## Automated checks
| Check | Result |
| --- | --- |
| pnpm type-check | Passed |
| pnpm build | Passed, single HTML |
| pnpm test:guidance | 6 passed |
| pnpm test:unit | 3 passed: settings, material bounds, camera projection/inverse |
| pnpm test:simulation | 43 passed in Chromium |
| pnpm verify | Passed, includes all of the above |
| pnpm test:conformance | 5 passed, 0 failed, 5 skipped |
| soyli check --json | Startup passed; 13 scripted interaction actions exercised |
Conformance skips: three manifest checks (no signed manifest supplied), wire
(no envelopes emitted during boot), lifecycle (not measured by the harness).
A green boot result alone is not the GPU proof: the separate simulation suite
asserts finite live particle states for all eleven materials, low-friction glass
movement under tilt, iron attraction toward a pointer, pause, preservation through
resize, clear/refill, camera bounds and orbit while paused, continuous/tap/keyboard
pouring, erasing, stroke undo, repulsion, slow motion, single-step, optional storage,
and absent/denied media. Audio tests cover host registration, volume, pause/play/stop
and ensure simulation activity never overrides a host pause. No uncaught app errors.
soyLI 0.34.0 on macOS arm64; host check browser 153.0.8010.12. Manual browser
automation used cached Chromium 157.0.8103.15. The original browser connector
could not find Chrome, so the installed Playwright CLI used that cached browser.
Loopback servers and browser processes required the execution environment's
sandbox escalation; this did not change the napplet's allow-scripts sandbox.
## Real hosted UI and interactions
Tested using `soyli dev` at its printed loopback URL, with the real injected
storage domain. Save setup and Restore setup round-tripped Beach, Pour and the
28°/−18° camera. Same-build persistence was checked in the first port verification.
Sound registered with the real host and enabled/disabled from a user gesture.
Audible quality and cross-napplet audio coordination remain untested on hardware.
Real iframe sizes 200×160, 280×240, 320×560, 900×600 and 1440×900 were tested with both
light and dark host themes. App-owned canvas and interface colors remained stable.
The 200×160 frame showed the minimum-size notice; other layouts had no clipped
main controls. Paused resize retained all 144,179 live beach grains and their
relative positions. The theme-free/domain-free fixture also worked.
Touch contexts: 390×844, 360×640 and 844×390, with a coarse pointer and actual
emulated touch input. Exercised material selection, scrollable drawer, tilt pad,
drawing, angled view, tool selection, continuous pouring, pause/resume and touch cancellation. Main controls were at least 44×44.
Frame dimensions were asserted; test-only host iframe style overrides removed
its minimum height and, for mobile, positioned it over the workshop chrome.
Inspected desktop, narrow, tiny, material-picker and phone screenshots. Final
cover (preview-v2.png) shows a raked Beach tray at an angle. The selected silent
preview-v2.webm records a refill, camera change, pouring and directional tilt;
inspected a 2 fps contact sheet spanning its 7.44 seconds and played the entire
clip in Listing. The selected image loaded at its full resolution. Its sidecar hash matches the final HTML. Portrait video is not
available in the current soyLI recorder.
## Remaining coverage and delivery
- Physical iOS/Android performance, sensor input and audible sound quality are
unverified. Motion sensors are deliberately replaced with on-screen controls.
- Independent-shell/Paja compatibility is unverified; the napplet launcher is not
installed. The reference conformance harness is not a second product shell.
- No controller support is claimed. Keyboard and touch are implemented.
- Public release is explicitly authorized after these checks. No backend is used.
- Upstream redistribution permission is unresolved; see THIRD_PARTY.md.
- Preview sessions and test browsers are stopped after review.
## Modern Web Guidance applied
- `form-factor-adaptation`: I used a definite-size tray container for width and
height queries and preserved 44px controls on touch. The guide says, “DO NOT
query `height` or `block-size` on an `inline-size` container”; `container: tray /
size` avoids that trap. Safe-area padding keeps edge controls reachable.
- `efficient-background-processing`: I attach the skip/resume listener directly
to the scene because “the `contentvisibilityautostatechange` event does not
bubble in some browser implementations.” Visibility handling and an
IntersectionObserver fallback suspend the GPU loop when appropriate.
