Last Updated or created 2025-06-15
I use OBS sometimes to explain things, I thought it would be nice to have a moving avatar thingy in my screencast which moves when I talk.
The image is a transparent PNG, and I’m moving it up and down using the microphone and a python script.



CODE:
import pygame
import pyaudio
import numpy as np
import sys
# Settings
PNG_PATH = "your_image.png" # Replace with your transparent PNG path
CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
MOVEMENT_THRESHOLD = 1
GRAVITY = 3 # Tweak this, to get smoother "talk" bounch
JUMP_MULTIPLIER = 3 # Controls jump power Tweak this shit
MAX_JUMP_HEIGHT = 30 # Max bounce
START_OFFSET = -450 # How far below screen the PNG starts ( so my avator stays covered in bottom )
# Initialize PyAudio
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK)
# Initialize Pygame
pygame.init()
info = pygame.display.Info()
screen_width, screen_height = info.current_w, info.current_h
screen = pygame.display.set_mode((screen_width, screen_height), pygame.FULLSCREEN)
pygame.display.set_caption("Mic Bounce PNG")
# Load image
image = pygame.image.load(PNG_PATH).convert_alpha()
img_rect = image.get_rect()
img_rect.centerx = screen_width // 2
img_rect.top = screen_height + START_OFFSET # Start below the screen
velocity_y = 0
clock = pygame.time.Clock()
def get_loudness():
data = np.frombuffer(stream.read(CHUNK, exception_on_overflow=False), dtype=np.int16)
volume = np.linalg.norm(data)
return volume / CHUNK
# Bottom and top bounce limits
bottom_y = screen_height + START_OFFSET
top_y = screen_height + START_OFFSET - MAX_JUMP_HEIGHT
try:
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT or \
(event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
raise KeyboardInterrupt
loudness = get_loudness()
if loudness > MOVEMENT_THRESHOLD and img_rect.top >= bottom_y:
# Jump upward with capped power
velocity_y = -int(min((loudness - MOVEMENT_THRESHOLD) * JUMP_MULTIPLIER, MAX_JUMP_HEIGHT))
# Apply gravity
velocity_y += GRAVITY
img_rect.y += velocity_y
if img_rect.top >= bottom_y:
img_rect.top = bottom_y
velocity_y = 0
if img_rect.top <= top_y:
img_rect.top = top_y
velocity_y = 0
# Draw chromakey green background
screen.fill((0, 255, 0))
# Draw PNG
screen.blit(image, img_rect)
pygame.display.flip()
clock.tick(60)
except KeyboardInterrupt:
stream.stop_stream()
stream.close()
p.terminate()
pygame.quit()
sys.exit()
import pygame
import pyaudio
import numpy as np
import sys
# Settings
PNG_PATH = "your_image.png" # Replace with your transparent PNG path
CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
MOVEMENT_THRESHOLD = 1
GRAVITY = 3 # Tweak this, to get smoother "talk" bounch
JUMP_MULTIPLIER = 3 # Controls jump power Tweak this shit
MAX_JUMP_HEIGHT = 30 # Max bounce
START_OFFSET = -450 # How far below screen the PNG starts ( so my avator stays covered in bottom )
# Initialize PyAudio
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK)
# Initialize Pygame
pygame.init()
info = pygame.display.Info()
screen_width, screen_height = info.current_w, info.current_h
screen = pygame.display.set_mode((screen_width, screen_height), pygame.FULLSCREEN)
pygame.display.set_caption("Mic Bounce PNG")
# Load image
image = pygame.image.load(PNG_PATH).convert_alpha()
img_rect = image.get_rect()
img_rect.centerx = screen_width // 2
img_rect.top = screen_height + START_OFFSET # Start below the screen
velocity_y = 0
clock = pygame.time.Clock()
def get_loudness():
data = np.frombuffer(stream.read(CHUNK, exception_on_overflow=False), dtype=np.int16)
volume = np.linalg.norm(data)
return volume / CHUNK
# Bottom and top bounce limits
bottom_y = screen_height + START_OFFSET
top_y = screen_height + START_OFFSET - MAX_JUMP_HEIGHT
try:
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT or \
(event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
raise KeyboardInterrupt
loudness = get_loudness()
if loudness > MOVEMENT_THRESHOLD and img_rect.top >= bottom_y:
# Jump upward with capped power
velocity_y = -int(min((loudness - MOVEMENT_THRESHOLD) * JUMP_MULTIPLIER, MAX_JUMP_HEIGHT))
# Apply gravity
velocity_y += GRAVITY
img_rect.y += velocity_y
if img_rect.top >= bottom_y:
img_rect.top = bottom_y
velocity_y = 0
if img_rect.top <= top_y:
img_rect.top = top_y
velocity_y = 0
# Draw chromakey green background
screen.fill((0, 255, 0))
# Draw PNG
screen.blit(image, img_rect)
pygame.display.flip()
clock.tick(60)
except KeyboardInterrupt:
stream.stop_stream()
stream.close()
p.terminate()
pygame.quit()
sys.exit()
import pygame import pyaudio import numpy as np import sys # Settings PNG_PATH = "your_image.png" # Replace with your transparent PNG path CHUNK = 1024 FORMAT = pyaudio.paInt16 CHANNELS = 1 RATE = 44100 MOVEMENT_THRESHOLD = 1 GRAVITY = 3 # Tweak this, to get smoother "talk" bounch JUMP_MULTIPLIER = 3 # Controls jump power Tweak this shit MAX_JUMP_HEIGHT = 30 # Max bounce START_OFFSET = -450 # How far below screen the PNG starts ( so my avator stays covered in bottom ) # Initialize PyAudio p = pyaudio.PyAudio() stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK) # Initialize Pygame pygame.init() info = pygame.display.Info() screen_width, screen_height = info.current_w, info.current_h screen = pygame.display.set_mode((screen_width, screen_height), pygame.FULLSCREEN) pygame.display.set_caption("Mic Bounce PNG") # Load image image = pygame.image.load(PNG_PATH).convert_alpha() img_rect = image.get_rect() img_rect.centerx = screen_width // 2 img_rect.top = screen_height + START_OFFSET # Start below the screen velocity_y = 0 clock = pygame.time.Clock() def get_loudness(): data = np.frombuffer(stream.read(CHUNK, exception_on_overflow=False), dtype=np.int16) volume = np.linalg.norm(data) return volume / CHUNK # Bottom and top bounce limits bottom_y = screen_height + START_OFFSET top_y = screen_height + START_OFFSET - MAX_JUMP_HEIGHT try: while True: for event in pygame.event.get(): if event.type == pygame.QUIT or \ (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE): raise KeyboardInterrupt loudness = get_loudness() if loudness > MOVEMENT_THRESHOLD and img_rect.top >= bottom_y: # Jump upward with capped power velocity_y = -int(min((loudness - MOVEMENT_THRESHOLD) * JUMP_MULTIPLIER, MAX_JUMP_HEIGHT)) # Apply gravity velocity_y += GRAVITY img_rect.y += velocity_y if img_rect.top >= bottom_y: img_rect.top = bottom_y velocity_y = 0 if img_rect.top <= top_y: img_rect.top = top_y velocity_y = 0 # Draw chromakey green background screen.fill((0, 255, 0)) # Draw PNG screen.blit(image, img_rect) pygame.display.flip() clock.tick(60) except KeyboardInterrupt: stream.stop_stream() stream.close() p.terminate() pygame.quit() sys.exit()