Step 1: Scaffold
In this tutorial you rebuild the built-in Spray effect, one feature per step. Spray releases characters a few at a time from one point on the canvas and sends each one to its place in the text, fading it into a color gradient as it travels. This is the built-in effect running on the input you use throughout the tutorial:

By the last step, tte --input-file tutorial_input.txt myspray looks the same.
Your copy is called MySpray and runs as tte myspray. TTE rejects two effects with the same command name, so it
can't be called spray.
In this step you write the smallest effect that runs: it shows the input text for one frame and stops.
Engine chapters used: Architecture, Frame loop.
Create the input file
Every step runs on the same three lines of text. Save them as tutorial_input.txt in the directory you run tte
from:
Welcome to the TTE Effect Guide.
Every character in this text is an EffectCharacter.
This tutorial rebuilds the Spray effect, one step at a time.
Create the module
Create myspray.py in your community effects directory:
Then add this code to ${XDG_CONFIG_HOME:-$HOME/.config}/terminaltexteffects/effects/myspray.py. Each part is
explained below.
"""MySpray, tutorial step 1: the smallest effect that runs.
The effect shows the input text in place for one frame. Later steps build it up into a copy of the built-in
Spray effect.
"""
from __future__ import annotations
from dataclasses import dataclass
from terminaltexteffects.engine.base_config import BaseConfig
from terminaltexteffects.engine.base_effect import BaseEffect, BaseEffectIterator
from terminaltexteffects.utils import argutils
def get_effect_resources() -> tuple[str, type[BaseEffect], type[BaseConfig]]:
"""Return the command name, effect class, and configuration class TTE registers."""
return "myspray", MySpray, MySprayConfig
@dataclass
class MySprayConfig(BaseConfig):
"""Configuration for the MySpray effect. It has no options yet."""
parser_spec = argutils.ParserSpec(
name="myspray",
help="Sprays the characters from a single point.",
description="myspray | Sprays the characters from a single point.",
epilog="Example: tte myspray",
)
class MySprayIterator(BaseEffectIterator[MySprayConfig]):
"""Produce the frames of the MySpray effect."""
def __init__(self, effect: MySpray) -> None:
"""Set up every character before the first frame."""
super().__init__(effect)
self.shown = False
for character in self.terminal.get_characters():
self.terminal.set_character_visibility(character, is_visible=True)
def __next__(self) -> str:
"""Return one frame showing the text, then stop."""
if not self.shown:
self.shown = True
return self.frame
raise StopIteration
class MySpray(BaseEffect[MySprayConfig]):
"""Sprays the characters from a single point."""
@property
def _config_cls(self) -> type[MySprayConfig]:
return MySprayConfig
@property
def _iterator_cls(self) -> type[MySprayIterator]:
return MySprayIterator
What each part does
get_effect_resources()
TTE calls this function when it loads your module. It returns three things: the command name, the effect class, and
the configuration class. The command name must match parser_spec.name in the configuration.
MySprayConfig
The configuration is a dataclass that subclasses BaseConfig. It has no options yet, only parser_spec, which
describes the myspray subcommand: its one-line help in tte -h, and the description and epilog shown by
tte myspray -h. You add options in Step 6.
MySprayIterator
The iterator does the work. Its __init__ runs once, before the first frame:
super().__init__(effect)copies the configuration and buildsself.terminal, which holds one character for each symbol of the input except spaces.- The loop makes every character visible. Characters start hidden, so without this the frame would be blank.
__next__ returns self.frame, the rendered canvas, the first time it is called, then raises StopIteration to end
the effect. It never calls self.update(), because no character has anything to animate yet. The
characters stay inactive, but the terminal draws every visible character whether or not it is active, so the
text still appears. See Active and visible are separate.
MySpray
The effect class is the public face of your effect. Its two properties tell BaseEffect which configuration and
iterator classes to use. You rarely add anything else to it.
Run it
From the directory that holds tutorial_input.txt, run:
The text appears at once, with no animation, and the command exits:

Your effect is also listed in tte -h, and tte myspray -h shows its help:
usage: tte myspray [-h]
myspray | Sprays the characters from a single point.
options:
-h, --help show this help message and exit
Example: tte myspray
If tte myspray reports an invalid choice, TTE didn't load your module. Check that the file is directly inside the
effects directory and that get_effect_resources() is spelled correctly.
Next step
Step 2 holds the characters back and releases a few each frame, so the text builds up over time.