Animpy
Animpy is a Python library for building lightweight terminal-based animations, visual effects, mini-games, and interactive apps. It gives you a simple API for placing text objects on a scene, moving them, coloring them, applying physics, reacting to input, playing sound, and even following a moving target with a camera-style offset.
It is built for developers who want to create animated terminal experiences without needing a full game engine or a graphical UI framework.
What Animpy can do
- Create and animate text objects in the terminal
- Draw shapes such as rectangles, circles, polygons, lines, hearts, triangles, ellipses, squares, and donuts
- Create particle bursts, trails, and explosions
- Apply built-in visual effects such as shaking, gravity, decay, fading, interpolation, pulsing, and typing effects
- Build scenes with layered objects, background colors, offsets, and camera-style views
- Add physics behaviors such as gravity, friction, bounce, angular motion, and force pushes
- Handle keyboard and mouse input for interactive demos and games
- Play audio clips with basic playback controls
- Format terminal output with color, panels, centering helpers, and ANSI utilities
Installation
pip install animpy
Animpy depends on the following packages:
pygame-cekeyboardmouserich
Quick start
import animpy
scene = animpy.Scene()
text = animpy.Text("Hello!", 10, 5, r=255, g=100, b=50)
scene.add(text)
scene.render()
A more animated example:
import animpy
import time
scene = animpy.Scene()
text = animpy.Text("Hello", 0, 0, r=0, g=255, b=255)
scene.add(text)
for _ in range(20):
text.moveX(1)
scene.render()
time.sleep(0.05)
Core concepts
1. Scenes
A Scene is the container that holds objects to be rendered to the terminal. It manages the draw order, background color, offsets, and optional camera logic.
scene = animpy.Scene()
scene.set_bg_rgb(0, 0, 30)
2. Renderer
Renderer is the compositing engine used by Scene.render(). It supports text,
particles, nested groups, z-order, scene offsets, cameras, and RGB backgrounds.
It can also render into a string or inspect a deterministic Frame, which is
useful for tests, recordings, and custom terminal integrations.
from io import StringIO
renderer = animpy.Renderer(size=(80, 24))
frame = renderer.render_frame(scene)
plain_text = frame.plain_text()
output = StringIO()
animpy.Renderer(stream=output, size=(80, 24)).render(scene)
3. Text objects
Text is the basic object that can be placed on the screen and moved around. Text can also be created from a list of frames, which allows simple frame-based animation.
text = animpy.Text("Hello", 5, 3)
text.moveX(4)
text.moveY(2)
text.set_color(255, 0, 0)
3. Effects and motion
EffectText adds common visual effects and movement helpers on top of Text.
fx = animpy.EffectText("Bounce", 10, 2)
fx.shaking_text(intensity=2)
fx.gravity_text(floor=10, gravity=0.5)
4. Groups
A Group lets you manage many objects as one unit. Position and color changes can be applied to the entire group.
group = animpy.Group()
group.add(animpy.Text("A", 1, 1), animpy.Text("B", 4, 1))
group.position(2, 0)
5. Camera
A Camera provides a simple view transform for a scene. You can point it at a target object or keep a custom offset.
scene = animpy.Scene()
player = animpy.Text("P", 10, 5)
scene.add(player)
camera = animpy.Camera((0, 0), None)
camera.follow(player)
scene.camera = camera
scene.render()
Feature reference
Text
Text is the base class for terminal objects.
text = animpy.Text("Hello", 2, 2, r=255, g=255, b=255)
Common methods:
change_frame()— advance to the next frame if the text was created from a listset_frame(index)— switch to a specific frameset_text(text)— replace the text content or frame listset_position(x, y)— directly set the positionmove_to(x, y)— alias forset_positionset_color(r, g, b)— update the color using RGB valueschange_rgb_values(r, g, b)— same asset_colormoveX(dx)— move horizontallymoveY(dy)— move verticallyslide_to_pos(new_pos, speed=1)— move smoothly toward a coordinate-like objectcenterX()— center the text horizontallycenterY()— center the text verticallycollides_with(other)— test for bounding-box overlapon_collide_callback(other, callback)— run a callback when collision occurstype_out(text, speed=0.05, scene=None)— print text gradually, optionally rendering after each characterfall(velocity, floor)— move the object downward until a floor is reached
Useful properties:
width— number of characters in the current frameheight— number of lines in the current framecurrent_frame— current frame index
EffectText
EffectText extends Text with reusable animation effects and velocity support.
fx = animpy.EffectText("Shaky", 5, 5)
fx.shaking_text(intensity=2)
fx.gravity_text(floor=10, gravity=0.5)
fx.pulse_text(time=1.0, pulse_rate=0.5)
Available methods:
gravity_text(floor=20, gravity=0.5)— apply gravity and clamp to a floorshaking_text(intensity=1)— jitter position randomlydecaying_text(time, decay_rate=0.1)— shorten the text over timefade_out_text(time, fade_rate=0.1)— fade the object toward blacklerp_text(target_x, target_y, t)— interpolate smoothly to a target positionpulse_text(time, pulse_rate=0.5)— change brightness over timeset_velocity(vx, vy)— set motion in X/Yreset_velocity()— clear velocity valuesapply_force(fx, fy)— add to the current velocityfade_in_text(time, fade_rate=0.1)— fade in over time
Shapes
Shapes return multi-line strings that can be used as text content.
rect = animpy.Shapes.rectangle(8, 4, "#")
circle = animpy.Shapes.circle(4, "*")
line = animpy.Shapes.line(0, 0, 10, 4, "-")
heart = animpy.Shapes.heart(5, "♥")
triangle = animpy.Shapes.triangle(0, 0, 6, 0, 3, 5, "^")
ellipse = animpy.Shapes.ellipse(6, 3, 4, 2, "o")
square = animpy.Shapes.square(5, "#")
donut = animpy.Shapes.donut(5, 2, "#")
Supported builders:
rectangle(width, height, char)circle(radius, char)polygon(points, char)line(x1, y1, x2, y2, char)triangle(x1, y1, x2, y2, x3, y3, char)ellipse(center_x, center_y, radius_x, radius_y, char)heart(size, char)square(size, char)donut(outer_radius, inner_radius, char)
Group
A Group collects multiple objects so they can be moved or recolored together.
group = animpy.Group()
text1 = animpy.Text("A", 0, 0)
text2 = animpy.Text("B", 2, 0)
group.add(text1, text2)
group.position(3, 1)
group.change_rgb_values(0, 255, 0)
Available methods:
add(*items)— add one or more objectsremove(*items)— remove one or more objectsclear()— remove all itemscontains(item)— check if an item exists in the groupfind_by_color(r, g, b)— return matching items by colorposition(newx, newy)— shift every item by an offsetchange_rgb_values(r, g, b)— recolor every itemchange_rgb_values_one(item, r, g, b)— recolor a single item
Coords, Keyframe, and KeyChains
These helpers support path-based movement.
from animpy import Coords, Keyframe, KeyChains
start = Coords(0, 0)
mid = Coords(10, 3)
end = Coords(15, 8)
path = KeyChains(Keyframe(start), Keyframe(mid), Keyframe(end))
path.follow_path(text, speed=1)
Coords(x, y)— simple coordinate containerdistance_to(other)— calculate Euclidean distanceoffset(dx, dy)— return a new offset coordinateKeyframe(pos)— stores a target positionset_pos(pos)— update keyframe positiondistance_to(other)— measure the distance to another keyframe or coordinateKeyChains(*keyframes)— store a list of keyframesappend(keyframe)— add a keyframeclear()— remove all keyframesreverse_path()— reverse the path orderis_complete— becomesTrueonce the path has no more keyframesfollow_path(obj, speed=1)— moves an object toward the current keyframe
Scene
A Scene handles rendering and object management.
scene = animpy.Scene()
scene.add(text1, text2)
scene.remove(text2)
scene.render()
Useful methods:
add(*items)— add objects to the sceneremove(*items)— remove objects from the scenerender()— draw the current frame to the terminalupdate(delta_time)— update particles and remove expired itemsset_bg_rgb(r, g, b)— set a background colorshake(intensity=1)— apply a random shaking offsetclear()— clear the terminal displayclear_items()— remove every object from the scenecount_items()— return the number of scene itemsfind_items_at(x, y)— return the objects sitting at a coordinate
Scene properties:
dt— elapsed time since the last frameoffset_x,offset_y— manual rendering offsetscamera— optionalCameraattached to the scene
Camera
The Camera helper lets a scene render from a different view.
camera = animpy.Camera((0, 0), None)
camera.follow(player)
scene.camera = camera
scene.render()
Available methods:
move(new_position)— set the camera position directlylook_at(new_target)— point the camera at a new targetfollow(target_object)— track an object by referenceget_view_matrix()— return the current view position as a tuple
PhysicsScene
PhysicsScene adds simple physical motion helpers to a scene.
scene = animpy.PhysicsScene()
ball = animpy.EffectText("O", 2, 2)
scene.add(ball)
scene.apply_gravity(ball)
scene.apply_friction(ball, friction=0.1)
scene.bounce(ball, bounce_factor=0.7)
scene.apply_physics(ball)
scene.angular_motion(ball, angle=45, speed=2.0)
scene.push(ball, force_x=0.5, force_y=-0.2)
Available methods:
apply_gravity(obj)— move an object downward toward the floorapply_friction(obj, friction=0.1)— reduce velocity over timebounce(obj, bounce_factor=0.5)— reverse vertical motion when hitting the floorapply_physics(obj)— combine gravity, friction, and bounceangular_motion(obj, angle, speed)— set an angled velocitypush(obj, force_x, force_y)— add force in X/Y directions
InteractiveScene
InteractiveScene makes it possible to react to keyboard and mouse input.
scene = animpy.InteractiveScene()
player = animpy.Text("P", 10, 10)
scene.add(player)
while True:
if scene.key_pressed("w"):
player.moveY(-1)
if scene.key_pressed("s"):
player.moveY(1)
if scene.key_pressed("esc"):
break
scene.render()
Available methods:
key_pressed(key)— check whether a key is held downkey_released(key)— check whether a key has been releasedon_key_press_callback(key, callback)— run a callback when a key is pressedon_key_release_callback(key, callback)— run a callback when a key is releasedmouse_pressed(button="left")— check whether a mouse button is held downmouse_position()— return the current mouse positionon_mouse_press_callback(button, callback)— run a callback when a mouse button is pressedmouse_release(button, callback)— run a callback when a button is releasedmouse_release_callback(button, callback)— alias for the release callback helperquick_exit(key="esc")— exit immediately when a key is pressedquick_exit_callback(key, callback)— run a callback when a key is pressedlimit_to_bounds(obj)— keep an object inside the terminal arealimit_group_to_bounds(group)— keep every object in a group inside the terminal area
Particles
Particles are useful for trails, explosions, smoke, sparks, and other visual effects.
particle = animpy.Particle("*", 10, 5, r=255, g=150, b=50, lifetime=2.0)
particle.burst(scene, count=20, speed=1.5)
Available methods:
update(delta_time)— advance the particle in timeemit(scene)— add a single particle to the sceneburst(scene, count=10, speed=1.0)— create many particles with random velocitieschange_rgb_values(r, g, b)— change the particle colorset_velocity(vx, vy)— set particle velocityapply_force(fx, fy)— add velocity changesset_color(r, g, b)— alias for setting colorreset(x, y, lifetime=None)— reset particle position and ageis_alive()— check whether the particle is still aliveupdate_all(delta_time)— update all particle children created by the parentis_dead()— check whether all tracked particles are dead
There are also built-in particle presets:
Presets.create_explosion(x, y, count=20, speed=1.0, lifetime=1.0)Presets.create_smoke(x, y, count=10, speed=0.5, lifetime=2.0)Presets.create_firework(x, y, count=30, speed=2.0, lifetime=1.5)
Audio
Audio support is built on pygame and can be used to add music or effects.
audio = animpy.Audio()
audio.load("bg", "music.mp3")
audio.play("bg", loop=-1)
audio.set_volume("bg", 0.5)
Available methods:
load(name, file_path)— load an audio fileplay(name, loop=0)— start playbackstop(name)— stop a specific soundstop_all()— stop everythingis_playing(name=None)— check whether a sound is activeset_volume(name, volume)— update a volume levelpause(name)— pause playbackresume(name)— resume playbackfade_out(name, duration)— fade the sound outfade_in(name, duration, loop=0)— fade in and optionally loopplay_for_time(name, duration)— play for a set length and stop automatically
Terminal utilities
Animpy also includes helpers for terminal output.
animpy.hide_cursor()
animpy.show_cursor()
animpy.clear_screen()
animpy.print_centered("Centered")
animpy.print_with_color("Colored", r=255, g=0, b=0)
animpy.print_panel("Hello", title="Info")
Available helpers:
lerp(start, end, t)— linear interpolationhide_cursor()/show_cursor()clear_screen()print_centered(text)print_with_color(text, r, g, b)print_panel(text, title="Panel", style="bold white on blue")- ANSI color constants such as
animpy.ANSI["red"]andanimpy.ANSI["bg_blue"] - terminal size helpers such as
get_terminal_size(),set_terminal_size(columns, lines),reset_terminal_size(),get_terminal_width(), andget_terminal_height()
Example projects
The repository contains examples for:
- basic movement and interpolation
- renderer compositing with camera, groups, particles, z-order, and backgrounds
- loading screens
- visual effects
- physics demos
- player controls
- collision and tag-style games
- audio playback
You can browse the examples folder in the project repository for complete scripts.
Version history
v2.6.0
Added a new Camera class for scene view transformation and object tracking. All existing particle and utility methods now work seamlessly with camera-enabled scenes.
New Camera class:
Camera(position, target=None)— Constructor that creates a camera at a specific position with an optional targetposition— tuple of (x, y) representing the camera's current locationtarget— optional object or position to point the camera at; defaults toNone
Camera methods:
move(new_position)— Directly set the camera position to a new tuple (x, y)look_at(new_target)— Point the camera at a new target position or objectfollow(target_object)— Continuously track a Text or object withxandypropertiesget_view_matrix()— Return the current camera view as a tuple (x, y)
Scene camera support:
Scene.camera— New optional property to attach a Camera instance- Assign a Camera to enable camera-based rendering offsets
- When set, all objects render relative to the camera's viewpoint
- Works with all scene objects: Text, Particles, Shapes, Groups
Particle class methods (now camera-compatible):
update(delta_time)— Advance particle age and positionemit(scene)— Add a single particle instance to the sceneburst(scene, count=10, speed=1.0)— Create multiple particles with random velocities in all directionschange_rgb_values(r, g, b)— Update particle colorset_velocity(vx, vy)— Set particle velocity directlyapply_force(fx, fy)— Add velocity changes via forceset_color(r, g, b)— Alias for changing colorreset(x, y, lifetime=None)— Reset particle position and ageis_alive()— Check if particle is still activeupdate_all(delta_time)— Update all child particles created by burstis_dead()— Check if all tracked particles have expired
Particle Presets class (now camera-compatible):
Presets.create_explosion(x, y, count=20, speed=1.0, lifetime=1.0)— Create an explosion effectPresets.create_smoke(x, y, count=10, speed=0.5, lifetime=2.0)— Create a smoke effectPresets.create_firework(x, y, count=30, speed=2.0, lifetime=1.5)— Create a firework effect
Utility functions (now camera-compatible):
lerp(start, end, t)— Linear interpolation between start and end valueshide_cursor()— Hide the terminal cursorshow_cursor()— Show the terminal cursorclear_screen()— Clear terminal displayprint_panel(text, title="Panel", style="bold white on blue")— Print text in a styled panelprint_centered(text)— Print text centered in the terminalprint_with_color(text, r, g, b)— Print colored text using RGB valuesget_terminal_size()— Return current terminal dimensionsset_terminal_size(columns, lines)— Set terminal sizereset_terminal_size()— Reset terminal size to defaultsget_terminal_width()— Get terminal column countget_terminal_height()— Get terminal line countANSI— Dictionary of ANSI color and style codes for advanced terminal formatting
v2.5.0
- Added coordinate helpers such as
Coords.distance_to()andCoords.offset() - Added
KeyframeandKeyChainshelpers for path-based motion - Added
Audio.pause(),Audio.resume(),Audio.fade_in(), andAudio.fade_out() - Added
Group.clear(),Group.contains(), andGroup.find_by_color() - Added particle helpers such as
set_velocity(),apply_force(),set_color(),reset(), andis_alive() - Added scene helpers such as
clear_items(),count_items(), andfind_items_at() - Added shape builders
square()anddonut() - Added text convenience setters such as
set_frame(),set_text(),set_position(),move_to(), andset_color()
v2.1.0
- Added
Shapes.line(),Shapes.ellipse(),Shapes.heart(), andShapes.triangle() - Added
clear_screen(),print_centered(), andprint_with_color()
v2.0.0
- Added
PhysicsScenefor gravity, friction, bounce, and force-driven motion - Added
EffectTextanimation helpers such asfade_out_text(),lerp_text(), andpulse_text() - Added interactive scene helpers such as
limit_to_bounds(),quick_exit(), andlimit_group_to_bounds()
Support
If you enjoy Animpy, please consider starring the project on GitHub:
Release files for animpy 3.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| animpy-3.5.0.tar.gz | 23.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| animpy-3.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 42.0 kB
Release files / animpy-3.5.0.tar.gz
| Download URL | animpy-3.5.0.tar.gz |
|---|---|
| Size | 23.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
095a5aa5193b63b17cded762565b1b4bdf67425258e357d082e7174d6020f0d4
|
|
BLAKE2b-256 checksum How to use checksums |
2c6cd46a23499b685ff4c128f34f1563c22373e1b26411b9ddb566e64b6754ea
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Release files / animpy-3.5.0-py3-none-any.whl
| Download URL | animpy-3.5.0-py3-none-any.whl |
|---|---|
| Size | 18.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
64a8f053a623cb453d459304a8b7756ce27d55d6ef205967311c5d900f9b36fe
|
|
BLAKE2b-256 checksum How to use checksums |
7ad13a503a3ceeb4774a32d6654a1085c12c5ad4fc5b5be36d295ce9c5a9d7f0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|