A fast, deterministic 2D rigid-body physics engine in C++20. SAT collision detection · XPBD constraint solver · joints with motors · YAML scenes · headless mode for simulation, testing and reinforcement learning.
Get started · Demos · API reference · Try it in your browser
- Physics you can read. One explicit pipeline, from broad phase to SAT contacts to a substepped XPBD solver, with solver and collision docs that derive the equations the code implements.
- Built for simulation, not just games. The headless build steps thousands of times per second with no window or GPU, for tests, batch rollouts and reinforcement learning.
- Deterministic and tested. Fixed timestep and solver order, adversarial stress scenes, and golden regression tests that keep speedups from changing the physics.
- Small and embeddable. About 8k lines of C++20 in one static library under BSD-3-Clause. Headless builds need only Eigen and yaml-cpp.
| Area | What you get |
|---|---|
| Shapes | Circles, capsules, edges, chains for terrain, and convex or rounded polygons |
| Collision | Dynamic AABB tree, SAT contacts with clipped manifolds, opt-in speculative CCD |
| Solver | Substepped XPBD with friction, restitution, warm starting, sleeping and a vectorised contact solve |
| Joints | Revolute and prismatic joints with PID motors, plus a mouse joint for click-and-drag |
| Queries & events | Ray casts, overlap and point queries, begin/end contact events and sensors |
| Scenes | YAML scenes with textures and joints, entity groups, collision filtering and one-call reset |
| Viewer & input | raylib viewer with an ImGui inspector; keyboard and mouse input that also works headless |
| Platforms | x86-64 and ARM64, tested with GCC, Clang, Apple Clang and MSVC |
git clone https://github.com/Bharath2/fx2d-physics-engine.git
cd fx2d-physics-engine
./scripts/build_headless.sh # needs only Eigen3, yaml-cpp and TBB
./build-headless/fx2d_tests # run the test suite
./build-headless/truck_headless # step a scene with no windowThen write a scene and step it:
# Scene.yml
scene:
size: [16, 9]
gravity: [0, -9.81]
entities:
floor:
pose: [8, 0.5, 0]
physics: { mass: 0, gravity_scale: 0 }
collision: { geometry: { rectangle: [16, 1] } }
ball:
pose: [8, 7, 0]
physics: { mass: 1.0, elasticity: 0.5 }
collision: { geometry: { circle: 0.25 } }#include "Fx2D/Scene.h"
#include "Fx2D/YamlUtils.h"
int main() {
auto scene = FxYAML::buildScene("Scene.yml");
auto ball = scene.get_entity("ball");
for (int i = 0; i < 600; ++i) {
scene.step(1.0 / 60.0); // fixed timestep, deterministic
std::cout << ball->pose << '\n'; // x, y, theta
}
}Want a window? Include Fx2D/Core.h instead and hand the scene to the renderer:
#include "Fx2D/Core.h"
int main() {
auto scene = FxYAML::buildScene("Scene.yml");
FxRylbRenderer renderer(scene, 60);
renderer.run();
}The installation guide covers the visual build (raylib, Dear ImGui, rlImGui) on Linux, macOS, Windows and MSYS2.
Every example is a plain C++ file plus a Scene.yml under examples/. Build them with -DFX2D_BUILD_EXAMPLES=ON and run from the repository root so asset paths resolve.
![]() Playground Mouse joint drag, spawn shapes, seesaw, chain ramp. Also runs in the browser |
![]() Angry boxes Mouse slingshot, trajectory preview, contact impulses, reset |
![]() Truck Wheels, suspension joints, textured bodies |
![]() Stacked boxes Resting stability, friction, textured shapes |
![]() Joint control Revolute and prismatic motors: position, velocity, effort modes |
![]() Chain terrain Polyline terrain, click-to-spawn, one-sided chain contacts |
![]() Bucket fill Hundreds of bodies piling into a container, sleeping, broad phase under load |
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DFX2D_BUILD_EXAMPLES=ON
cmake --build build -j
./build/example_playground # also: example_angry_boxes, example_truck, example_stacked_boxes,
# example_joint_control, example_chain_terrain, example_bucket_fillThe physics core has no dependency on the renderer. Build with -DFX2D_HEADLESS=ON (or ./scripts/build_headless.sh) and you get the same engine with no raylib, no Dear ImGui and no window, which is what CI runs.
FxScene scene = FxYAML::buildScene("examples/angry_boxes/Scene.yml");
// Drive the scene the way a player would, without a cursor.
scene.input().set_mouse_position(world_xy, screen_xy);
scene.input().set_mouse_button(FxMouseButton::Left, true);
scene.step(dt);
// Observe: contacts, impulses, ray casts, overlaps.
for (const auto& c : scene.contacts()) { /* c.entity1, c.normal, c.jn_accumulated */ }
FxRayHit hit;
if (scene.raycast(origin, direction, max_distance, hit)) { /* hit.entity, hit.point */ }
scene.reset(); // back to the authored scene, groups includedBecause stepping is deterministic and single-threaded, many independent scenes can run in parallel across processes or threads for batched rollouts. See the headless guide, input, queries and contacts and sensors.
CMake 3.16+, Eigen 3.3+ and yaml-cpp. The windowed viewer also needs raylib 4.5+, Dear ImGui and rlImGui; the installation guide covers each platform.
The documentation site has the guides, demos, browser playground and API reference. To browse the same pages on GitHub, including the roadmap, start from the docs index.
Contributions are welcome. See CONTRIBUTING for the workflow, the lint gate (./scripts/lint.sh) and how to run the test suite.
If Fx2D is useful to you, a ⭐ on the repository helps others find it.
@software{fx2d,
author = {Irigireddy, Bharath Chandra},
title = {Fx2D: A 2D rigid-body physics engine in C++20},
year = {2025},
url = {https://github.com/Bharath2/fx2d-physics-engine}
}




