Loop the RGB-split glitch burst with pauses, instead of playing once

The jitter previously played its 4 states once (~1s total) then went
quiet for the rest of the 5s static window, since render.Sequence doesn't
loop. Now repeats the burst-then-pause pattern enough times to cover the
whole window: GLITCH_REPEATS is computed from STATIC_OVERLAY_SECONDS so
it still fully covers the window if either changes, rather than a
hardcoded repeat count silently falling short.

Verified via an isolated single-cycle test (burst of 4 states + 1 pause,
outside the main zoom animation) that the pause frame is genuinely clean
(no tint bands) -- pixlet collapses each multi-tick hold into a single
deduped output frame, which made an earlier attempt to verify this by
guessing raw frame indices unreliable; confirmed instead by exhaustively
inspecting all 5 of the isolated test's (correctly deduped) output
frames.
This commit is contained in:
poprhythm committed 2026-10-08 16:53:30 -04:00
1 parent 6cd5decdf5
commit 78621b3831
1 file changed
+35 -17
+35 -17
View File
@@ -356,6 +356,7 @@ def _zoomed_art(art_bytes, zoom_scale, zoom_position, subsonic_id):
# keyframe that was visible: roughly the top third and an overlapping # keyframe that was visible: roughly the top third and an overlapping
# middle band, each about 30% of the frame's height. # middle band, each about 30% of the frame's height.
GLITCH_HOLD_TICKS = 5 # ~250ms per jitter state at the default 50ms/tick GLITCH_HOLD_TICKS = 5 # ~250ms per jitter state at the default 50ms/tick
GLITCH_PAUSE_TICKS = 15 # ~750ms of clean art between bursts
GLITCH_STATES = [ GLITCH_STATES = [
# (top1, height1, shift1, hue1, top2, height2, shift2, hue2) # (top1, height1, shift1, hue1, top2, height2, shift2, hue2)
(3, 9, -2, 150.0, 19, 8, 2, -60.0), (3, 9, -2, 150.0, 19, 8, 2, -60.0),
@@ -364,6 +365,16 @@ GLITCH_STATES = [
(1, 10, 1, 150.0, 21, 7, -2, -60.0), (1, 10, 1, 150.0, 21, 7, -2, -60.0),
] ]
# 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,
# the repeats have to be spelled out as one long children list up front
# rather than relying on any runtime looping. Rounds up so the window is
# always fully covered, even if that runs a little past
# STATIC_OVERLAY_SECONDS -- better than the jitter cutting out early.
_GLITCH_CYCLE_TICKS = len(GLITCH_STATES) * GLITCH_HOLD_TICKS + GLITCH_PAUSE_TICKS
_GLITCH_TICKS_PER_SECOND = 20 # 1000ms / 50ms-per-tick
GLITCH_REPEATS = int(STATIC_OVERLAY_SECONDS * _GLITCH_TICKS_PER_SECOND / _GLITCH_CYCLE_TICKS) + 1
def _glitch_band(zoomed_art, top, height, shift_x, hue_change): def _glitch_band(zoomed_art, top, height, shift_x, hue_change):
tinted = filter.Hue(child = zoomed_art, change = hue_change) tinted = filter.Hue(child = zoomed_art, change = hue_change)
band = render.Padding( band = render.Padding(
@@ -379,29 +390,36 @@ def _glitch_band(zoomed_art, top, height, shift_x, hue_change):
# the 32px art slot, bleeding into the text column next to it. # the 32px art slot, bleeding into the text column next to it.
return render.Box(width = ART_SLOT, height = ART_SLOT, child = shifted) return render.Box(width = ART_SLOT, height = ART_SLOT, child = shifted)
def _glitch_jitter(zoomed_art): def _glitch_hold(child, duration):
states = [] # A no-op Transformation purely to pin a Sequence child's FrameCount
for top1, h1, shift1, hue1, top2, h2, shift2, hue2 in GLITCH_STATES: # to `duration` ticks, so render.Sequence holds on it for that long
state = render.Stack(children = [ # before advancing to the next child -- Sequence has no other way to
_glitch_band(zoomed_art, top1, h1, shift1, hue1), # control how long a given child plays.
_glitch_band(zoomed_art, top2, h2, shift2, hue2), return animation.Transformation(
]) child = child,
duration = duration,
# Wrapping in a no-op Transformation just to pin this state's
# FrameCount to GLITCH_HOLD_TICKS, so render.Sequence holds on it
# for that many ticks before advancing to the next state --
# Sequence has no other way to control how long a child plays.
states.append(animation.Transformation(
child = state,
duration = GLITCH_HOLD_TICKS,
width = ART_SLOT, width = ART_SLOT,
height = ART_SLOT, height = ART_SLOT,
keyframes = [ keyframes = [
animation.Keyframe(percentage = 0.0, transforms = []), animation.Keyframe(percentage = 0.0, transforms = []),
animation.Keyframe(percentage = 1.0, transforms = []), animation.Keyframe(percentage = 1.0, transforms = []),
], ],
)) )
return render.Sequence(children = states)
def _glitch_jitter(zoomed_art):
pause = _glitch_hold(render.Box(width = ART_SLOT, height = ART_SLOT), GLITCH_PAUSE_TICKS)
sequence = []
for _ in range(GLITCH_REPEATS):
for top1, h1, shift1, hue1, top2, h2, shift2, hue2 in GLITCH_STATES:
state = render.Stack(children = [
_glitch_band(zoomed_art, top1, h1, shift1, hue1),
_glitch_band(zoomed_art, top2, h2, shift2, hue2),
])
sequence.append(_glitch_hold(state, GLITCH_HOLD_TICKS))
sequence.append(pause)
return render.Sequence(children = sequence)
def _glitch_scanlines(): def _glitch_scanlines():
# render.Sequence doesn't loop (it just stops painting once its total # render.Sequence doesn't loop (it just stops painting once its total
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.