Skip to content

Instantly share code, notes, and snippets.

@aarondfrancis
Created February 7, 2026 13:28
Show Gist options
  • Select an option

  • Save aarondfrancis/94ad3c16f582620b09378e0b10deb9ba to your computer and use it in GitHub Desktop.

Select an option

Save aarondfrancis/94ad3c16f582620b09378e0b10deb9ba to your computer and use it in GitHub Desktop.
Terminal typing animation with AI tool cycling and particle explosion effect
#!/bin/bash
# Hide the real cursor
tput civis
# Restore cursor on exit
trap 'tput cnorm' EXIT
# ANSI color codes
BOLD="\033[1m"
DARK_PURPLE="\033[38;5;54m"
TEAL="\033[38;5;30m"
RESET="\033[0m"
CURSOR_BG="\033[48;5;55m"
# Indentation
INDENT=" "
# Column where typed text starts (8 indent + 10 "faster.dev" + 1 space + 4 "main" + 1 space + 1 "›" + 1 space = 26, so column 27)
TEXT_START_COL=27
# Current typed text
TYPED=""
# Function to print the prompt (call once)
print_prompt() {
printf "${INDENT}${BOLD}${DARK_PURPLE}faster.dev${RESET} ${TEAL}main ${RESET}"
}
# Function to render just the typed text (doesn't touch prompt)
render() {
local show_cursor="${1:-true}"
# Move to the column where text starts, clear to end of line
printf "\033[${TEXT_START_COL}G\033[K"
# Print typed text
printf "%s" "$TYPED"
# Show cursor block if requested
if [ "$show_cursor" = "true" ]; then
printf "${CURSOR_BG} ${RESET}"
fi
}
# Function to render with first character highlighted (for cycling loops)
render_highlighted() {
# Move to the column where text starts, clear to end of line
printf "\033[${TEXT_START_COL}G\033[K"
# Print first character with block cursor and white text
if [ ${#TYPED} -gt 0 ]; then
printf "${CURSOR_BG}\033[97m%s${RESET}" "${TYPED:0:1}"
# Print rest of the text
if [ ${#TYPED} -gt 1 ]; then
printf "%s" "${TYPED:1}"
fi
fi
}
# Function to blink cursor
blink_cursor() {
local duration=$1
local i=0
local iterations=$(echo "$duration * 2" | bc | cut -d. -f1)
while [ $i -lt $iterations ]; do
if [ $((i % 2)) -eq 0 ]; then
render true
else
render false
printf " "
fi
sleep 0.5
i=$((i + 1))
done
render true
}
# Function to type out text
type_text() {
local text="$1"
local delay="${2:-0.1}"
local i=0
while [ $i -lt ${#text} ]; do
TYPED="${TYPED}${text:$i:1}"
render true
sleep "$delay"
i=$((i + 1))
done
}
# Function to backspace text
backspace_text() {
local count=$1
local delay="${2:-0.05}"
local i=0
while [ $i -lt $count ]; do
local len=${#TYPED}
if [ $len -gt 0 ]; then
TYPED="${TYPED:0:$((len - 1))}"
render true
sleep "$delay"
fi
i=$((i + 1))
done
}
# Clear screen and start
clear
# Add vertical padding (4 lines)
printf "\n\n\n\n"
# Print the prompt once (never redrawn)
print_prompt
# Initial render with just cursor
TYPED=""
render true
# Initial pause for camera setup
blink_cursor 2
# Type "code ."
type_text "code ." 0.1
# Blink cursor for 1.5 seconds
blink_cursor 1.5
# Backspace "code ." (6 characters)
backspace_text 6 0.08
# Hold on blank line
sleep 0.25
# Type "claude"
type_text "claude" 0.1
# Hold with blinking cursor (same as after "code .")
blink_cursor 1.5
# Backspace "claude" faster (6 characters)
backspace_text 6 0.04
# Type "gemini"
type_text "gemini" 0.1
sleep 0.2
# Backspace "gemini" (6 characters)
backspace_text 6 0.04
# Type "codex"
type_text "codex" 0.1
sleep 0.2
# Backspace "codex" (5 characters)
backspace_text 5 0.04
# Type "amp"
type_text "amp" 0.1
sleep 0.2
# Backspace "amp" (3 characters) so cursor is at start for loops
backspace_text 3 0.04
# AI tools to cycle through
AI_TOOLS=("claude" "gemini" "codex" "amp")
# Total iterations
TOTAL_WORDS=37
START_DELAY=0.1
END_DELAY=0.04
# Gradually speed up from START_DELAY to END_DELAY
for i in $(seq 0 $((TOTAL_WORDS - 1))); do
tool_idx=$((i % 4))
TYPED="${AI_TOOLS[$tool_idx]}"
render_highlighted
# Calculate delay: linear interpolation from START_DELAY to END_DELAY
delay=$(echo "scale=3; $START_DELAY - ($START_DELAY - $END_DELAY) * $i / ($TOTAL_WORDS - 1)" | bc)
sleep "$delay"
done
# Particle explosion animation
explosion() {
local frames=30
local chars='!@#$%^&*()_+-=[]{}|;:,.<>?/~`\|/-_=+*#@$%&'
# Get terminal dimensions
local cols=$(tput cols)
local rows=$(tput lines)
# Center point (where the text was)
local center_x=$((TEXT_START_COL + 3))
local center_y=3
for frame in $(seq 1 $frames); do
# Number of particles increases then decreases (much denser)
local num_particles
if [ $frame -lt 15 ]; then
num_particles=$((frame * 80))
else
num_particles=$(( (frames - frame) * 60 ))
fi
# Draw particles
for p in $(seq 1 $num_particles); do
# Random position expanding from center
local spread=$((frame * 3))
local x=$((center_x + RANDOM % (spread * 2 + 1) - spread))
local y=$((center_y + RANDOM % (spread + 1) - spread / 2))
# Keep in bounds
[ $x -lt 1 ] && x=1
[ $x -gt $cols ] && x=$cols
[ $y -lt 1 ] && y=1
[ $y -gt $rows ] && y=$rows
# Random character
local char_idx=$((RANDOM % ${#chars}))
local char="${chars:$char_idx:1}"
# Random color (dark blues, dark purples, dark reds, light greys)
local colors=(17 18 19 24 25 53 54 55 89 90 52 88 124 160 250 251 252 253 254 255)
local color=${colors[$((RANDOM % ${#colors[@]}))]}
# Draw particle
printf "\033[%d;%dH\033[38;5;%dm%s\033[0m" "$y" "$x" "$color" "$char"
done
sleep 0.03
done
# Final flash - fill screen with characters then fade
for fade in 1 2 3; do
printf "\033[H\033[J"
local density=$((400 / fade))
for i in $(seq 1 $density); do
local x=$((RANDOM % cols + 1))
local y=$((RANDOM % rows + 1))
local char_idx=$((RANDOM % ${#chars}))
local char="${chars:$char_idx:1}"
printf "\033[%d;%dH\033[38;5;%dm%s" "$y" "$x" "$((232 + 23 / fade))" "$char"
done
sleep 0.05
done
# Clear to black
printf "\033[H\033[J"
printf "\033[0m"
}
# Run explosion
explosion
# Final pause
sleep 1
echo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment