Animation sequences

AnimationSequence schedules any Animation: image effects, labels, callbacks, scenes, or a solver view. SequenceStep supplies a zero-argument factory, a positive duration, an optional frozen hold, a blank gap, and a caption name. All times are finite simulation seconds, independent of presentation FPS. Both classes are exported from bruhanimate with typed constructors/properties.

from bruhanimate import (
    AnimationSequence, DissolveImageEffect, Layer, Scene, SequenceStep,
    TextStyle, WipeImageEffect, effect_registry, play,
)

def build(buffer):
    title = AnimationSequence(
        SequenceStep(
            lambda: WipeImageEffect("bruh", duration=1, direction="right"),
            duration=1, hold_duration=2, gap=0.5, name="Wipe",
        ),
        SequenceStep(
            lambda: DissolveImageEffect("bruh", duration=1, seed=42),
            duration=1, hold_duration=2, gap=0.5, name="Dissolve",
        ),
        repeat=True,
    )
    return Scene(
        Layer(effect_registry.create("plasma", buffer, " "), updates_per_second=30),
        Layer(title, style=TextStyle(foreground=231), preserve_background=True),
    )

play(effect_factory=build, duration=12, fps=60, on_resize="resize")

For an entrance that reverses back out, configure reverse=True on the image effect and give its step enough time for both legs and its internal hold. For example, WipeImageEffect("bruh", duration=2, reverse=True, hold_duration=3) needs SequenceStep(..., duration=7, gap=1). A step’s hold_duration freezes the child’s final frame after its entire duration; it is separate from a reversing image’s internal hold between legs. The TV demo demonstrates this.

Timing and skipped steps

Only update advances time. Each child receives updates for at most its step duration. When its optional finished property becomes True, further updates stop; its final frame stays available through the remaining duration and hold. The blank gap paints nothing, so a Scene reveals its lower layers cleanly. duration on the sequence includes all step durations, holds, and gaps.

Large time jumps go directly to the destination step. Skipped steps are never constructed or updated. The destination receives one update with its elapsed time; a fixed-tick simulation therefore performs one tick, not a replay of every missed tick. Do not use skipped steps as callbacks for mandatory side effects. Ordinary Player updates provide small intervals for continuous animation.

With repeat=False the sequence finishes at its total duration and retains the last frame, or stays blank if the final step has a gap. repeat=True creates fresh children on every entered cycle and never finishes. An empty sequence is already finished. Use on_complete="hold" when a background should continue.

Ownership and resize

Construction and draw do not call factories. start creates and starts the first child; entering another step closes the old child before creating its replacement. Each factory must return a fresh, unowned Animation. Never return the same object on repeat, or share it with a Scene/Player. Cleanup covers partial startup failures; close is idempotent and safe before start. A closed sequence cannot restart.

The sequence owns only its active child. current exposes a borrowed Animation or None before start/after close; do not close it yourself. index, phase_name, visible, and finished support captions and status displays. Configure steps and repeat at construction. No terminal or input handling is created implicitly; route keyboard events explicitly through Player if children need them.

resize((width, height)) preserves time and the active instance, forwarding the notification to resizable content. Later children receive the latest size after startup and before their first update/draw. Factories should construct inert animations; children without a resize method must draw into arbitrary buffers. Use Panels, layer clocks, and temporary overlays to keep independent background animations running outside the sequence, and ASCII images, transitions, masks, and styling for transition options and masks.

API

Timed, factory-owned animation sequences, independent of presentation FPS.

class bruhanimate.sequence.SequenceStep(factory: Callable[[], Animation], duration: float, hold_duration: float = 0, gap: float = 0, name: str = '')[source]

One animation factory and its simulation-second timing.

factory must return a fresh, unowned Animation each time it is called. duration is positive; hold_duration freezes its final frame, then gap hides it. Early child completion also freezes its frame until the step expires. name labels the step for captions. Construction never calls the factory.

factory: Callable[[], Animation]
duration: float
hold_duration: float = 0
gap: float = 0
name: str = ''
property total_duration: float

Seconds including the frozen hold and blank gap.

__init__(factory: Callable[[], Animation], duration: float, hold_duration: float = 0, gap: float = 0, name: str = '') → None
class bruhanimate.sequence.AnimationSequence(*steps: SequenceStep, repeat: bool = False)[source]

Play timed steps, owning only the active child; optionally repeat forever.

Factories run at start or entry into a step, never during construction/draw. Replaced children close before the next factory runs. close is idempotent, including before start and after failed startup. Do not reuse child instances.

update(dt) uses non-negative simulation seconds. Large jumps skip unvisited steps/cycles without constructing them and update the destination child once with its elapsed time. This is scheduling, not a fixed-tick simulation replay. Child finished=True stops updates, but timing still follows SequenceStep. A finite sequence retains its last frame (or a blank final gap) when finished. Empty sequences finish immediately. Configuration is fixed at construction.

Resize preserves the active child and forwards the latest viewport to future children before their first update/draw. No input is polled or automatically routed; pass a handler to Player explicitly if interactive content needs one.

__init__(*steps: SequenceStep, repeat: bool = False) → None[source]
property duration: float

Seconds for one entire sequence, including holds and gaps.

property index: int

Zero-based active step; zero for an empty sequence.

property current: Animation | None

Borrowed active child, or None before start/after close. Never close it.

property phase_name: str

Active step name; empty for an empty sequence.

property finished: bool
property visible: bool
start() → None[source]
update(dt: float) → None[source]
draw(buffer: Buffer) → None[source]
resize(size: tuple[int, int]) → None[source]
close() → None[source]