Skip to content

refactor(apps): native spinner sprites - #410

Open
lfkdsk wants to merge 1 commit into
pocket-stack:mainfrom
lfkdsk:refactor/native-spinner-sprites
Open

lfkdsk wants to merge 1 commit into
pocket-stack:mainfrom
lfkdsk:refactor/native-spinner-sprites

Conversation

@lfkdsk

@lfkdsk lfkdsk commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Six spinner sites drive their animation from the guest: createSpriteAnimation
advances a signal every frame and <Image src={spinnerSrc()}> swaps the texture
every third frame. The core already owns sprite-atlas playback, and the Octane
variants (apps/hero/app.octane.tsx, apps/gallery/app.octane.tsx,
apps/library/app.octane.tsx) have used it all along — the Solid and Vue Vapor
variants just never followed.

This converts apps/hero, apps/hero/app.vue-vapor.tsx, apps/hero-vue-vapor,
apps/gallery, apps/gallery/app.vue-vapor.tsx and apps/nsengine to
<Sprite sprite="spinner-atlas.svg">, and removes the frame lists and the guest
hook along with them. Keeping the JSX swap while leaving the frame constants in
place would have packed both the atlas and the eight standalone frames.

spinnerFrameStep is preserved, not dropped

apps/blackberry-classic-demo and apps/iphone4s-demo both pass a non-default
step of 6, so the prop had to survive the conversion. The core set_sprite
surface already takes a runtime step and uses it in the same deterministic cell
calculation, so Sprite.frameStep is wired through the native tree with
finite-number validation, floor and clamp to 1–65535, reactive rebinding, and
fallback to the manifest value.

Measured on main

Across the six targets, in-frame traffic drops from 750 calls / 8,136 B to
514 / 6,248 B, and the per-frame setImage traffic that drove the animation
(236 calls / 1,888 B) goes to zero. On 480-frame idle runs, 1,049 calls /
10,052 B becomes 453 / 5,284 B. Guest-side HostOps time across the six canonical
runs goes 2.168 ms → 1.314 ms.

Every affected PAK shrinks (five by 416 B, nsengine by 432 B) because the eight
standalone frames stop being packed; aggregate JS grows 424 B for the
frameStep plumbing.

Verification

1080/1080 canonical frames and 2880/2880 zero-input frames match clean main
builds, and both step-6 apps match 180/180 on the runtime-step path. The eight
atlas cells are pixel-identical to the standalone frames (0 differing pixels).
Mutating hero's manifest step from 3 to 4 diverges the canonical replay at frame
2, so the check has teeth.

The suite shows the same five pre-existing unit failures as clean main, and
the committed hero-main tape hashes diverge at the same frame-0 pair on both.

@lfkdsk
lfkdsk marked this pull request as ready for review September 14, 2026 21:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant