Smooth aurora + hero text state transitions#35
Merged
Conversation
Two things made state changes feel abrupt: 1. Colors snapped. The blobs consume var(--aurora-a/b) inside a radial-gradient, and "transition: background" cannot interpolate gradient images, so the declared .6s transition never actually ran. The vars are now registered with @Property (syntax '<color>') and transitioned on the readout itself, so a state flip reads as one ~1.1s color sweep. Browsers without @Property fall back to the old instant swap. 2. The blobs teleported around buffering. The arming state overrode animation-duration (14s -> 4s), which remaps the animation's elapsed time to a different progress fraction - a visible jump on every enter/exit. The drift is now identical in every state; arming's energy comes from a new dedicated .aurora-pulse layer, a bottom glow that fades in via an opacity transition and breathes via a transform-only animation, so its keyframe restart never fights the fade. Verified the @Property blocks, pulse rules, and var transition survive build.rs minification in the embedded output. 235 tests green.
Bundles the text polish into the aurora work so the hero changes state as one sweep instead of three unsynchronized snaps: - The big number's color transition now matches the aurora's 1.1s sweep (it used to finish in .25s, visibly ahead of the glow), and the state pill's text/background/border sweep over .8s instead of hard-cutting. The consuming properties interpolate when --sc changes, so no extra @Property registration is needed there. - setSubText now takes a message-kind key and only animates when the KIND changes. The buffering, bitrate, and auto-cut countdown lines embed numbers that tick up to 4x/s (SSE pushes on every state change, checked at 250ms), and every tick replayed the fade - a constant pulse. Same kind now updates text in place; a kind change gets a directional swap (old line drops out, new one drops in via a transition-suppressed pre-frame). - The arming counter counts continuously: each ~250ms update animates linearly into the next instead of the old 80ms ease-out that sprinted and then froze ~170ms between updates. Phase jumps (arm/activate/cut) get a longer 700ms eased sweep. The fill bar switches to linear width for the same reason. animateNumber also gained a generation guard so mid-flight retargets stop the previous rAF loop instead of stacking duplicate loops on the element. 235 tests green; verified the new code survives build.rs minification.
Two fixes from live use: 1. "Adjusting -> rewinding to 15s (currently 0s)..." described motion the engine doesn't do. The delay never rewinds gradually - the cut machinery holds position until the ring has enough history, then makes ONE IDR-aligned jump (compute_delay_cut's build-buffer-first branch). The copy now tracks the server's buffer_building flag: "Building history - jumps to 15s once Xs buffered reaches it" while waiting, and "Jumping back/forward to 15s of delay..." in the brief pre-jump window. The adjusting detector is also gated on egress_alive: current_delay_ms derives from the slowest LIVE destination and reads 0 with none connected, which made the old line show a fake permanent "rewinding (currently 0s)" state. Fixed the same "smoothly rewind" wording in arm_delay's doc comment. 2. Disarm / cancel now rolls the big number down to 0.0 over 1.1s, the same duration as the aurora + number color sweep, so the count-down and the fade-to-idle land as one motion instead of the number vanishing ahead of the glow.
Four issues from live use: 1. Disarm 15s -> 0s didn't animate. animateNumber is re-called with the same target on every state tick (~4x/s), and the no-change branch hard-set textContent to the end value, killing any in-flight roll. The idle state repeats after disarm, so ~250ms into the 1.1s count-down it snapped to 0. Now the no-change branch only writes text when the animation has actually settled (cur.last === target), letting an in-flight roll finish. 2. Arming count-up stuttered. It animated over 300ms but the poll fallback updates every 500ms, so it finished early and froze. Bumped the arming duration to 600ms (linear + the generation guard) so it's always still moving when the next value lands, whether the driver is SSE (~4x/s) or the 500ms poll. 3. Profile chips (hero) and the Profiles pane flickered - both wiped and rebuilt their DOM every state tick. Added a content signature guard so they only rebuild when the rendered result actually changes. 4. The "delay too big for your buffer" gate flickered and could momentarily unlock an unfillable delay (e.g. 5min at ~7Mbps): it planned from the raw instantaneous bitrate, so a dip inflated the computed capacity and made the chip clickable for a frame. Planning now uses a slow-decaying peak-hold (worst-case bitrate), so the gate is stable and can't transiently unlock a delay that can't fill. 235 tests green; verified all four in the embedded minified output.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The hero now changes state as one coordinated sweep - aurora glow, big number, state pill, and subtext move together - and the per-second stutter in the text and the arming counter is gone.
Aurora: two bugs behind the jank
Colors never actually transitioned. The blobs consume
var(--aurora-a/b)inside aradial-gradient, andtransition: backgroundcannot interpolate gradient images, so the declared.6stransition silently never ran - every state change was a hard color cut. Fix: the colors are registered custom properties (@propertywithsyntax: '<color>') and the variables themselves transition over ~1.1 s. Browsers without@propertykeep the old instant swap.The blobs teleported around the arming state. Arming overrode
animation-duration(14 s → 4 s), and changing duration mid-flight remaps elapsed time to a different progress fraction - a visible jump on every enter/exit of buffering. The drift animation is now identical in every state; arming's energy comes from a dedicated.aurora-pulselayer (bottom glow, 0.9 s opacity fade-in, transform-only breathing so its restart never fights the fade).Text: synchronized sweeps, no more per-second pulsing
animateNumberalso gained a generation guard so mid-flight retargets stop the previous rAF loop instead of stacking duplicate loops on the element.Verification
index.html.gzand confirmed the@propertyblocks, pulse rules, variable transition, and the new text-swap/counter code all survive build.rs minification.cargo fmt --checkclean,cargo test --release: 235 passed.