Pymunk is a easy-to-use pythonic 2D physics library built on Munk2D
npx @tessl/cli install tessl/pypi-pymunk@7.1.0Pymunk is a easy-to-use pythonic 2D physics library that provides a complete interface to the Chipmunk2D physics engine. It enables developers to create realistic physics simulations with rigid bodies, collisions, constraints, and forces for games, simulations, and educational physics demonstrations.
Package Name: pymunk
Version: 7.1.0
License: MIT
Homepage: https://www.pymunk.org
Repository: https://github.com/viblo/pymunk
pip install pymunkimport pymunk
# Core simulation classes
from pymunk import Space, Body, Shape
# Shape types
from pymunk import Circle, Poly, Segment
# Utility classes
from pymunk import Vec2d, BB, Transform, ShapeFilter
# Physics information
from pymunk import Arbiter, ContactPoint, ContactPointSet
from pymunk import PointQueryInfo, SegmentQueryInfo, ShapeQueryInfo
# Debug visualization
from pymunk import SpaceDebugDrawOptions
# Constraint/joint classes (from pymunk.constraints)
from pymunk.constraints import (
PinJoint, SlideJoint, PivotJoint, GrooveJoint,
DampedSpring, DampedRotarySpring, RotaryLimitJoint,
RatchetJoint, GearJoint, SimpleMotor
)
# Utility modules
import pymunk.pygame_util # Pygame integration
import pymunk.pyglet_util # Pyglet integration
import pymunk.matplotlib_util # Matplotlib integration
import pymunk.autogeometry # Automatic geometry generation
import pymunk.util # Geometry and math utilities
import pymunk.batch # High-performance batch operationsimport pymunk
# Create the physics space
space = pymunk.Space()
space.gravity = (0, -981) # Earth gravity (pixels/s²)
# Create a static ground body
ground_body = pymunk.Body(body_type=pymunk.Body.STATIC)
ground_shape = pymunk.Segment(ground_body, (0, 0), (800, 0), 5)
ground_shape.friction = 0.7
space.add(ground_body, ground_shape)
# Create a dynamic falling ball
ball_mass = 10
ball_radius = 20
ball_moment = pymunk.moment_for_circle(ball_mass, 0, ball_radius)
ball_body = pymunk.Body(ball_mass, ball_moment)
ball_body.position = 400, 600
ball_shape = pymunk.Circle(ball_body, ball_radius)
ball_shape.friction = 0.7
ball_shape.elasticity = 0.8 # Bounciness
space.add(ball_body, ball_shape)
# Run the simulation
dt = 1/60.0 # 60 FPS
for i in range(600): # 10 seconds
space.step(dt)
print(f"Ball position: {ball_body.position}")import pymunk
space = pymunk.Space()
space.gravity = (0, -981)
# Create two bodies connected by a pin joint
body1 = pymunk.Body(10, pymunk.moment_for_circle(10, 0, 20))
body1.position = 100, 300
shape1 = pymunk.Circle(body1, 20)
body2 = pymunk.Body(10, pymunk.moment_for_circle(10, 0, 20))
body2.position = 200, 300
shape2 = pymunk.Circle(body2, 20)
# Connect with a pin joint (rigid connection)
pin = pymunk.PinJoint(body1, body2, (0, 0), (0, 0))
# Add a spring between them
spring = pymunk.DampedSpring(
body1, body2, (0, 0), (0, 0),
rest_length=50, stiffness=1000, damping=50
)
space.add(body1, shape1, body2, shape2, pin, spring)import pymunk
def collision_handler(arbiter, space, data):
"""Handle collision between two shapes"""
print(f"Collision! Impulse: {arbiter.total_impulse}")
return True # Process the collision normally
space = pymunk.Space()
# Set collision handler for specific collision types
space.add_collision_handler(1, 2).begin = collision_handler
# Create shapes with collision types
ball1 = pymunk.Circle(body1, 20)
ball1.collision_type = 1
ball2 = pymunk.Circle(body2, 20)
ball2.collision_type = 2
space.add(body1, ball1, body2, ball2)Pymunk follows a clean object-oriented design with the following core components:
The Space class represents the physics simulation world that contains and manages all physical objects. It handles collision detection, constraint solving, and time stepping.
Key Responsibilities:
The Body class represents rigid objects with mass, position, velocity, and rotation. Bodies can be:
Shape classes (Circle, Poly, Segment) define the collision boundaries and material properties of bodies:
Constraint classes define relationships between bodies:
Supporting classes for common operations:
Complete control over physics simulation parameters and object lifecycles.
# Space configuration
space.iterations = 10 # Solver iterations per step
space.gravity = (0, -981) # Global gravity vector
space.damping = 0.999 # Global velocity damping
space.sleep_time_threshold = 0.5 # Time before objects sleep
space.collision_slop = 0.1 # Collision overlap tolerance
# Object management
space.add(body, shape, constraint) # Add objects
space.remove(shape) # Remove objects
space.step(dt) # Advance simulation
# Spatial queries
hits = space.point_query((100, 200), 0, pymunk.ShapeFilter())
hit = space.point_query_nearest((100, 200), 0, pymunk.ShapeFilter())
hits = space.segment_query((0, 0), (100, 100), 0, pymunk.ShapeFilter())→ See Physics World Documentation
Flexible rigid body dynamics with multiple shape types and material properties.
# Body types and creation
body = pymunk.Body(mass=10, moment=pymunk.moment_for_circle(10, 0, 25))
kinematic_body = pymunk.Body(body_type=pymunk.Body.KINEMATIC)
static_body = space.static_body # Built-in static body
# Shape creation with materials
circle = pymunk.Circle(body, radius=25, offset=(0, 10))
circle.friction = 0.7
circle.elasticity = 0.95
circle.density = 1.0
poly = pymunk.Poly(body, vertices=[(0,0), (50,0), (50,50), (0,50)])
segment = pymunk.Segment(body, (0, 0), (100, 0), radius=5)
# Force and impulse application
body.apply_force_at_world_point((1000, 0), (100, 100))
body.apply_impulse_at_local_point((500, 0), (0, 0))→ See Bodies and Shapes Documentation
Rich set of constraint types for connecting bodies with realistic joint behavior.
# Joint constraints
pin = pymunk.PinJoint(body_a, body_b, anchor_a=(0, 0), anchor_b=(0, 0))
pivot = pymunk.PivotJoint(body_a, body_b, pivot_point=(100, 100))
slide = pymunk.SlideJoint(body_a, body_b, anchor_a, anchor_b, min_dist=10, max_dist=50)
groove = pymunk.GrooveJoint(body_a, body_b, groove_a=(0,0), groove_b=(100,0), anchor_b=(0,0))
# Spring constraints
spring = pymunk.DampedSpring(
body_a, body_b, anchor_a=(0, 0), anchor_b=(0, 0),
rest_length=100, stiffness=500, damping=20
)
rotary_spring = pymunk.DampedRotarySpring(
body_a, body_b, rest_angle=0, stiffness=1000, damping=50
)
# Motor constraints
motor = pymunk.SimpleMotor(body_a, body_b, rate=math.pi)
gear = pymunk.GearJoint(body_a, body_b, phase=0, ratio=2.0)→ See Constraints Documentation
Comprehensive 2D math utilities with intuitive vector operations and geometric calculations.
# Vector operations
v1 = pymunk.Vec2d(10, 20)
v2 = pymunk.Vec2d(5, 15)
result = v1 + v2 # Vector addition
length = abs(v1) # Vector magnitude
unit = v1.normalized() # Unit vector
rotated = v1.rotated(math.pi/2) # Rotate vector
# Bounding boxes
bb = pymunk.BB(left=0, bottom=0, right=100, top=100)
center = bb.center() # Get center point
area = bb.area() # Calculate area
expanded = bb.expand(pymunk.Vec2d(10, 10)) # Expand by vector
# Transformations
transform = pymunk.Transform.rotation(math.pi/4) # 45° rotation
translated = transform.translated(100, 50) # Add translation
point = transform @ pymunk.Vec2d(10, 10) # Apply to pointBuilt-in debug drawing support for popular graphics libraries with customizable rendering options.
# Pygame debug drawing
import pygame
import pymunk.pygame_util
screen = pygame.display.set_mode((800, 600))
draw_options = pymunk.pygame_util.DrawOptions(screen)
space.debug_draw(draw_options)
# Pyglet debug drawing
import pyglet
import pymunk.pyglet_util
draw_options = pymunk.pyglet_util.DrawOptions()
with draw_options:
space.debug_draw(draw_options)
# Matplotlib debug drawing
import matplotlib.pyplot as plt
import pymunk.matplotlib_util
fig, ax = plt.subplots()
draw_options = pymunk.matplotlib_util.DrawOptions(ax)
space.debug_draw(draw_options)
plt.show()
# Custom debug options
draw_options.shape_outline_color = pymunk.SpaceDebugColor(1, 0, 0, 1) # Red
draw_options.collision_point_color = pymunk.SpaceDebugColor(0, 1, 0, 1) # Green→ See Visualization Documentation
Advanced geometry utilities for automatic shape generation, convex decomposition, and marching squares.
import pymunk.autogeometry
import pymunk.util
# Automatic geometry processing
points = [(0,0), (100,0), (100,100), (50,150), (0,100)]
simplified = pymunk.autogeometry.simplify_curves(points, tolerance=5.0)
convex_hull = pymunk.autogeometry.to_convex_hull(points, tolerance=1.0)
convex_parts = pymunk.autogeometry.convex_decomposition(points, tolerance=1.0)
# Marching squares for bitmap to geometry conversion
def sample_func(point):
"""Sample function returning 1.0 for solid, 0.0 for empty"""
x, y = point
return 1.0 if (x-50)**2 + (y-50)**2 < 25**2 else 0.0
polylines = pymunk.autogeometry.march_soft(
pymunk.BB(0, 0, 100, 100), 20, 20, 0.5, sample_func
)
# Utility geometry functions
is_convex = pymunk.util.is_convex(points)
area = pymunk.util.calc_area(points)
centroid = pymunk.util.calc_center(points)
triangles = pymunk.util.triangulate(points)Pymunk provides convenience functions for calculating mass properties and areas of common shapes. These are essential for setting up bodies with proper physics properties.
def moment_for_circle(
mass: float,
inner_radius: float,
outer_radius: float,
offset: tuple[float, float] = (0, 0)
) -> float:
"""
Calculate the moment of inertia for a hollow circle.
A solid circle has an inner radius of 0.
Args:
mass: Mass of the shape
inner_radius: Inner radius (0 for solid circle)
outer_radius: Outer radius
offset: Offset from body center of gravity
Returns:
Moment of inertia value
Example:
# Solid circle
moment = pymunk.moment_for_circle(10, 0, 25)
# Hollow circle (ring)
moment = pymunk.moment_for_circle(10, 20, 25)
"""
def moment_for_segment(
mass: float,
a: tuple[float, float],
b: tuple[float, float],
radius: float
) -> float:
"""
Calculate the moment of inertia for a line segment.
The endpoints a and b are relative to the body.
Args:
mass: Mass of the shape
a: First endpoint
b: Second endpoint
radius: Thickness radius
Returns:
Moment of inertia value
"""
def moment_for_box(mass: float, size: tuple[float, float]) -> float:
"""
Calculate the moment of inertia for a solid box centered on the body.
Args:
mass: Mass of the shape
size: (width, height) of the box
Returns:
Moment of inertia value
Example:
moment = pymunk.moment_for_box(10, (50, 30))
"""
def moment_for_poly(
mass: float,
vertices: Sequence[tuple[float, float]],
offset: tuple[float, float] = (0, 0),
radius: float = 0
) -> float:
"""
Calculate the moment of inertia for a solid polygon shape.
Assumes the polygon center of gravity is at its centroid.
The offset is added to each vertex.
Args:
mass: Mass of the shape
vertices: List of vertices
offset: Offset added to each vertex
radius: Corner rounding radius
Returns:
Moment of inertia value
"""def area_for_circle(inner_radius: float, outer_radius: float) -> float:
"""
Calculate area of a hollow circle.
Args:
inner_radius: Inner radius (0 for solid circle)
outer_radius: Outer radius
Returns:
Area of the circle
"""
def area_for_segment(
a: tuple[float, float],
b: tuple[float, float],
radius: float
) -> float:
"""
Calculate area of a beveled segment.
Will always be zero if radius is zero.
Args:
a: First endpoint
b: Second endpoint
radius: Thickness radius
Returns:
Area of the segment
"""
def area_for_poly(
vertices: Sequence[tuple[float, float]],
radius: float = 0
) -> float:
"""
Calculate signed area of a polygon shape.
Returns a negative number for polygons with clockwise winding.
Args:
vertices: List of vertices
radius: Corner rounding radius
Returns:
Signed area of the polygon
"""def empty_callback(*args, **kwargs) -> None:
"""
A default empty callback function.
Can be used to reset a collision callback to its original empty
function. More efficient than defining your own empty function.
"""Pymunk provides version information for both the Python library and the underlying Chipmunk2D physics engine.
version: str
"""
The release version of this Pymunk installation.
Valid only if Pymunk was installed from a source or binary
distribution (i.e. not in a checked-out copy from git).
Example:
import pymunk
print(pymunk.version) # "7.1.0"
"""
chipmunk_version: str
"""
The Chipmunk2D version used with this Pymunk version.
This property shows the actual version when you import Pymunk.
The string format is: <cpVersionString>-<github_commit>
where cpVersionString is the version string set by Chipmunk2D
and the git commit hash corresponds to the specific chipmunk
source from github.com/viblo/Chipmunk2D included with Pymunk.
Example:
import pymunk
print(pymunk.chipmunk_version) # "7.0.3-ade7ed72849e60289eefb7a41e79ae6322fefaf3"
"""import pymunk
# Get version information
print(f"Pymunk version: {pymunk.version}")
print(f"Chipmunk version: {pymunk.chipmunk_version}")
# Version compatibility checking
def check_version_compatibility():
"""Check if current Pymunk version meets minimum requirements"""
required_version = "7.0.0"
current_version = pymunk.version
# Simple string comparison (works for semantic versioning)
if current_version >= required_version:
print(f"✓ Pymunk {current_version} meets requirement (>= {required_version})")
return True
else:
print(f"✗ Pymunk {current_version} does not meet requirement (>= {required_version})")
return False
check_version_compatibility()Pymunk uses standard Python exceptions and provides detailed error messages for common issues:
Space(threaded=True)Space.use_spatial_hash() for large numbers of objectspymunk.batch for large datasetsSpace.iterations for stability vs. performance trade-offsThis comprehensive physics library provides all the tools needed for realistic 2D physics simulations with an intuitive Python interface that abstracts the underlying C physics engine complexity while maintaining high performance and flexibility.