Widen the ending glitch window; decouple jitter duration from it

User reported the progress bar consistently stalling a few segments
short of full -- SUB/WAVE's reported track duration appears to run a bit
longer than the station actually plays before crossfading/cutting to the
next track, so "remaining" rarely if ever dropped below the old 5s
end-window before the subsonic_id changed out from under it. Split the
single STATIC_OVERLAY_SECONDS into separate start (5s, unchanged) and end
(22s) windows to compensate.

Widening the end window naively (scaling GLITCH_REPEATS to cover all 22s
of it) pushed a real render to 481KB, over the 460KB device limit --
each repeat adds several more uniquely-tinted/shifted frames for WebP to
encode, and 22s of continuous jitter is a lot of those. Added
GLITCH_ACTIVE_SECONDS (6s) to cap how much of the window actually gets
jitter bursts, independent of how long show_static itself stays true --
scanlines and corner ticks are cheap, constant per-pixel layers that
still cover the full window on their own.

Verified: payload stays well under budget (max 234KB) across every zoom
level/position combination after the fix.
This commit is contained in:
poprhythm committed 2026-10-08 18:51:35 -04:00
1 parent 5f9bc11e47
commit c82503c73f
1 file changed
+32 -12
+32 -12
View File
@@ -91,11 +91,21 @@ COLOR_SCHEMES = {
} }
DEFAULT_COLOR_SCHEME = "cyberpunk" # matches the station's own site theme DEFAULT_COLOR_SCHEME = "cyberpunk" # matches the station's own site theme
# How many seconds into a track's start, or before its end, the "static" # How many seconds into a track's start, or before its reported end, the
# overlay shows over the album art (a bundled animated GIF of sparse noise # glitch overlay shows over the album art. Only used when the "Static
# specks over a transparent background -- images/static.gif). Only used # overlay" schema field is enabled.
# when the "Static overlay" schema field is enabled. #
STATIC_OVERLAY_SECONDS = 5 # These aren't symmetric on purpose: SUB/WAVE's reported `duration` is a
# track's full metadata length, but in practice the station appears to
# cut/crossfade to the next track a bit before that duration actually
# elapses (observed via the progress bar consistently stalling a few
# segments short of full, never reaching the end) -- so `remaining` was
# rarely if ever dropping below a short end-window before the subsonic_id
# changed out from under it. Widening the end window compensates for that
# observed early cutoff; the start window doesn't have the same problem
# (elapsed genuinely starts at 0) so it stays short.
STATIC_OVERLAY_START_SECONDS = 5
STATIC_OVERLAY_END_SECONDS = 22
DEFAULT_STATIC_OVERLAY = "on" DEFAULT_STATIC_OVERLAY = "on"
# How far in the album art zoom goes, relative to its fully-zoomed-out rest # How far in the album art zoom goes, relative to its fully-zoomed-out rest
@@ -180,7 +190,7 @@ def main(config):
show_static = ( show_static = (
static_overlay_enabled and static_overlay_enabled and
progress != None and progress != None and
(progress["elapsed"] < STATIC_OVERLAY_SECONDS or progress["remaining"] < STATIC_OVERLAY_SECONDS) (progress["elapsed"] < STATIC_OVERLAY_START_SECONDS or progress["remaining"] < STATIC_OVERLAY_END_SECONDS)
) )
# The show/host and time/temp rows always render, even when empty, so # The show/host and time/temp rows always render, even when empty, so
@@ -384,14 +394,24 @@ GLITCH_STATE_POOL = [
] ]
# render.Sequence doesn't loop on its own (see _glitch_scanlines below), so # render.Sequence doesn't loop on its own (see _glitch_scanlines below), so
# to repeat the burst-then-pause pattern for the whole show_static window, # to repeat the burst-then-pause pattern, the repeats have to be spelled
# the repeats have to be spelled out as one long children list up front # out as one long children list up front rather than relying on any
# rather than relying on any runtime looping. Rounds up so the window is # runtime looping.
# always fully covered, even if that runs a little past #
# STATIC_OVERLAY_SECONDS -- better than the jitter cutting out early. # Deliberately *not* sized against the full show_static window (which can
# now be up to STATIC_OVERLAY_END_SECONDS = 22s): each repeat adds several
# more uniquely-tinted/shifted frames for WebP to encode, and scaling that
# up to cover 22s of jitter pushed a real render past the 460KB device
# limit (measured 481KB). Scanlines and corner ticks (see _glitch_effect)
# are cheap, constant per-pixel layers that can cover the full window for
# free, but the hue-shifted bands don't need to keep jittering the whole
# time to read as "something's happening" -- GLITCH_ACTIVE_SECONDS caps
# how much of the window actually gets jitter, independent of how long
# the window itself is.
GLITCH_ACTIVE_SECONDS = 6
_GLITCH_CYCLE_TICKS = GLITCH_STATES_PER_CYCLE * GLITCH_HOLD_TICKS + GLITCH_PAUSE_TICKS _GLITCH_CYCLE_TICKS = GLITCH_STATES_PER_CYCLE * GLITCH_HOLD_TICKS + GLITCH_PAUSE_TICKS
_GLITCH_TICKS_PER_SECOND = 20 # 1000ms / 50ms-per-tick _GLITCH_TICKS_PER_SECOND = 20 # 1000ms / 50ms-per-tick
GLITCH_REPEATS = int(STATIC_OVERLAY_SECONDS * _GLITCH_TICKS_PER_SECOND / _GLITCH_CYCLE_TICKS) + 1 GLITCH_REPEATS = int(GLITCH_ACTIVE_SECONDS * _GLITCH_TICKS_PER_SECOND / _GLITCH_CYCLE_TICKS) + 1
def _glitch_states_for_cycle(cycle_index): def _glitch_states_for_cycle(cycle_index):
# Rotate through GLITCH_STATE_POOL instead of slicing (which doesn't # Rotate through GLITCH_STATE_POOL instead of slicing (which doesn't
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.