Skip to content

Waves

Demo

Quick Start

waves.py
from terminaltexteffects.effects.effect_waves import Waves

effect = Waves("YourTextHere")
with effect.terminal_output() as terminal:
    for frame in effect:
        terminal.print(frame)

Circular Waves

New in 0.16.0

Circular directions, CharacterOrder directions, --reverse-wave-direction, and --travel-speed are available on development main and are not in the published 0.15.0 package.

The default direction, circle_center_to_outside, spreads waves outward in circular rings. Use circle_outside_to_center for waves that converge on the text center. These directions account for terminal character proportions. The existing diamonds_center_to_outside and diamonds_outside_to_center directions use diamond-shaped groups. The former center_to_outside and outside_to_center spellings remain accepted as compatibility aliases.

tte waves --wave-direction circle_center_to_outside
tte waves --wave-direction circle_outside_to_center

Character Orders and Reversal

--wave-direction accepts every CharacterOrder: rows, columns, diagonals, diamond and circular bands, individual row traversals, random order, and all six spiral orders. By default, spatial orders activate one complete group per frame and individual orders activate one character per frame. Double and quad spiral arms interleave in that individual character sequence. The default remains circle_center_to_outside.

Use --reverse-wave-direction to reverse activation order while retaining each group's membership. Reversed spirals travel outward along the selected path. Reversal defaults to off.

tte waves --wave-direction spiral_clockwise_quad
tte waves --wave-direction spiral_counter_clockwise_double --reverse-wave-direction --travel-speed 8
from terminaltexteffects import CharacterOrder
from terminaltexteffects.effects.effect_waves import Waves

effect = Waves("YourTextHere")
effect.effect_config.wave_direction = CharacterOrder.SPIRAL_CLOCKWISE
effect.effect_config.reverse_wave_direction = True
effect.effect_config.travel_speed = 8

CLI names, including the previous eight directions, and legacy CharacterGroup / CharacterSort values normalize to CharacterOrder. The circular default and grouped activation timing at speed 1 are unchanged.

Travel Speed

--travel-speed sets how many ordered entries activate per frame. For individual orders this is the number of characters; for spatial orders it is the number of complete groups. The value must be a positive integer and defaults to 1. The last batch activates only the remaining entries, retaining the selected order and reversal. Animation timing still advances once per frame.

tte waves --wave-direction spiral_clockwise --travel-speed 12
tte waves --wave-direction circle_center_to_outside --travel-speed 2

Use --wave-length to change the duration of each character's wave animation; --travel-speed controls how quickly new characters or groups start their waves.

Waves travel across the terminal leaving behind the characters.

Classes:

Name Description
Waves

Creates waves that travel across the terminal, leaving behind the characters.

WavesConfig

Configuration for the Waves effect.

WavesIterator

Iterates over the effect. Does not normally need to be called directly.

Waves

Bases: BaseEffect[WavesConfig]

Creates waves that travel across the terminal, leaving behind the characters.

Attributes:

Name Type Description
effect_config WavesConfig

Configuration for the effect.

terminal_config TerminalConfig

Configuration for the terminal.

Source code in terminaltexteffects/effects/effect_waves.py
class Waves(BaseEffect[WavesConfig]):
    """Creates waves that travel across the terminal, leaving behind the characters.

    Attributes:
        effect_config (WavesConfig): Configuration for the effect.
        terminal_config (TerminalConfig): Configuration for the terminal.

    """

    @property
    def _config_cls(self) -> type[WavesConfig]:
        return WavesConfig

    @property
    def _iterator_cls(self) -> type[WavesIterator]:
        return WavesIterator

WavesConfig dataclass

Bases: BaseConfig

Configuration for the Waves effect.

Attributes:

Name Type Description
wave_symbols tuple[str, ...]

Individually printable symbols to use in sequence for the wave animation.

wave_gradient_stops tuple[Color, ...]

Tuple of colors for the animated wave gradient. If only one color is provided, the characters will be displayed in that color.

wave_gradient_steps tuple[int, ...]

Tuple of the number of gradient steps to use. More steps will create a smoother and longer gradient animation. Valid values are n > 0.

wave_count int

Number of waves to generate. Valid values are n > 0.

wave_length int

The number of frames for each step of the wave. Higher wave-lengths will create a slower wave. Valid values are n > 0.

reverse_wave_direction bool

Reverse the complete traversal while preserving group membership.

wave_direction CharacterOrder

Character order for wave activation.

travel_speed int

Number of ordered entries to activate per frame. Each entry is a complete spatial group or an individual character. Valid values are n > 0. Defaults to 1.

wave_easing EasingFunction

Easing function to use for wave travel.

final_gradient_stops tuple[Color, ...]

Tuple of colors for the final color gradient. If only one color is provided, the characters will be displayed in that color.

final_gradient_steps tuple[int, ...] | int

Tuple of the number of gradient steps to use. More steps will create a smoother and longer gradient animation. Valid values are n > 0.

final_gradient_direction Direction

Direction of the final gradient.

Source code in terminaltexteffects/effects/effect_waves.py
@dataclass
class WavesConfig(BaseConfig):
    """Configuration for the Waves effect.

    Attributes:
        wave_symbols (tuple[str, ...]): Individually printable symbols to use in sequence for the wave animation.
        wave_gradient_stops (tuple[Color, ...]): Tuple of colors for the animated wave gradient. If only one color is
            provided, the characters will be displayed in that color.
        wave_gradient_steps (tuple[int, ...]): Tuple of the number of gradient steps to use. More steps will create a
            smoother and longer gradient animation. Valid values are n > 0.
        wave_count (int): Number of waves to generate. Valid values are n > 0.
        wave_length (int): The number of frames for each step of the wave. Higher wave-lengths will create a slower
            wave. Valid values are n > 0.
        reverse_wave_direction (bool): Reverse the complete traversal while preserving group membership.
        wave_direction (CharacterOrder): Character order for wave activation.
        travel_speed (int): Number of ordered entries to activate per frame. Each entry is a complete spatial
            group or an individual character. Valid values are n > 0. Defaults to 1.
        wave_easing (easing.EasingFunction): Easing function to use for wave travel.
        final_gradient_stops (tuple[Color, ...]): Tuple of colors for the final color gradient. If only one color is
            provided, the characters will be displayed in that color.
        final_gradient_steps (tuple[int, ...] | int): Tuple of the number of gradient steps to use. More steps will
            create a smoother and longer gradient animation. Valid values are n > 0.
        final_gradient_direction (Gradient.Direction): Direction of the final gradient.

    """

    parser_spec: argutils.ParserSpec = argutils.ParserSpec(
        name="waves",
        help="Waves travel across the terminal leaving behind the characters.",
        description="waves | Waves travel across the terminal leaving behind the characters.",
        epilog=(
            f"{argutils.EASING_EPILOG} Example: terminaltexteffects waves --wave-symbols ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ "
            "▇ ▆ ▅ ▄ ▃ ▂ ▁ --wave-gradient-stops f0ff65 ffb102 31a0d4 ffb102 f0ff65 --wave-gradient-steps 6 "
            "--wave-count 7 --wave-length 2 --wave-direction circle_center_to_outside --wave-easing IN_OUT_SINE "
            "--final-gradient-stops ffb102 31a0d4 f0ff65 --final-gradient-steps 12 "
            "--final-gradient-direction diagonal"
        ),
    )

    wave_symbols: tuple[str, ...] = argutils.ArgSpec(
        name="--wave-symbols",
        type=argutils.Symbol.type_parser,
        default=("▁", "▂", "▃", "▄", "▅", "▆", "▇", "█", "▇", "▆", "▅", "▄", "▃", "▂", "▁"),
        nargs="+",
        action=argutils.TupleAction,
        metavar=argutils.Symbol.METAVAR,
        help="Space separated, individually printable symbols to use in sequence for the wave animation.",
    )  # pyright: ignore[reportAssignmentType]
    ("tuple[str, ...] : Individually printable symbols to use in sequence for the wave animation.")

    wave_gradient_stops: tuple[Color, ...] = argutils.ArgSpec(
        name="--wave-gradient-stops",
        type=argutils.ColorArg.type_parser,
        nargs="+",
        action=argutils.TupleAction,
        default=(Color("#f0ff65"), Color("#ffb102"), Color("#31a0d4"), Color("#ffb102"), Color("#f0ff65")),
        metavar=argutils.ColorArg.METAVAR,
        help="Space separated, unquoted, list of colors for the animated wave gradient. If "
        "only one color is provided, the characters will be displayed in that color.",
    )  # pyright: ignore[reportAssignmentType]
    (
        "tuple[Color, ...] : Tuple of colors for the animated wave gradient. If only one color is provided, the "
        "characters will be displayed in that color."
    )

    wave_gradient_steps: tuple[int, ...] = argutils.ArgSpec(
        name="--wave-gradient-steps",
        type=argutils.PositiveInt.type_parser,
        nargs="+",
        action=argutils.TupleAction,
        default=(6,),
        metavar=argutils.PositiveInt.METAVAR,
        help="Space separated, unquoted, list of the number of gradient steps to use. More steps will create a "
        "smoother and longer gradient animation.",
    )  # pyright: ignore[reportAssignmentType]
    (
        "tuple[int, ...] : Tuple of the number of gradient steps to use. More steps will create a smoother and "
        "longer gradient animation."
    )

    wave_count: int = argutils.ArgSpec(
        name="--wave-count",
        type=argutils.PositiveInt.type_parser,
        default=7,
        help="Number of waves to generate. n > 0.",
    )  # pyright: ignore[reportAssignmentType]
    "int : Number of waves to generate. n > 0."

    wave_length: int = argutils.ArgSpec(
        name="--wave-length",
        type=argutils.PositiveInt.type_parser,
        default=2,
        metavar=argutils.PositiveInt.METAVAR,
        help="The number of frames for each step of the wave. Higher wave-lengths will create a slower wave.",
    )  # pyright: ignore[reportAssignmentType]
    "int : The number of frames for each step of the wave. Higher wave-lengths will create a slower wave."

    wave_direction: argutils.CharacterOrder | argutils.CharacterGroup | argutils.CharacterSort = argutils.ArgSpec(
        name="--wave-direction",
        default=argutils.CharacterOrder.CIRCLE_CENTER_TO_OUTSIDE,
        type=argutils.CharacterOrderArg.type_parser,
        metavar=" ".join(argutils.CharacterOrderArg.METAVAR),
        help="Character order for wave activation; spatial groups or individual characters, including spirals.",
    )  # pyright: ignore[reportAssignmentType]
    "CharacterOrder : Character order for wave activation."

    reverse_wave_direction: bool = argutils.ArgSpec(
        name="--reverse-wave-direction",
        default=False,
        action="store_true",
        help="Reverse the complete wave direction traversal.",
    )  # pyright: ignore[reportAssignmentType]
    "bool : Reverse wave activation order, preserving group membership."

    travel_speed: int = argutils.ArgSpec(
        name="--travel-speed",
        type=argutils.PositiveInt.type_parser,
        default=1,
        metavar=argutils.PositiveInt.METAVAR,
        help="Number of ordered entries to activate per frame: whole spatial groups or individual characters. n > 0.",
    )  # pyright: ignore[reportAssignmentType]
    "int : Number of ordered groups or individual characters to activate per frame. Defaults to 1."

    wave_easing: easing.EasingFunction = argutils.ArgSpec(
        name="--wave-easing",
        type=argutils.Ease.type_parser,
        default=easing.in_out_sine,
        help="Easing function to use for wave travel.",
    )  # pyright: ignore[reportAssignmentType]
    "easing.EasingFunction : Easing function to use for wave travel."

    final_gradient_stops: tuple[Color, ...] = FinalGradientStopsArg(
        default=(Color("#ffb102"), Color("#31a0d4"), Color("#f0ff65")),
    )  # pyright: ignore[reportAssignmentType]
    (
        "tuple[Color, ...] : Tuple of colors for the final color gradient. If only one color is provided, the "
        "characters will be displayed in that color."
    )

    final_gradient_steps: tuple[int, ...] | int = FinalGradientStepsArg(
        default=12,
    )  # pyright: ignore[reportAssignmentType]
    (
        "tuple[int, ...] | int : Int or Tuple of ints for the number of gradient steps to use. More steps "
        "will create a smoother and longer gradient animation."
    )

    final_gradient_direction: Gradient.Direction = FinalGradientDirectionArg(
        default=Gradient.Direction.DIAGONAL,
    )  # pyright: ignore[reportAssignmentType]
    "Gradient.Direction : Direction of the final gradient."

final_gradient_direction = FinalGradientDirectionArg(default=Gradient.Direction.DIAGONAL) class-attribute instance-attribute

Gradient.Direction : Direction of the final gradient.

final_gradient_steps = FinalGradientStepsArg(default=12) class-attribute instance-attribute

tuple[int, ...] | int : Int or Tuple of ints for the number of gradient steps to use. More steps will create a smoother and longer gradient animation.

final_gradient_stops = FinalGradientStopsArg(default=(Color('#ffb102'), Color('#31a0d4'), Color('#f0ff65'))) class-attribute instance-attribute

tuple[Color, ...] : Tuple of colors for the final color gradient. If only one color is provided, the characters will be displayed in that color.

reverse_wave_direction = argutils.ArgSpec(name='--reverse-wave-direction', default=False, action='store_true', help='Reverse the complete wave direction traversal.') class-attribute instance-attribute

bool : Reverse wave activation order, preserving group membership.

travel_speed = argutils.ArgSpec(name='--travel-speed', type=argutils.PositiveInt.type_parser, default=1, metavar=argutils.PositiveInt.METAVAR, help='Number of ordered entries to activate per frame: whole spatial groups or individual characters. n > 0.') class-attribute instance-attribute

int : Number of ordered groups or individual characters to activate per frame. Defaults to 1.

wave_count = argutils.ArgSpec(name='--wave-count', type=argutils.PositiveInt.type_parser, default=7, help='Number of waves to generate. n > 0.') class-attribute instance-attribute

int : Number of waves to generate. n > 0.

wave_direction = argutils.ArgSpec(name='--wave-direction', default=argutils.CharacterOrder.CIRCLE_CENTER_TO_OUTSIDE, type=argutils.CharacterOrderArg.type_parser, metavar=' '.join(argutils.CharacterOrderArg.METAVAR), help='Character order for wave activation; spatial groups or individual characters, including spirals.') class-attribute instance-attribute

CharacterOrder : Character order for wave activation.

wave_easing = argutils.ArgSpec(name='--wave-easing', type=argutils.Ease.type_parser, default=easing.in_out_sine, help='Easing function to use for wave travel.') class-attribute instance-attribute

easing.EasingFunction : Easing function to use for wave travel.

wave_gradient_steps = argutils.ArgSpec(name='--wave-gradient-steps', type=argutils.PositiveInt.type_parser, nargs='+', action=argutils.TupleAction, default=(6,), metavar=argutils.PositiveInt.METAVAR, help='Space separated, unquoted, list of the number of gradient steps to use. More steps will create a smoother and longer gradient animation.') class-attribute instance-attribute

tuple[int, ...] : Tuple of the number of gradient steps to use. More steps will create a smoother and longer gradient animation.

wave_gradient_stops = argutils.ArgSpec(name='--wave-gradient-stops', type=argutils.ColorArg.type_parser, nargs='+', action=argutils.TupleAction, default=(Color('#f0ff65'), Color('#ffb102'), Color('#31a0d4'), Color('#ffb102'), Color('#f0ff65')), metavar=argutils.ColorArg.METAVAR, help='Space separated, unquoted, list of colors for the animated wave gradient. If only one color is provided, the characters will be displayed in that color.') class-attribute instance-attribute

tuple[Color, ...] : Tuple of colors for the animated wave gradient. If only one color is provided, the characters will be displayed in that color.

wave_length = argutils.ArgSpec(name='--wave-length', type=argutils.PositiveInt.type_parser, default=2, metavar=argutils.PositiveInt.METAVAR, help='The number of frames for each step of the wave. Higher wave-lengths will create a slower wave.') class-attribute instance-attribute

int : The number of frames for each step of the wave. Higher wave-lengths will create a slower wave.

wave_symbols = argutils.ArgSpec(name='--wave-symbols', type=argutils.Symbol.type_parser, default=('▁', '▂', '▃', '▄', '▅', '▆', '▇', '█', '▇', '▆', '▅', '▄', '▃', '▂', '▁'), nargs='+', action=argutils.TupleAction, metavar=argutils.Symbol.METAVAR, help='Space separated, individually printable symbols to use in sequence for the wave animation.') class-attribute instance-attribute

tuple[str, ...] : Individually printable symbols to use in sequence for the wave animation.

WavesIterator

Bases: BaseEffectIterator[WavesConfig]

Iterator for the Waves effect.

Source code in terminaltexteffects/effects/effect_waves.py
class WavesIterator(BaseEffectIterator[WavesConfig]):
    """Iterator for the Waves effect."""

    def __init__(self, effect: Waves) -> None:
        """Initialize the iterator with the provided effect.

        Args:
            effect (Waves): The effect to iterate over.

        """
        super().__init__(effect)
        self.pending_columns: list[list[EffectCharacter]] = []
        self.character_final_color_map: dict[EffectCharacter, ColorPair] = {}
        self.build()

    def build(self) -> None:
        """Build the effect."""
        final_gradient_mapping: dict[Coord, Color] = {}
        if self.terminal.config.existing_color_handling != "dynamic":
            final_gradient = Gradient(*self.config.final_gradient_stops, steps=self.config.final_gradient_steps)
            final_gradient_mapping = final_gradient.build_coordinate_color_mapping(
                self.terminal.canvas.text_bottom,
                self.terminal.canvas.text_top,
                self.terminal.canvas.text_left,
                self.terminal.canvas.text_right,
                self.config.final_gradient_direction,
            )
        final_transition_steps = (
            self.config.final_gradient_steps[0]
            if isinstance(self.config.final_gradient_steps, tuple)
            else self.config.final_gradient_steps
        )
        wave_gradient = Gradient(*self.config.wave_gradient_stops, steps=self.config.wave_gradient_steps)
        transition_gradients: dict[Color, Gradient] = {}

        def transition_gradient(color: Color) -> Gradient:
            """Reuse a settling gradient for each endpoint color during construction."""
            if color not in transition_gradients:
                transition_gradients[color] = Gradient(
                    wave_gradient.spectrum[-1],
                    color,
                    steps=final_transition_steps,
                )
            return transition_gradients[color]

        # Distribute symbols and immutable colors once; each character still owns its playback frames and visuals.
        recipe_scene = Scene(scene_id="wave_recipe")
        recipe_scene.apply_gradient_to_symbols(
            self.config.wave_symbols,
            duration=self.config.wave_length,
            fg_gradient=wave_gradient,
        )
        wave_recipe = tuple(
            (frame.character_visual.symbol, frame.character_visual.colors) for frame in recipe_scene.frames
        )
        for character in self.terminal.get_characters():
            if self.terminal.config.existing_color_handling == "dynamic":
                self.character_final_color_map[character] = ColorPair(
                    fg=character.animation.input_fg_color,
                    bg=character.animation.input_bg_color,
                )
            else:
                self.character_final_color_map[character] = ColorPair(
                    fg=final_gradient_mapping[character.input_coord],
                )
            wave_scn = character.animation.new_scene(cache_easing=True, cache_appearance=True)
            wave_scn.ease = self.config.wave_easing
            for _ in range(self.config.wave_count):
                for symbol, colors in wave_recipe:
                    wave_scn.add_frame(symbol, self.config.wave_length, colors=colors)
            final_scn = character.animation.new_scene()
            if self.terminal.config.existing_color_handling == "dynamic":
                final_colors = self.character_final_color_map[character]
                if final_colors.fg is None and final_colors.bg is None:
                    final_scn.add_frame(character.input_symbol, 10, colors=ColorPair())
                else:
                    final_fg_color = final_colors.fg
                    final_bg_color = final_colors.bg
                    final_scn.apply_gradient_to_symbols(
                        character.input_symbol,
                        duration=10,
                        fg_gradient=(transition_gradient(final_fg_color) if final_fg_color is not None else None),
                        bg_gradient=(transition_gradient(final_bg_color) if final_bg_color is not None else None),
                    )
                    if final_fg_color is None:
                        final_scn.add_frame(character.input_symbol, 10, colors=ColorPair(bg=final_bg_color))
            else:
                final_fg_color = self.character_final_color_map[character].fg
                assert final_fg_color is not None
                for step in transition_gradient(final_fg_color):
                    final_scn.add_frame(character.input_symbol, 10, colors=ColorPair(fg=step))
            character.event_handler.register_event(
                EventHandler.Event.SCENE_COMPLETE,
                wave_scn,
                EventHandler.Action.ACTIVATE_SCENE,
                final_scn,
            )
            character.animation.activate_scene(wave_scn)
            if self.terminal.config.existing_color_handling == "dynamic":
                character.animation.set_appearance(character.input_symbol, self.character_final_color_map[character])
        self.pending_columns = self.terminal.get_characters_grouped(
            order=self.config.wave_direction,
            reverse=self.config.reverse_wave_direction,
        )

    def __next__(self) -> str:
        """Return the next frame in the animation."""
        if self.pending_columns or self.active_characters:
            for _ in range(min(self.config.travel_speed, len(self.pending_columns))):
                next_column = self.pending_columns.pop(0)
                for character in next_column:
                    self.terminal.set_character_visibility(character, is_visible=True)
                    self.active_characters.add(character)
            self.update()
            return self.frame
        raise StopIteration

__init__(effect)

Initialize the iterator with the provided effect.

Parameters:

Name Type Description Default
effect Waves

The effect to iterate over.

required
Source code in terminaltexteffects/effects/effect_waves.py
def __init__(self, effect: Waves) -> None:
    """Initialize the iterator with the provided effect.

    Args:
        effect (Waves): The effect to iterate over.

    """
    super().__init__(effect)
    self.pending_columns: list[list[EffectCharacter]] = []
    self.character_final_color_map: dict[EffectCharacter, ColorPair] = {}
    self.build()

__next__()

Return the next frame in the animation.

Source code in terminaltexteffects/effects/effect_waves.py
def __next__(self) -> str:
    """Return the next frame in the animation."""
    if self.pending_columns or self.active_characters:
        for _ in range(min(self.config.travel_speed, len(self.pending_columns))):
            next_column = self.pending_columns.pop(0)
            for character in next_column:
                self.terminal.set_character_visibility(character, is_visible=True)
                self.active_characters.add(character)
        self.update()
        return self.frame
    raise StopIteration

build()

Build the effect.

Source code in terminaltexteffects/effects/effect_waves.py
def build(self) -> None:
    """Build the effect."""
    final_gradient_mapping: dict[Coord, Color] = {}
    if self.terminal.config.existing_color_handling != "dynamic":
        final_gradient = Gradient(*self.config.final_gradient_stops, steps=self.config.final_gradient_steps)
        final_gradient_mapping = final_gradient.build_coordinate_color_mapping(
            self.terminal.canvas.text_bottom,
            self.terminal.canvas.text_top,
            self.terminal.canvas.text_left,
            self.terminal.canvas.text_right,
            self.config.final_gradient_direction,
        )
    final_transition_steps = (
        self.config.final_gradient_steps[0]
        if isinstance(self.config.final_gradient_steps, tuple)
        else self.config.final_gradient_steps
    )
    wave_gradient = Gradient(*self.config.wave_gradient_stops, steps=self.config.wave_gradient_steps)
    transition_gradients: dict[Color, Gradient] = {}

    def transition_gradient(color: Color) -> Gradient:
        """Reuse a settling gradient for each endpoint color during construction."""
        if color not in transition_gradients:
            transition_gradients[color] = Gradient(
                wave_gradient.spectrum[-1],
                color,
                steps=final_transition_steps,
            )
        return transition_gradients[color]

    # Distribute symbols and immutable colors once; each character still owns its playback frames and visuals.
    recipe_scene = Scene(scene_id="wave_recipe")
    recipe_scene.apply_gradient_to_symbols(
        self.config.wave_symbols,
        duration=self.config.wave_length,
        fg_gradient=wave_gradient,
    )
    wave_recipe = tuple(
        (frame.character_visual.symbol, frame.character_visual.colors) for frame in recipe_scene.frames
    )
    for character in self.terminal.get_characters():
        if self.terminal.config.existing_color_handling == "dynamic":
            self.character_final_color_map[character] = ColorPair(
                fg=character.animation.input_fg_color,
                bg=character.animation.input_bg_color,
            )
        else:
            self.character_final_color_map[character] = ColorPair(
                fg=final_gradient_mapping[character.input_coord],
            )
        wave_scn = character.animation.new_scene(cache_easing=True, cache_appearance=True)
        wave_scn.ease = self.config.wave_easing
        for _ in range(self.config.wave_count):
            for symbol, colors in wave_recipe:
                wave_scn.add_frame(symbol, self.config.wave_length, colors=colors)
        final_scn = character.animation.new_scene()
        if self.terminal.config.existing_color_handling == "dynamic":
            final_colors = self.character_final_color_map[character]
            if final_colors.fg is None and final_colors.bg is None:
                final_scn.add_frame(character.input_symbol, 10, colors=ColorPair())
            else:
                final_fg_color = final_colors.fg
                final_bg_color = final_colors.bg
                final_scn.apply_gradient_to_symbols(
                    character.input_symbol,
                    duration=10,
                    fg_gradient=(transition_gradient(final_fg_color) if final_fg_color is not None else None),
                    bg_gradient=(transition_gradient(final_bg_color) if final_bg_color is not None else None),
                )
                if final_fg_color is None:
                    final_scn.add_frame(character.input_symbol, 10, colors=ColorPair(bg=final_bg_color))
        else:
            final_fg_color = self.character_final_color_map[character].fg
            assert final_fg_color is not None
            for step in transition_gradient(final_fg_color):
                final_scn.add_frame(character.input_symbol, 10, colors=ColorPair(fg=step))
        character.event_handler.register_event(
            EventHandler.Event.SCENE_COMPLETE,
            wave_scn,
            EventHandler.Action.ACTIVATE_SCENE,
            final_scn,
        )
        character.animation.activate_scene(wave_scn)
        if self.terminal.config.existing_color_handling == "dynamic":
            character.animation.set_appearance(character.input_symbol, self.character_final_color_map[character])
    self.pending_columns = self.terminal.get_characters_grouped(
        order=self.config.wave_direction,
        reverse=self.config.reverse_wave_direction,
    )

get_effect_resources()

Get the command, effect class, and configuration class for the effect.

Returns:

Type Description
tuple[str, type[BaseEffect], type[BaseConfig]]

tuple[str, type[BaseEffect], type[BaseConfig]]: The command name, effect class, and configuration class.

Source code in terminaltexteffects/effects/effect_waves.py
def get_effect_resources() -> tuple[str, type[BaseEffect], type[BaseConfig]]:
    """Get the command, effect class, and configuration class for the effect.

    Returns:
        tuple[str, type[BaseEffect], type[BaseConfig]]: The command name, effect class, and configuration class.

    """
    return "waves", Waves, WavesConfig