Name: Towards AI Legal Name: Towards AI, Inc. Description: Towards AI is the world's leading artificial intelligence (AI) and technology publication. Read by thought-leaders and decision-makers around the world. Phone Number: +1-650-246-9381 Email: pub@towardsai.net
228 Park Avenue South New York, NY 10003 United States
Website: Publisher: https://towardsai.net/#publisher Diversity Policy: https://towardsai.net/about Ethics Policy: https://towardsai.net/about Masthead: https://towardsai.net/about
Name: Towards AI Legal Name: Towards AI, Inc. Description: Towards AI is the world's leading artificial intelligence (AI) and technology publication. Founders: Roberto Iriondo, , Job Title: Co-founder and Advisor Works for: Towards AI, Inc. Follow Roberto: X, LinkedIn, GitHub, Google Scholar, Towards AI Profile, Medium, ML@CMU, FreeCodeCamp, Crunchbase, Bloomberg, Roberto Iriondo, Generative AI Lab, Generative AI Lab VeloxTrend Ultrarix Capital Partners Denis Piffaretti, Job Title: Co-founder Works for: Towards AI, Inc. Louie Peters, Job Title: Co-founder Works for: Towards AI, Inc. Louis-François Bouchard, Job Title: Co-founder Works for: Towards AI, Inc. Cover:
Towards AI Cover
Logo:
Towards AI Logo
Areas Served: Worldwide Alternate Name: Towards AI, Inc. Alternate Name: Towards AI Co. Alternate Name: towards ai Alternate Name: towardsai Alternate Name: towards.ai Alternate Name: tai Alternate Name: toward ai Alternate Name: toward.ai Alternate Name: Towards AI, Inc. Alternate Name: towardsai.net Alternate Name: pub.towardsai.net
5 stars – based on 497 reviews

Frequently Used, Contextual References

TODO: Remember to copy unique IDs whenever it needs used. i.e., URL: 304b2e42315e

Resources

Free: 6-day Agentic AI Engineering Email Guide.
Learnings from Towards AI's hands-on work with real clients.
Building Physics Simulations with Pymunk and Pygame — Part III
Latest   Machine Learning

Building Physics Simulations with Pymunk and Pygame — Part III

Last Updated on July 23, 2026 by Editorial Team

Author(s): Sakshi Bhatia

Originally published on Towards AI.

Building Physics Simulations with Pymunk and Pygame — Part III

Building Physics Simulations with Pymunk and Pygame — Part III
http://www.pymunk.org/

Damped Spring

It is a damped spring. (Without damping, the spring would oscillate forever once compressed or stretched.)

from pymunk.constraints import RotarySpring
spring = DampedSpring(
a,
b,
anchor_a,
anchor_b,
rest_length,
stiffness,
damping
)

Other than the parameters inherited from the base constraint class, it has the following extra parameters:

  1. anchor_a ((float, float)): It is where on body A the spring attaches relative to body A’s center.
  2. anchor_b ((float, float)): It is where on body B the spring attaches relative to body B’s center.
  3. rest_length (float): the distance the spring wants to be
  4. stiffness (float): spring constant, k
  5. damping (float)
  6. static spring_force: This is Pymunk’s default for spring restoration force, which is essentially Hooke’s law: force = (rest_length — dist) * stiffness
  7. force_func: This is Pymunk’s force callback function that is called each step and used to calculate spring force, excluding damping. It defaults to DampedSpring.springforce(), but can be overridden. For example, if we want to construct a spring that becomes stiff after stretching too far:
def custom_force(spring, dist):

stretch = dist - spring.rest_length

if stretch < 20:
return stretch * spring.stiffness

return stretch * spring.stiffness * 5


spring.force_func = custom_force
Hooke’s law

Damped Rotary Spring

Damped Rotary Spring is the rotational equivalent of Damped Spring. A damped spring resists linear displacement. A damped rotary spring resists angular displacement.

Representation of a damped rotary spring (courtesy: ChatGPT)

It behaves like a torsion spring (e.g., door hinge spring, car suspension stabilizer).

from pymunk.constraints import DampedRotarySpring
spring = DampedRotarySpring(
a,
b,
rest_angle,
stiffness,
damping
)

Its parameters are similar to a rotary spring, with angular displacement replacing linear displacement, and torque replacing force.

rest_angle (radians): It is the desired relative angle between the two bodies. The constraint will try to restore to this angle after the applied external force is removed.

Gear Joint

A gear joint enforces meshing of two connected bodies where the bodies act like gears. When one body rotates, the other body connected to it by a gear joint rotates in proportion, determined by a fixed ratio.

GearJoint keeps the angular velocity ratio of a pair of bodies constant.

GearJoint(bodyA, bodyB, phase, ratio)
  1. phase (float): This is the initial angle offset between the two bodies. If body A is at angle 0 and body B at angle pi, the phase between these two bodies is pi.
  2. ratio (float):

The gear joint tries to maintain

Differentiating both sides with respect to time gives,

from pymunk.constraints import GearJoint
gear = GearJoint(
wheel1,
wheel2,
phase=0,
ratio=-1
)

For the above constraint, if wheel 1 turns 10 degrees, wheel 2 will turn -10 degrees.

Simple Motor

SimpleMotor keeps the relative angular velocity constant.

It should not be confused with GearJoint, which keeps the ratio of angular velocity constant, while SimpleMotor keeps the relative (difference) of angular velocity constant.

SimpleMotor vs GearJoint

Rate is the desired relative angular velocity.

from pymunk.constraints import SimpleMotor
motor = SimpleMotor(a, b, rate=5)

It is a good idea to cap the max_force:

motor.max_force = 1000

Let’s say the wheel hits a wall. Without an upper limit on force, the motor keeps increasing the force to maintain the specified rate, potentially generating unrealistically large torque, hence making the simulation unstable.

Pin Joint

A Pin Joint is a rigid, massless rod (fixed length) connecting two points on two bodies.

from pymunk.constraints import PinJoint 
joint = pymunk.PinJoint(
body_a,
body_b,
)

It has additional parameters from the base constraint class for anchor points and distance. Anchor points are coordinates local to each body.

  1. anchor_a ((float, float)): Coordinates for attachment of pin joint in A’s local coordinates. Defaults to (0,0), i.e., center of mass of A.
  2. anchor_b ((float, float)): Coordinates for attachment of pin joint in B’s local coordinates. Defaults to (0,0), i.e., center of mass of B.
  3. distance (float): The distance can be specified explicitly, or Pymunk calculates this based on the distance between the anchor points in the space when the joint is created. For a Pin Joint, the simulator tries to keep this distance fixed.
joint = PinJoint(
body_a,
body_b,
anchor_a=(20, 0),
anchor_b=(-10, 5),
)

joint.distance = 120

A pin joint only fixes the distance between the two bodies. The bodies are free to rotate.

Slide Joint

A Slide joint is a generalization of a Pin joint. Instead of enforcing one exact distance, it allows the distance to vary within a specified range.

Become a Medium member

For example,

from pymunk.constraints import SlideJoint

joint = SlideJoint(
body_a,
body_b,
anchor_a=(0, 0),
anchor_b=(0, 0),
min=50,
max=150,
)

The anchors are allowed to be anywhere between 50 and 150 units apart. As long as the distance between the anchors is within this range, the joint does nothing. If the distance exceedsmax, it acts like a taut rope and pulls the bodies together. If the distance falls belowmin, it acts like a rigid spacer and pushes the bodies apart.

Pivot Joint

It connects two bodies so that one point on body A and one point on body B are always at the same world position. The bodies are then free to rotate around that shared point. Example: a door hinge, a wheel axle. A pivot joint fixes both the x and y coordinates of the anchor points.

Before the joint,

The anchor points are separate before a pivot joint

After the joint,

Both objects have their anchor points on the pivot, and can rotate independently around it.

Example: a door hinge

The hinge keeps one edge of the door attached to the wall. Door is free to rotate.

We can create a pivot joint in Pymunk in two ways:

  • Method 1: Specify pivot’s world coordinates
from pymunk.constraints import PivotJoint
joint = PivotJoint(body_a, body_b, (300, 200))

Here, (300,200) is a point in the world. Pymunk computes the corresponding local anchor points automatically.

  • Method 2: Specify local anchors

These anchors are in local body coordinates. Ensure that the anchors overlap for a stable simulation.

body_a.position = (90, 50) # so local (10, 0) is at (100, 50)
body_b.position = (120, 50) # so local (-20, 0) is at (100, 50)

joint = PivotJoint(
body_a,
body_b,
(10, 0),
(-20, 0),
)

When the simulation starts, Pymunk moves the bodies (through constraint forces) until those two anchor points coincide. If the initial mismatch is small, this is usually fine. If it’s large, you may see a noticeable snap or violent movement as the solver corrects the error.

Groove Joint

A groove joint is a pivot joint with one DOF added.

Degrees of freedom: A pivot joint removes two translational degrees of freedom between the anchor points, fixing them together at one location. A groove joint removes only the motion perpendicular to the groove, while allowing motion along the groove. The attached body can also rotate freely unless another constraint restricts it.

A groove joint is useful for mechanisms where a part must slide along a predefined path, such as:

  • Sliding drawers
  • Piston rods in a straight slot

Imagine a bolt sliding inside a straight slot:

Body A (slot)

groove_a ---------------------- groove_b
| |
+------------------------------+

● <-- anchor on Body B
^
can slide anywhere
along the groove

anchor_b is attached to the groove. It cannot leave the groove, but it can move freely along its length.

GrooveJoint class has the following parameters:

  • groove_a ((float, float)): One endpoint of the groove on body A
  • groove_b ((float, float)): Other endpoint of the groove on body A
  • anchor_b ((float, float)): coordinates of point on body B that is constrained

Like almost every Pymunk constraint, groove_a, groove_b, and anchor_b are all expressed in their own body’s local coordinates.

For example, anchor_b = (0,0) means the center of body B slides in the groove, and anchor_b = (20,0) means a point 20 pixels to the right of body B’s center slides in the groove.

Unlike some other Pymunk objects, GrooveJoint does not have default values for its geometric parameters. A common convention is:

groove_a = (-L/2, 0)
groove_b = ( L/2, 0)
anchor_b = (0, 0)

Example,

from pymunk.constraints import GrooveJoint
joint = GrooveJoint(
body_a,
body_b,
(-50, 0), # groove starts here
(50, 0), # groove ends here
(0, 0) # body B's center slides in the groove
)

Ratchet Joint

Imagine tightening a bolt with a socket wrench.

  • You rotate the wrench clockwise and every few degrees you hear a click.
  • You can keep tightening. If you try to rotate backward, the wrench locks (depending on the ratchet direction).
  • It allows relative rotation only in discrete angular steps.

Constructor:

from pymunk.constraints import RatchetJoint
RatchetJoint(a, b, phase, ratchet)
  • ratchet (float): angular spacing between clicks (in radians). If ratchet = math.pi/6 (=30°), the joint locks every 0°, 30°, 60°, 90°, and so on, relative to the phase.
  • phase(float): It is the angular offset from the beginning.

Example,

joint = pymunk.constraints.RatchetJoint(
body1,
body2,
phase=15,
ratchet=math.pi/4
)

The joint locks every 15°, 60°, 105°, and so on.

Rotary Limit Joint

It restricts the relative angle between two bodies to stay within a specified range; the bodies can rotate freely within the allowed range, but cannot rotate beyond the minimum or maximum angle.

Constructor:

from pymunk.constraints import RotaryLimitJoint
RotaryLimitJoint(a, b, min, max)
  • min (float): minimum allowed relative angle (radians)
  • max (float): maximum allowed relative angle (radians)

These were all the essential details we need to know about constraints in Pymunk. In the next story, we will start building small systems. Until then, goodbye!

Join thousands of data leaders on the AI newsletter. Join over 80,000 subscribers and keep up to date with the latest developments in AI. From research to projects and ideas. If you are building an AI startup, an AI-related product, or a service, we invite you to consider becoming a sponsor.

Published via Towards AI


Towards AI Academy

We Build Enterprise-Grade AI. We'll Teach You to Master It Too.

15 engineers. 100,000+ students. Towards AI Academy teaches what actually survives production.

Start free — no commitment:

6-Day Agentic AI Engineering Email Guide — one practical lesson per day

Agents Architecture Cheatsheet — 3 years of architecture decisions in 6 pages

Our courses:

AI Engineering Certification — 90+ lessons from project selection to deployed product. The most comprehensive practical LLM course out there.

Agent Engineering Course — Hands on with production agent architectures, memory, routing, and eval frameworks — built from real enterprise engagements.

AI for Work — Understand, evaluate, and apply AI for complex work tasks.

Note: Article content contains the views of the contributing authors and not Towards AI.