web: hide native number spinners; slower, fully staggered rounds

Per Justin: the native number-input spinners rendered as light boxes on
the dark theme, so they are gone app-wide (typing and arrow keys keep
working). Playback pacing: rounds now take 1000 ms at 1x, appearance
moments spread across 95% of the interval via a murmur3-style
full-avalanche hash, and each dot fades over 30% of the interval, so a
round reads as a continuous trickle instead of a flash.
This commit is contained in:
Justin Visser 2026-06-10 18:26:28 +02:00
parent d9b3812306
commit 3a4e21b701
4 changed files with 27 additions and 7 deletions

View file

@ -72,6 +72,19 @@ button {
font-family: inherit; font-family: inherit;
} }
/* The native number-input spinners render as light boxes that clash with
the dark theme and crowd the compact fields; arrow keys and typing keep
working without them. */
input[type='number'] {
appearance: textfield;
}
input[type='number']::-webkit-outer-spin-button,
input[type='number']::-webkit-inner-spin-button {
appearance: none;
margin: 0;
}
.visually-hidden { .visually-hidden {
position: absolute; position: absolute;
width: 1px; width: 1px;

View file

@ -75,7 +75,7 @@ function onNodeClick(nodeIndex: number, event: MouseEvent) {
// the panel stats), then fade in over the rest of the interval; playback // the panel stats), then fade in over the rest of the interval; playback
// never shows a finished still frame between rounds. // never shows a finished still frame between rounds.
const roundIntervalMs = computed(() => ROUND_MS / store.state.speed) const roundIntervalMs = computed(() => ROUND_MS / store.state.speed)
const popDurationMs = computed(() => roundIntervalMs.value * (1 - APPEAR_WINDOW) + 120) const popDurationMs = computed(() => roundIntervalMs.value * 0.3)
function popDelay(nodeIndex: number): string { function popDelay(nodeIndex: number): string {
return `${appearanceJitter(nodeIndex) * APPEAR_WINDOW * roundIntervalMs.value}ms` return `${appearanceJitter(nodeIndex) * APPEAR_WINDOW * roundIntervalMs.value}ms`

View file

@ -6,8 +6,8 @@ import { useSimStore, type SimStore } from './useSimStore'
// one-time autoplay). Playback never loops; at the final round the play // one-time autoplay). Playback never loops; at the final round the play
// button becomes a replay affordance. // button becomes a replay affordance.
export const ROUND_MS = 700 export const ROUND_MS = 1000
export const REDUCED_MOTION_ROUND_MS = 1000 export const REDUCED_MOTION_ROUND_MS = 1200
const SPEED_CYCLE: Record<number, 0.5 | 1 | 2> = { 0.5: 1, 1: 2, 2: 0.5 } const SPEED_CYCLE: Record<number, 0.5 | 1 | 2> = { 0.5: 1, 1: 2, 2: 0.5 }

View file

@ -1,5 +1,3 @@
import { mulberry32 } from './mulberry32'
// Cumulative reach at a playback round, shared by the panel stats and the // Cumulative reach at a playback round, shared by the panel stats and the
// reach chart so both always agree with the network picture. // reach chart so both always agree with the network picture.
export function reachedCountAtRound(reachedAtRound: number[], round: number): number { export function reachedCountAtRound(reachedAtRound: number[], round: number): number {
@ -12,10 +10,19 @@ export function reachedCountAtRound(reachedAtRound: number[], round: number): nu
// jitter drives the dot's animation-delay and the live counter, so the // jitter drives the dot's animation-delay and the live counter, so the
// percentage ticks up exactly as dots start appearing. // percentage ticks up exactly as dots start appearing.
export const APPEAR_WINDOW = 0.7 export const APPEAR_WINDOW = 0.95
// A full-avalanche integer hash (murmur3 finalizer): consecutive node
// indices land anywhere in [0, 1), so the appearance moments of a round's
// nodes are spread evenly instead of clustering into one flash.
export function appearanceJitter(nodeIndex: number): number { export function appearanceJitter(nodeIndex: number): number {
return mulberry32(nodeIndex * 7919 + 17)() let mixed = Math.imul(nodeIndex + 1, 0x9e3779b1)
mixed ^= mixed >>> 16
mixed = Math.imul(mixed, 0x85ebca6b)
mixed ^= mixed >>> 13
mixed = Math.imul(mixed, 0xc2b2ae35)
mixed ^= mixed >>> 16
return (mixed >>> 0) / 4294967296
} }
// Reach as displayed mid-transition: every earlier round fully, plus the // Reach as displayed mid-transition: every earlier round fully, plus the