Skip to main content
bofa is built on the terminaltexteffects library and includes custom rendering logic for terminal animations.

Dependencies

Source: pyproject.toml:10-12
  • Python: Requires Python 3.10 or higher
  • terminaltexteffects: Version 0.14.2 or higher
  • Build System: Uses uv_build for package building
requires-python = ">=3.10"
dependencies = [
    "terminaltexteffects>=0.14.2",
]

Terminal Requirements

TTY Detection

Source: __init__.py:124-126 bofa checks if it’s running in an interactive terminal:
if not sys.stdout.isatty() or os.environ.get("TERM", "").lower() == "dumb":
    print(msg)
    return
Fallback Behavior: If no TTY is detected or TERM=dumb, bofa skips animations and prints plain text.

Terminal Size Detection

Source: __init__.py:309-314 bofa automatically detects terminal dimensions and calculates optimal text width:
columns = shutil.get_terminal_size(fallback=(80, 24)).columns
target = columns - 2  # Leave 2-column margin
if target <= 0:
    target = columns
return max(34, min(target, 120))
Width Constraints:
  • Minimum: 34 characters (ensures text fits)
  • Maximum: 120 characters (prevents excessive width)
  • Margin: 2 columns reserved for borders
  • Fallback: 80×24 if size cannot be detected

Unicode Encoding Detection

Source: __init__.py:49-57 bofa tests Unicode support before selecting characters:
def _unicode_ok() -> bool:
    encoding = sys.stdout.encoding
    if not encoding:
        return False
    try:
        "✦❖✺".encode(encoding)
    except UnicodeEncodeError:
        return False
    return True
Fallback: If Unicode encoding fails, ASCII-only characters are used for confetti and fireworks.

Rendering Configuration

Terminal Config

Source: __init__.py:100-108 All effects use a shared terminal configuration:
terminal_config = TerminalConfig._build_config()
terminal_config.frame_rate = 90
terminal_config.canvas_width = 0  # Auto-detect
terminal_config.canvas_height = 0  # Auto-detect
terminal_config.anchor_canvas = "c"  # Center
terminal_config.anchor_text = "c"  # Center
terminal_config.reuse_canvas = True
ParameterValuePurpose
frame_rate90 fpsSmooth animations without flicker
canvas_width0 (auto)Adapts to terminal width
canvas_height0 (auto)Adapts to terminal height
anchor_canvas"c"Centers canvas in terminal
anchor_text"c"Centers text on canvas
reuse_canvasTrueEfficient frame updates

Effect Playback

Source: __init__.py:111-115 Each effect is played using the same rendering loop:
def _play(effect: BaseEffect, rng: random.Random) -> None:
    random.seed(rng.randrange(2**32))
    with effect.terminal_output() as terminal:
        for frame in effect:
            terminal.print(frame)
Key Details:
  • Random seed is set per-effect for reproducible randomness within that effect
  • Terminal context manager handles setup and cleanup
  • Iterator yields frames at the configured frame rate

Character Rendering

Confetti Characters

Source: __init__.py:36-37 bofa uses different character sets based on Unicode support:
ASCII_CONFETTI_CHARS = "*+x~^@"  # 6 characters
UNICODE_CONFETTI_CHARS = "✦✧❖✺✹✷✸✶✱✲✳✴✵✼✽❇❈❉❊" + ASCII_CONFETTI_CHARS  # 22 characters
Usage: Borders and decorative elements (lines 60-62, 66-77, 84-96)

Firework Symbols

Source: __init__.py:303-306 Firework explosion particles adapt to terminal capabilities:
if unicode_ok:
    return ("✦", "✧", "❇", "❈", "✺", "*", "+", "x")  # 8 options
return ("o", "*", "+", "x")  # 4 options
One symbol is randomly chosen for each run (line 162-164).

Color System

Rainbow Gradient

Source: __init__.py:38-46 All effects use a consistent 7-color rainbow gradient:
RAINBOW_STOPS = (
    Color("#e81416"),  # Red
    Color("#ffa500"),  # Orange  
    Color("#faeb36"),  # Yellow
    Color("#79c314"),  # Green
    Color("#487de7"),  # Blue
    Color("#4b369d"),  # Indigo
    Color("#70369d"),  # Violet
)
Application:
  • Used as final_gradient_stops in all effects
  • VHSTape adds white endpoints for glitch effects (lines 254-256)
  • Gradient directions vary by effect (horizontal, radial)

Gradient Directions

From terminaltexteffects.utils.graphics.Gradient.Direction:
  • HORIZONTAL: Left to right color transition
  • RADIAL: Center-outward color transition (used for spotlight effects)

Moon Mode

Source: moon.py:1-62 The --moon flag triggers ASCII art rendering with different logic:

Character Aspect Ratio

Source: moon.py:9
CHAR_ASPECT = 2.0
Explanation: Terminal characters are typically twice as tall as they are wide. This ratio is used to render circular shapes correctly (lines 16, 28, 43).

Circle Rendering

Source: moon.py:13-32 The moon uses distance-based rendering:
for y in range(-R, R + 1):
    for x in range(-W, W + 1):
        dist = math.sqrt((x / CHAR_ASPECT) ** 2 + y ** 2)
        if abs(dist - R) < 0.5:
            row[cx] = "#"  # Moon border
Algorithm:
  1. Calculate radius R from terminal rows (max 10)
  2. Calculate width W accounting for character aspect ratio
  3. For each position, compute distance from center
  4. Place # characters where distance ≈ radius

Crater Generation

Source: moon.py:10, 33-53
CRATERS = ("()", "o", ".", "o", ".", "()")
Random craters are placed inside the circle:
  • Seed is fixed at 42 for consistent output (line 22)
  • One crater per 12 interior positions (line 48)
  • Center line (y=0) displays the payload text instead (lines 34-38)

Randomization

Effect Selection

Source: __init__.py:197-226 Intro effect chosen by random roll:
  • 0.00-0.34: ColorShift (34%)
  • 0.34-0.67: Spotlights (33%)
  • 0.67-1.00: Spray (33%)
Source: __init__.py:146-152 Interlude plays 75% of the time.

Random Number Generator

Source: __init__.py:133 A single random.Random() instance is created and passed to all functions, ensuring reproducible randomness within a run while allowing variety between runs.

Keyboard Interrupts

Source: __init__.py:153-155, 184-186 Keyboard interrupts (Ctrl+C) are handled gracefully:
try:
    _play_intro(...)
    _play_interlude(...)
except KeyboardInterrupt:
    print(msg)
    return
Behavior: If interrupted, bofa immediately prints the plain text message and exits cleanly.

Build docs developers (and LLMs) love