Skip to content

feat: v0.3.7 — idle drift cruise: wire driftAngle/driftSpeed into the physics loop - #60

Merged
Jess Sullivan (Jesssullivan) merged 1 commit into
mainfrom
feat/idle-drift-cruise
Sep 1, 2026
Merged

feat: v0.3.7 — idle drift cruise: wire driftAngle/driftSpeed into the physics loop#60
Jess Sullivan (Jesssullivan) merged 1 commit into
mainfrom
feat/idle-drift-cruise

Conversation

@Jesssullivan

Copy link
Copy Markdown
Contributor

Root cause

Through 0.3.6, driftAngle/driftSpeed were initialized per blob (since 0.3.0) but never read by the physics loop. With no pointer, scroll, or devicemotion input — i.e. every idle desktop — the only inputs were zero-mean jitter and a slow bounded slosh that the *= 0.992 per-substep damping decorrelates in ~1.4 s, so the background read as frozen-with-a-shiver. The evidently-intended cruise field was dead code.

Fix

  • Wire the cruise into the screensaver physics step: a constant per-substep force along each blob's persistent driftAngle heading, with magnitude driftSpeed * (1 - VELOCITY_DAMPING) so driftSpeed is exactly the terminal cruise speed under the unchanged damping. Purely additive, consumes no randomness: gravity/pointer/scroll feel and transient-impulse decay are untouched.
  • Raise driftSpeed init 0.01 + rand * 0.0150.05 + rand * 0.05 (the dead-code-era values were weaker than the ambient slosh): terminal cruise ≈ 3–6 units/s in the 180-unit field, a field crossing in ~30–60 s. Blobs reach walls and bounce; recordBounce() already re-randomizes the heading on impact.
  • Extract the damping literal to VELOCITY_DAMPING (still 0.992); no behavior change.
  • Version 0.3.7 across package.json/MODULE.bazel/BUILD.bazel, CHANGELOG entry, feel-contract status update.

Reduced motion

Unchanged: the cruise lives inside the physics step, which TinyVectors never runs while (prefers-reduced-motion: reduce) matches (respectReducedMotion defaults true and renders the existing static frame).

Verification

tests/unit/idle-drift-cruise.test.ts (deterministic, constant-0.5 mocked RNG):

  • a differential run pair (driftSpeed 0.1 vs 0) isolates the cruise displacement exactly — brownian/jitter terms cancel byte-for-byte;
  • a wall test proves a cruising blob bounces and stays inside the field;
  • an init-floor test pins the new driftSpeed range.

Verification is remote-only per operator ruling: CI (Verify → js-bazel-package reusable workflow) runs typecheck, unit tests, build, package checks, bundle size, and the Bazel targets on repo-owned runners.

… physics loop

Through 0.3.6 driftAngle/driftSpeed were initialized per blob but never
read: with no pointer, scroll, or devicemotion input (every idle desktop)
the only motion was zero-mean jitter plus a slow bounded slosh, so the
background read as frozen. The loop now applies a constant per-substep
force along each blob's persistent heading with driftSpeed as the terminal
speed under the unchanged *=0.992 damping (extracted to VELOCITY_DAMPING):
blobs drift gently by default, reach walls, and bounce — recordBounce()
already re-randomizes the heading on impact. The force is purely additive
and consumes no randomness, so gravity/pointer/scroll feel is untouched,
and reduced-motion behavior is unchanged (the cruise lives inside the step
the component never runs under prefers-reduced-motion).

driftSpeed init raised 0.01+rand*0.015 -> 0.05+rand*0.05 (terminal ~3-6
units/s in the 180-unit field; field crossing in ~30-60s). Deterministic
unit coverage in tests/unit/idle-drift-cruise.test.ts: a differential run
pair isolates the cruise term exactly, plus wall-bounce containment and
the init floor.
@Jesssullivan
Jess Sullivan (Jesssullivan) merged commit 9b64b67 into main Sep 1, 2026
7 checks passed
Jess Sullivan (Jesssullivan) added a commit to Great-Falls-Tool-Bus/greatfallstoolbus.org that referenced this pull request Sep 1, 2026
… desktop (#228)

* feat(brand): tinyvectors 0.3.7 — blobs drift and bounce by default on desktop

Root cause: through 0.3.6 the package initialized each blob's
driftAngle/driftSpeed cruise field but no code ever read it, so with no
pointer, scroll, or devicemotion input (i.e. every idle desktop) the only
motion was zero-mean jitter plus a slow bounded slosh the engine's damping
decorrelates in ~1.4s — the background read as frozen. Fixed upstream in
tinyvectors v0.3.7 (tinyland-inc/tinyvectors#60, signed tag on 9b64b67):
the physics loop now applies a constant per-substep force along each
blob's persistent heading with driftSpeed as the terminal speed (~3-6
units/s in the 180-unit field), so blobs drift, reach the walls, and
bounce; recordBounce() re-randomizes the heading on impact. The force is
purely additive: the devicemotion enhancement (#224, iOS pill included)
and the prefers-reduced-motion full freeze are unchanged.

Consumes the release across every seam this repo pins, per the module's
release ritual (tinyvectors docs/release-flow.md) and this repo's
.bazelrc re-pin discipline, in one commit:

- package.json tag-tarball pin -> v0.3.7, with the archive's sha256 SRI
  added to PINNED_INTEGRITY in scripts/build-tinyvectors.mjs (same value
  the registry source.json carries) and pnpm-lock.yaml following the pin
- MODULE.bazel bazel_dep 0.3.6 -> 0.3.7
- .bazelrc registry pin f23d6975 -> ef734c4a
  (tinyland-inc/bazel-registry#115; the registry's .bazelversion at that
  commit re-read and still records estate value 8.2.1), with
  MODULE.bazel.lock refreshed in the same commit: every registry URL key
  re-keyed to the new SHA, tinyvectors 0.3.6 file hashes replaced by
  0.3.7's (values verified against the served registry bytes)
- src/lib/brand-vectors-motion-contract.test.ts pins the 0.3.7 floor,
  the PINNED_INTEGRITY entry for the pinned tag, and the layout
  call-site motion contract (animated + respectReducedMotion defaults
  kept, enableDeviceMotion stays the enhancement layer)

Verification is remote-only per operator ruling 2026-09-01: upstream
Verify + tag lanes green (typecheck, unit incl. the deterministic idle
drift differential test, build, package, bundle-size, Bazel targets);
registry validate + immutability gates green; this repo's CI is the
evidence for the consumer bump.

* fix(test): widen the version-floor reduce accumulator to number

svelte-check narrows the accumulator of IDLE_DRIFT_FLOOR.reduce to the
as-const literal union (0 | 3 | 7), so s * 1000 + n fails to type. Pass
the number type argument explicitly; behavior unchanged.
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