Skip to content

Easing Functions

Module: terminaltexteffects.utils.easing

Easing functions take a progress ratio from 0 to 1. Back and elastic functions can return values below 0 or above 1. Custom curves from make_easing can also overshoot when their vertical control points are outside [0, 1]. The horizontal control points, x1 and x2, must be finite values in [0, 1].

EasingTracker preserves overshoot by default; pass clamp=True to limit its stepped values to [0, 1]. SequenceEaser always clamps its internal tracker before selecting elements.

from terminaltexteffects.utils import easing

assert easing.in_back(0.5) < 0
assert easing.out_elastic(0.5) > 1

tracker = easing.EasingTracker(lambda _: 1.25, total_steps=1, clamp=True)
assert tracker.step() == 1.0

Functions and Classes for easing calculations.

Easing functions take progress ratios from 0 to 1. Back and elastic functions can return values outside that range, as can custom cubic Bezier curves.

Classes:

Name Description
EasingTracker

Tracks the progression of an easing function over a set number of steps.

SequenceEaser

Eases over the leading portion of a sequence, tracking added, removed, and total elements.

Functions:

Name Description
linear

Linear easing function.

in_sine

Ease in using a sine function.

out_sine

Ease out using a sine function.

in_out_sine

Ease in/out using a sine function.

in_quad

Ease in using a quadratic function.

out_quad

Ease out using a quadratic function.

in_out_quad

Ease in/out using a quadratic function.

in_cubic

Ease in using a cubic function.

out_cubic

Ease out using a cubic function.

in_out_cubic

Ease in/out using a cubic function.

in_quart

Ease in using a quartic function.

out_quart

Ease out using a quartic function.

in_out_quart

Ease in/out using a quartic function.

in_quint

Ease in using a quintic function.

out_quint

Ease out using a quintic function.

in_out_quint

Ease in/out using a quintic function.

in_expo

Ease in using an exponential function.

out_expo

Ease out using an exponential function.

in_out_expo

Ease in/out using an exponential function.

in_circ

Ease in using a circular function.

out_circ

Ease out using a circular function.

in_out_circ

Ease in/out using a circular function.

in_back

Ease in using a back function.

out_back

Ease out using a back function.

in_out_back

Ease in/out using a back function.

in_elastic

Ease in using an elastic function.

out_elastic

Ease out using an elastic function.

in_out_elastic

Ease in/out using an elastic function.

in_bounce

Ease in using a bounce function.

out_bounce

Ease out using a bounce function.

in_out_bounce

Ease in/out using a bounce function.

make_easing

Create a cubic Bezier easing function using the provided control points.

EasingFunction = typing.Callable[[float], float] module-attribute

Easing functions take a progress ratio from 0 to 1 and may overshoot that range.

EasingTracker dataclass

Describe the progression of items as an easing function is applied over a sequence.

Eased values preserve overshoot by default. Set clamp=True to limit each stepped value to [0, 1]; this also affects step_delta.

Attributes:

Name Type Description
easing_function EasingFunction

The easing function being tracked.

total_steps int

Positive number of steps for the easing function.

clamp bool

Whether eased values should be clamped to the range [0, 1] after each step. Changes to this setting affect subsequent steps.

current_step int

The current step in the easing progression.

progress_ratio float

The ratio of the current step to the total steps.

step_delta float

The change in eased value from the last step to the current step.

eased_value float

The current eased value based on the easing function and progress ratio.

Methods:

Name Description
step

Advance the easing tracker by one step and return the new eased value.

is_complete

Check if the easing tracker has completed all steps.

Source code in terminaltexteffects/utils/easing.py
@dataclass
class EasingTracker:
    """Describe the progression of items as an easing function is applied over a sequence.

    Eased values preserve overshoot by default. Set `clamp=True` to limit each
    stepped value to `[0, 1]`; this also affects `step_delta`.

    Attributes:
        easing_function (EasingFunction): The easing function being tracked.
        total_steps (int): Positive number of steps for the easing function.
        clamp (bool): Whether eased values should be clamped to the range `[0, 1]`
            after each step. Changes to this setting affect subsequent steps.
        current_step (int): The current step in the easing progression.
        progress_ratio (float): The ratio of the current step to the total steps.
        step_delta (float): The change in eased value from the last step to the current step.
        eased_value (float): The current eased value based on the easing function and progress ratio.

    Methods:
        step() -> float: Advance the easing tracker by one step and return the new eased value.
        is_complete() -> bool: Check if the easing tracker has completed all steps.

    """

    easing_function: EasingFunction
    total_steps: int = 100
    clamp: bool = False

    def __post_init__(self) -> None:
        """Validate the step count and initialize progression state.

        Raises:
            ValueError: If `total_steps` is not a positive non-boolean integer.

        """
        if isinstance(self.total_steps, bool) or not isinstance(self.total_steps, int) or self.total_steps < 1:
            msg = "total_steps must be a positive integer"
            raise ValueError(msg)
        self.current_step: int = 0
        self.progress_ratio: float = 0.0
        self.step_delta: float = 0.0
        self.eased_value: float = 0.0
        self._last_eased_value: float = 0.0

    def step(self) -> float:
        """Advance the easing tracker by one step.

        If the current step is less than the total steps, increment the current step,
        update the progress ratio, compute the new eased value using the easing function,
        and calculate the step delta.

        If clamp is enabled, the eased value is constrained between 0 and 1.

        Returns:
            float: The new eased value after advancing one step.

        """
        if self.current_step < self.total_steps:
            self.current_step += 1
            self.progress_ratio = self.current_step / self.total_steps
            self.eased_value = self.easing_function(self.progress_ratio)
            if self.clamp:
                self.eased_value = max(0.0, min(self.eased_value, 1.0))
            self.step_delta = self.eased_value - self._last_eased_value
            self._last_eased_value = self.eased_value
        return self.eased_value

    def reset(self) -> None:
        """Reset the easing tracker to the initial state."""
        self.current_step = 0
        self.progress_ratio = 0.0
        self.step_delta = 0.0
        self.eased_value = 0.0
        self._last_eased_value = 0.0

    def is_complete(self) -> bool:
        """Check if the easing tracker has completed all steps.

        Returns:
            bool: True if all steps have been completed, False otherwise.

        """
        return self.current_step >= self.total_steps

    def __iter__(self) -> typing.Iterator[float]:
        """Iterate over eased values until completion.

        Yields:
            float: The eased value at each step.

        """
        while not self.is_complete():
            yield self.step()

__iter__()

Iterate over eased values until completion.

Yields:

Name Type Description
float float

The eased value at each step.

Source code in terminaltexteffects/utils/easing.py
def __iter__(self) -> typing.Iterator[float]:
    """Iterate over eased values until completion.

    Yields:
        float: The eased value at each step.

    """
    while not self.is_complete():
        yield self.step()

__post_init__()

Validate the step count and initialize progression state.

Raises:

Type Description
ValueError

If total_steps is not a positive non-boolean integer.

Source code in terminaltexteffects/utils/easing.py
def __post_init__(self) -> None:
    """Validate the step count and initialize progression state.

    Raises:
        ValueError: If `total_steps` is not a positive non-boolean integer.

    """
    if isinstance(self.total_steps, bool) or not isinstance(self.total_steps, int) or self.total_steps < 1:
        msg = "total_steps must be a positive integer"
        raise ValueError(msg)
    self.current_step: int = 0
    self.progress_ratio: float = 0.0
    self.step_delta: float = 0.0
    self.eased_value: float = 0.0
    self._last_eased_value: float = 0.0

is_complete()

Check if the easing tracker has completed all steps.

Returns:

Name Type Description
bool bool

True if all steps have been completed, False otherwise.

Source code in terminaltexteffects/utils/easing.py
def is_complete(self) -> bool:
    """Check if the easing tracker has completed all steps.

    Returns:
        bool: True if all steps have been completed, False otherwise.

    """
    return self.current_step >= self.total_steps

reset()

Reset the easing tracker to the initial state.

Source code in terminaltexteffects/utils/easing.py
def reset(self) -> None:
    """Reset the easing tracker to the initial state."""
    self.current_step = 0
    self.progress_ratio = 0.0
    self.step_delta = 0.0
    self.eased_value = 0.0
    self._last_eased_value = 0.0

step()

Advance the easing tracker by one step.

If the current step is less than the total steps, increment the current step, update the progress ratio, compute the new eased value using the easing function, and calculate the step delta.

If clamp is enabled, the eased value is constrained between 0 and 1.

Returns:

Name Type Description
float float

The new eased value after advancing one step.

Source code in terminaltexteffects/utils/easing.py
def step(self) -> float:
    """Advance the easing tracker by one step.

    If the current step is less than the total steps, increment the current step,
    update the progress ratio, compute the new eased value using the easing function,
    and calculate the step delta.

    If clamp is enabled, the eased value is constrained between 0 and 1.

    Returns:
        float: The new eased value after advancing one step.

    """
    if self.current_step < self.total_steps:
        self.current_step += 1
        self.progress_ratio = self.current_step / self.total_steps
        self.eased_value = self.easing_function(self.progress_ratio)
        if self.clamp:
            self.eased_value = max(0.0, min(self.eased_value, 1.0))
        self.step_delta = self.eased_value - self._last_eased_value
        self._last_eased_value = self.eased_value
    return self.eased_value

SequenceEaser dataclass

Bases: Generic[_T]

Eases over the leading portion of a sequence, tracking added, removed, and total elements.

At each step, total is the contiguous slice sequence[:length] determined by the current eased progress. added and removed describe the change from the previous step. The internal EasingTracker uses clamp=True, so overshooting functions cannot select elements outside the sequence.

Attributes:

Name Type Description
sequence Sequence[_T]

The sequence to ease over.

easing_function EasingFunction

The easing function to use.

total_steps int

Positive number of steps for the easing function.

added Sequence[_T]

Contiguous slice of newly included elements from the current step.

removed Sequence[_T]

Contiguous slice of elements removed from the previous step.

total Sequence[_T]

Current leading slice of sequence selected by eased progress.

Source code in terminaltexteffects/utils/easing.py
@dataclass
class SequenceEaser(typing.Generic[_T]):
    """Eases over the leading portion of a sequence, tracking added, removed, and total elements.

    At each step, `total` is the contiguous slice `sequence[:length]` determined by
    the current eased progress. `added` and `removed` describe the change from the
    previous step. The internal `EasingTracker` uses `clamp=True`, so overshooting
    functions cannot select elements outside the sequence.

    Attributes:
        sequence (Sequence[_T]): The sequence to ease over.
        easing_function (EasingFunction): The easing function to use.
        total_steps (int): Positive number of steps for the easing function.
        added (Sequence[_T]): Contiguous slice of newly included elements from the current step.
        removed (Sequence[_T]): Contiguous slice of elements removed from the previous step.
        total (Sequence[_T]): Current leading slice of `sequence` selected by eased progress.

    """

    sequence: typing.Sequence[_T]
    easing_function: EasingFunction
    total_steps: int = 100
    added: typing.Sequence[_T] = field(init=False, default_factory=list)
    removed: typing.Sequence[_T] = field(init=False, default_factory=list)
    total: typing.Sequence[_T] = field(init=False, default_factory=list)

    def __post_init__(self) -> None:
        """Initialize the SequenceEaser."""
        self.easing_tracker = EasingTracker(
            easing_function=self.easing_function,
            total_steps=self.total_steps,
            clamp=True,
        )

    def step(self) -> typing.Sequence[_T]:
        """Advance the easing tracker by one step and update sequence state.

        `total` is updated to the current eased leading slice of `sequence`. If that
        slice grows, `added` contains the newly included elements. If it shrinks,
        `removed` contains the elements that were dropped. If its length is unchanged,
        both are empty.

        Returns:
            typing.Sequence[_T]: The elements added in the current step.

        """
        previous_length = len(self.total)
        eased_value = self.easing_tracker.step()
        seq_len = len(self.sequence)
        if seq_len == 0:
            self.added = self.sequence[:0]
            self.removed = self.sequence[:0]
            self.total = self.sequence[:0]
            return self.added

        length = int(eased_value * seq_len)
        # Endpoint rounding can otherwise leave the last element unselected.
        if self.easing_tracker.is_complete() and math.isclose(eased_value, 1.0, rel_tol=0.0, abs_tol=1e-12):
            length = seq_len

        if length > previous_length:
            self.added = self.sequence[previous_length:length]
            self.removed = self.sequence[:0]
        elif length < previous_length:
            self.added = self.sequence[:0]
            self.removed = self.sequence[length:previous_length]
        else:
            self.added = self.sequence[:0]
            self.removed = self.sequence[:0]

        self.total = self.sequence[:length]
        return self.added

    def is_complete(self) -> bool:
        """Check if the easing over the sequence is complete.

        Returns:
            bool: True if all steps have been completed, False otherwise.

        """
        return self.easing_tracker.is_complete()

    def reset(self) -> None:
        """Reset the SequenceEaser to the initial state."""
        self.easing_tracker.reset()
        self.added = self.sequence[:0]
        self.removed = self.sequence[:0]
        self.total = self.sequence[:0]

__post_init__()

Initialize the SequenceEaser.

Source code in terminaltexteffects/utils/easing.py
def __post_init__(self) -> None:
    """Initialize the SequenceEaser."""
    self.easing_tracker = EasingTracker(
        easing_function=self.easing_function,
        total_steps=self.total_steps,
        clamp=True,
    )

is_complete()

Check if the easing over the sequence is complete.

Returns:

Name Type Description
bool bool

True if all steps have been completed, False otherwise.

Source code in terminaltexteffects/utils/easing.py
def is_complete(self) -> bool:
    """Check if the easing over the sequence is complete.

    Returns:
        bool: True if all steps have been completed, False otherwise.

    """
    return self.easing_tracker.is_complete()

reset()

Reset the SequenceEaser to the initial state.

Source code in terminaltexteffects/utils/easing.py
def reset(self) -> None:
    """Reset the SequenceEaser to the initial state."""
    self.easing_tracker.reset()
    self.added = self.sequence[:0]
    self.removed = self.sequence[:0]
    self.total = self.sequence[:0]

step()

Advance the easing tracker by one step and update sequence state.

total is updated to the current eased leading slice of sequence. If that slice grows, added contains the newly included elements. If it shrinks, removed contains the elements that were dropped. If its length is unchanged, both are empty.

Returns:

Type Description
Sequence[_T]

typing.Sequence[_T]: The elements added in the current step.

Source code in terminaltexteffects/utils/easing.py
def step(self) -> typing.Sequence[_T]:
    """Advance the easing tracker by one step and update sequence state.

    `total` is updated to the current eased leading slice of `sequence`. If that
    slice grows, `added` contains the newly included elements. If it shrinks,
    `removed` contains the elements that were dropped. If its length is unchanged,
    both are empty.

    Returns:
        typing.Sequence[_T]: The elements added in the current step.

    """
    previous_length = len(self.total)
    eased_value = self.easing_tracker.step()
    seq_len = len(self.sequence)
    if seq_len == 0:
        self.added = self.sequence[:0]
        self.removed = self.sequence[:0]
        self.total = self.sequence[:0]
        return self.added

    length = int(eased_value * seq_len)
    # Endpoint rounding can otherwise leave the last element unselected.
    if self.easing_tracker.is_complete() and math.isclose(eased_value, 1.0, rel_tol=0.0, abs_tol=1e-12):
        length = seq_len

    if length > previous_length:
        self.added = self.sequence[previous_length:length]
        self.removed = self.sequence[:0]
    elif length < previous_length:
        self.added = self.sequence[:0]
        self.removed = self.sequence[length:previous_length]
    else:
        self.added = self.sequence[:0]
        self.removed = self.sequence[:0]

    self.total = self.sequence[:length]
    return self.added

in_back(progress_ratio)

Ease in using a back function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value that may fall below 0.

Source code in terminaltexteffects/utils/easing.py
def in_back(progress_ratio: float) -> float:
    """Ease in using a back function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value that may fall below 0.

    """
    c1 = 1.70158
    c3 = c1 + 1
    return c3 * progress_ratio**3 - c1 * progress_ratio**2

in_bounce(progress_ratio)

Ease in using a bounce function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def in_bounce(progress_ratio: float) -> float:
    """Ease in using a bounce function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    return 1 - out_bounce(1 - progress_ratio)

in_circ(progress_ratio)

Ease in using a circular function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def in_circ(progress_ratio: float) -> float:
    """Ease in using a circular function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    return 1 - math.sqrt(1 - progress_ratio**2)

in_cubic(progress_ratio)

Ease in using a cubic function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def in_cubic(progress_ratio: float) -> float:
    """Ease in using a cubic function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    return progress_ratio**3

in_elastic(progress_ratio)

Ease in using an elastic function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value that may fall below 0.

Source code in terminaltexteffects/utils/easing.py
def in_elastic(progress_ratio: float) -> float:
    """Ease in using an elastic function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value that may fall below 0.

    """
    c4 = (2 * math.pi) / 3
    if progress_ratio == 0:
        return 0
    if progress_ratio == 1:
        return 1
    return -(2 ** (10 * progress_ratio - 10)) * math.sin((progress_ratio * 10 - 10.75) * c4)

in_expo(progress_ratio)

Ease in using an exponential function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def in_expo(progress_ratio: float) -> float:
    """Ease in using an exponential function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    if progress_ratio == 0:
        return 0
    return 2 ** (10 * progress_ratio - 10)

in_out_back(progress_ratio)

Ease in/out using a back function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value that may fall below 0 or exceed 1.

Source code in terminaltexteffects/utils/easing.py
def in_out_back(progress_ratio: float) -> float:
    """Ease in/out using a back function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value that may fall below 0 or exceed 1.

    """
    c1 = 1.70158
    c2 = c1 * 1.525
    if progress_ratio < 0.5:
        return ((2 * progress_ratio) ** 2 * ((c2 + 1) * 2 * progress_ratio - c2)) / 2
    return ((2 * progress_ratio - 2) ** 2 * ((c2 + 1) * (progress_ratio * 2 - 2) + c2) + 2) / 2

in_out_bounce(progress_ratio)

Ease in/out using a bounce function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def in_out_bounce(progress_ratio: float) -> float:
    """Ease in/out using a bounce function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    if progress_ratio < 0.5:
        return (1 - out_bounce(1 - 2 * progress_ratio)) / 2
    return (1 + out_bounce(2 * progress_ratio - 1)) / 2

in_out_circ(progress_ratio)

Ease in/out using a circular function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def in_out_circ(progress_ratio: float) -> float:
    """Ease in/out using a circular function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    if progress_ratio < 0.5:
        return (1 - math.sqrt(1 - (2 * progress_ratio) ** 2)) / 2
    return (math.sqrt(1 - (-2 * progress_ratio + 2) ** 2) + 1) / 2

in_out_cubic(progress_ratio)

Ease in/out using a cubic function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def in_out_cubic(progress_ratio: float) -> float:
    """Ease in/out using a cubic function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    if progress_ratio < 0.5:
        return 4 * progress_ratio**3
    return 1 - (-2 * progress_ratio + 2) ** 3 / 2

in_out_elastic(progress_ratio)

Ease in/out using an elastic function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value that may fall below 0 or exceed 1.

Source code in terminaltexteffects/utils/easing.py
def in_out_elastic(progress_ratio: float) -> float:
    """Ease in/out using an elastic function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value that may fall below 0 or exceed 1.

    """
    c5 = (2 * math.pi) / 4.5
    if progress_ratio == 0:
        return 0
    if progress_ratio == 1:
        return 1
    if progress_ratio < 0.5:
        return -(2 ** (20 * progress_ratio - 10) * math.sin((20 * progress_ratio - 11.125) * c5)) / 2
    return (2 ** (-20 * progress_ratio + 10) * math.sin((20 * progress_ratio - 11.125) * c5)) / 2 + 1

in_out_expo(progress_ratio)

Ease in/out using an exponential function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def in_out_expo(progress_ratio: float) -> float:
    """Ease in/out using an exponential function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    if progress_ratio == 0:
        return 0
    if progress_ratio == 1:
        return 1
    if progress_ratio < 0.5:
        return 2 ** (20 * progress_ratio - 10) / 2
    return (2 - 2 ** (-20 * progress_ratio + 10)) / 2

in_out_quad(progress_ratio)

Ease in/out using a quadratic function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def in_out_quad(progress_ratio: float) -> float:
    """Ease in/out using a quadratic function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    if progress_ratio < 0.5:
        return 2 * progress_ratio**2
    return 1 - (-2 * progress_ratio + 2) ** 2 / 2

in_out_quart(progress_ratio)

Ease in/out using a quartic function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def in_out_quart(progress_ratio: float) -> float:
    """Ease in/out using a quartic function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    if progress_ratio < 0.5:
        return 8 * progress_ratio**4
    return 1 - (-2 * progress_ratio + 2) ** 4 / 2

in_out_quint(progress_ratio)

Ease in/out using a quintic function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def in_out_quint(progress_ratio: float) -> float:
    """Ease in/out using a quintic function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    if progress_ratio < 0.5:
        return 16 * progress_ratio**5
    return 1 - (-2 * progress_ratio + 2) ** 5 / 2

in_out_sine(progress_ratio)

Ease in/out using a sine function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def in_out_sine(progress_ratio: float) -> float:
    """Ease in/out using a sine function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    return -(math.cos(math.pi * progress_ratio) - 1) / 2

in_quad(progress_ratio)

Ease in using a quadratic function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def in_quad(progress_ratio: float) -> float:
    """Ease in using a quadratic function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    return progress_ratio**2

in_quart(progress_ratio)

Ease in using a quartic function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def in_quart(progress_ratio: float) -> float:
    """Ease in using a quartic function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    return progress_ratio**4

in_quint(progress_ratio)

Ease in using a quintic function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def in_quint(progress_ratio: float) -> float:
    """Ease in using a quintic function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    return progress_ratio**5

in_sine(progress_ratio)

Ease in using a sine function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def in_sine(progress_ratio: float) -> float:
    """Ease in using a sine function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    return 1 - math.cos((progress_ratio * math.pi) / 2)

linear(progress_ratio)

Linear easing function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def linear(progress_ratio: float) -> float:
    """Linear easing function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    return progress_ratio

make_easing(x1, y1, x2, y2)

Create a cubic Bezier easing function using the provided control points.

The easing function maps a progress ratio from 0 to 1 to an output value according to a cubic Bezier curve defined by four points. Horizontal control points must be finite and lie in [0, 1]; vertical control points may lie outside that range and produce overshoot: - Start point: (0, 0) - First control point: (x1, y1) - Second control point: (x2, y2) - End point: (1, 1)

Parameters:

Name Type Description Default
x1 float

Finite horizontal position of the first control point, from 0 to 1 inclusive. Smaller values make the curve start off steeper, while larger values delay the initial acceleration.

required
y1 float

Vertical position of the first control point; values outside [0, 1] are allowed. Smaller values create a gentler ease-in effect; larger values increase the initial acceleration.

required
x2 float

Finite horizontal position of the second control point, from 0 to 1 inclusive. Larger values extend the period of change, affecting how late the acceleration or deceleration begins.

required
y2 float

Vertical position of the second control point; values outside [0, 1] are allowed. Larger values can create a more abrupt ease-out effect; smaller values result in a smoother finish.

required

Note: Use a resource such as cubic-bezier.com to design an appropriate easing curve for your needs.

Returns:

Name Type Description
EasingFunction EasingFunction

A function that takes progress_ratio from 0 to 1 and returns the

EasingFunction

curve's eased value, which may be outside [0, 1].

Raises:

Type Description
ValueError

If x1 or x2 is non-finite or outside 0 to 1.

Source code in terminaltexteffects/utils/easing.py
def make_easing(x1: float, y1: float, x2: float, y2: float) -> EasingFunction:
    """Create a cubic Bezier easing function using the provided control points.

    The easing function maps a progress ratio from 0 to 1 to an output value
    according to a cubic Bezier curve defined by four points. Horizontal control
    points must be finite and lie in `[0, 1]`; vertical control points may lie
    outside that range and produce overshoot:
      - Start point: (0, 0)
      - First control point: (x1, y1)
      - Second control point: (x2, y2)
      - End point: (1, 1)

    Args:
      x1 (float): Finite horizontal position of the first control point, from 0 to 1 inclusive.
          Smaller values make the curve start off steeper, while larger values delay the initial acceleration.
      y1 (float): Vertical position of the first control point; values outside `[0, 1]` are allowed.
          Smaller values create a gentler ease-in effect; larger values increase the initial acceleration.
      x2 (float): Finite horizontal position of the second control point, from 0 to 1 inclusive.
          Larger values extend the period of change, affecting how late the acceleration or deceleration begins.
      y2 (float): Vertical position of the second control point; values outside `[0, 1]` are allowed.
          Larger values can create a more abrupt ease-out effect; smaller values result in a smoother finish.

    Note: Use a resource such as cubic-bezier.com to design an appropriate easing curve for your needs.

    Returns:
        EasingFunction: A function that takes `progress_ratio` from 0 to 1 and returns the
        curve's eased value, which may be outside `[0, 1]`.

    Raises:
        ValueError: If `x1` or `x2` is non-finite or outside 0 to 1.

    """
    if not math.isfinite(x1) or not 0 <= x1 <= 1:
        msg = "x1 must be finite and between 0 and 1"
        raise ValueError(msg)
    if not math.isfinite(x2) or not 0 <= x2 <= 1:
        msg = "x2 must be finite and between 0 and 1"
        raise ValueError(msg)

    # Compute Bezier curve x for a given parameter t.
    def sample_curve_x(t: float) -> float:
        return 3 * x1 * (1 - t) ** 2 * t + 3 * x2 * (1 - t) * t**2 + t**3

    # Compute Bezier curve y for a given parameter t.
    def sample_curve_y(t: float) -> float:
        return 3 * y1 * (1 - t) ** 2 * t + 3 * y2 * (1 - t) * t**2 + t**3

    # Compute derivative of curve x with respect to t.
    def sample_curve_derivative_x(t: float) -> float:
        return 3 * (1 - t) ** 2 * x1 + 6 * (1 - t) * t * (x2 - x1) + 3 * t**2 * (1 - x2)

    def bezier_easing(progress: float) -> float:
        # Clamp progress between 0 and 1.
        if progress <= 0:
            return 0
        if progress >= 1:
            return 1

        # Keep Newton's method inside a bracket so flat derivatives cannot
        # send t outside the curve or leave an unconverged endpoint result.
        lower, upper = 0.0, 1.0
        t = progress
        for _ in range(50):
            x_est = sample_curve_x(t)
            if x_est == progress:
                break
            if x_est < progress:
                lower = t
            else:
                upper = t
            derivative = sample_curve_derivative_x(t)
            candidate = t - (x_est - progress) / derivative if derivative > 0 else -1.0
            if not lower < candidate < upper:
                candidate = (lower + upper) / 2
            if abs(candidate - t) <= 1e-12:
                t = candidate
                break
            t = candidate
        return sample_curve_y(t)

    return functools.wraps(bezier_easing)(functools.lru_cache(maxsize=8192)(bezier_easing))

out_back(progress_ratio)

Ease out using a back function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value that may exceed 1.

Source code in terminaltexteffects/utils/easing.py
def out_back(progress_ratio: float) -> float:
    """Ease out using a back function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value that may exceed 1.

    """
    c1 = 1.70158
    c3 = c1 + 1
    return 1 + c3 * (progress_ratio - 1) ** 3 + c1 * (progress_ratio - 1) ** 2

out_bounce(progress_ratio)

Ease out using a bounce function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def out_bounce(progress_ratio: float) -> float:
    """Ease out using a bounce function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    n1 = 7.5625
    d1 = 2.75
    if progress_ratio < 1 / d1:
        return n1 * progress_ratio**2
    if progress_ratio < 2 / d1:
        return n1 * (progress_ratio - 1.5 / d1) ** 2 + 0.75
    if progress_ratio < 2.5 / d1:
        return n1 * (progress_ratio - 2.25 / d1) ** 2 + 0.9375
    return n1 * (progress_ratio - 2.625 / d1) ** 2 + 0.984375

out_circ(progress_ratio)

Ease out using a circular function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def out_circ(progress_ratio: float) -> float:
    """Ease out using a circular function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    return math.sqrt(1 - (progress_ratio - 1) ** 2)

out_cubic(progress_ratio)

Ease out using a cubic function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def out_cubic(progress_ratio: float) -> float:
    """Ease out using a cubic function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    return 1 - (1 - progress_ratio) ** 3

out_elastic(progress_ratio)

Ease out using an elastic function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value that may exceed 1.

Source code in terminaltexteffects/utils/easing.py
def out_elastic(progress_ratio: float) -> float:
    """Ease out using an elastic function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value that may exceed 1.

    """
    c4 = (2 * math.pi) / 3
    if progress_ratio == 0:
        return 0
    if progress_ratio == 1:
        return 1
    return 2 ** (-10 * progress_ratio) * math.sin((progress_ratio * 10 - 0.75) * c4) + 1

out_expo(progress_ratio)

Ease out using an exponential function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def out_expo(progress_ratio: float) -> float:
    """Ease out using an exponential function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    if progress_ratio == 1:
        return 1
    return 1 - 2 ** (-10 * progress_ratio)

out_quad(progress_ratio)

Ease out using a quadratic function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def out_quad(progress_ratio: float) -> float:
    """Ease out using a quadratic function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    return 1 - (1 - progress_ratio) * (1 - progress_ratio)

out_quart(progress_ratio)

Ease out using a quartic function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def out_quart(progress_ratio: float) -> float:
    """Ease out using a quartic function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    return 1 - (1 - progress_ratio) ** 4

out_quint(progress_ratio)

Ease out using a quintic function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def out_quint(progress_ratio: float) -> float:
    """Ease out using a quintic function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    return 1 - (1 - progress_ratio) ** 5

out_sine(progress_ratio)

Ease out using a sine function.

Parameters:

Name Type Description Default
progress_ratio float

Progress ratio from 0 to 1.

required

Returns:

Name Type Description
float float

Eased value for the given progress ratio.

Source code in terminaltexteffects/utils/easing.py
def out_sine(progress_ratio: float) -> float:
    """Ease out using a sine function.

    Args:
        progress_ratio (float): Progress ratio from 0 to 1.

    Returns:
        float: Eased value for the given progress ratio.

    """
    return math.sin((progress_ratio * math.pi) / 2)