Created
February 7, 2026 13:28
-
-
Save aarondfrancis/94ad3c16f582620b09378e0b10deb9ba to your computer and use it in GitHub Desktop.
Terminal typing animation with AI tool cycling and particle explosion effect
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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