2D physics sandbox in Rust, in the spirit of Algodoo.
⚠️ Experimental — Marble is an independent, work-in-progress alternative to Algodoo, built from scratch in Rust. It is not affiliated with, endorsed by, or a replacement for Algodoo. Expect breaking changes, bugs, and missing features. Use at your own risk.
Marble is a real-time, interactive 2D rigid-body physics simulator. It aims to reproduce the feel of Algodoo: drag to create shapes, hinge them together, slice them with a virtual knife, save the scene to a text file, share it.
export-1783900612333_10mb.mp4
The project is in M0: a window opens and clears to dark blue via skia-safe. No physics yet.
See docs/research-2026-06-19.md for the
full stack research that backs every dependency version.
| Milestone | Status | Description |
|---|---|---|
| M0 | Done | Window + skia-safe GL clear |
| M1 | Done | Fixed timestep + rapier2d ball drop |
| M2 | Done | Tools (Box / Circle / Polygon / Erase) + hotkeys + zoom |
| M3 | Done | Save/Load (RON) + Joints (Hinge / Spring) |
| M4 | Done | Knife (polygon split) + Brush (palette cycle) + Thruster (force) |
| M5 | Done | Half-circle knife (ball → 2 half-discs with perpendicular kick) |
| M6 | Done | GUI color picker for Brush ([/] cycle, palette strip overlay) |
| M7 | Done | Rope tool (click vertices → chain of balls + spring joints) |
| M8 | Done | Soft Circle / Blob (drag → ring of 8 balls + center + 16 springs) |
| M11 | Done | Camera pan (right-click drag) |
| M14 | Done | Select + drag-to-move (kinematic during drag) + Delete key |
| M18 | Done | Multi-select (Shift+click toggle) + group drag + Delete all |
| M23 | Done | GUI Toolbar (left sidebar, click buttons to switch tool) |
| M15+ | Backlog | Fluid, soft-body mesh, click-on-swatch, rotate selected, ... |
- Rust 1.85 or newer (
rustup default stable-msvc) - Visual Studio 2022 Build Tools with the "Desktop development with C++" workload
(provides
cl.exe,link.exe, Windows SDK, MSVC v143) - Open the "x64 Native Tools Command Prompt for VS 2022" before building
(or run
vcvars64.batin your shell) curlonPATH(skia-safe downloads a prebuilt archive on first build)
The docs/setup-windows.md file will detail this further (TODO).
# from the project root
cargo run --releaseFirst build downloads the skia prebuilt binary (~3-8 MiB) and then compiles all crates — expect 3-6 minutes on a modern machine. Subsequent builds are incremental.
Run either:
cargo run— opens a 1024×768 window with 4 marbles dropping onto a green groundcargo run --example ball_drop— headless: simulates 1 ball dropping, prints positions to stdout (no window needed)
GUI Toolbar (M23): a left-side strip shows one button per tool with its hotkey letter. Click a button to switch tools — hotkeys still work as shortcuts.
B— Box: click + drag to create a static cuboidC— Circle: click + drag from center to edge, creates a dynamic ballP— Polygon: click vertices, right-click or Enter to finalize (convex hull)E— Erase: click on a marble to remove itH— Hinge: click 2 bodies to weld a revolute joint between themG— Spring: click 2 bodies to weld a soft (springy) jointK— Knife: click 2 points to slice. Convex polygons are split in two; circles are cut into 2 half-discs that fly apart (M5)T— Brush: click a body to apply the currently-selected color (use[/]to cycle through 8-swatch palette; strip is shown at top-left when brush is active)F— Thruster: click + hold a body to apply a continuous force from its center to the cursorR— Rope: click vertices to define a chain, Enter to spawn (first click is anchored, rest dangle)M— Soft Circle: drag from center to edge to spawn a wobbly blob (1 center + 8 ring balls + 16 springs)- Right-click + drag — pan the camera (world under cursor stays under cursor)
- Select tool (
S) — click a body to select (yellow ring), drag to move (kinematic during drag). Shift+click to toggle a body in/out of the multi-select. Drag any selected body → all move together.Delete/Backspaceremoves all selected bodies,Escdeselects. Space— pause / resumeEsc— cancel in-progress actionCtrl+S— save scene to./marble_scene.marbleCtrl+O— load scene from./marble_scene.marbleMouse wheel— zoom in/out
In window: close = exit.
.
├── Cargo.toml # pinned versions, features
├── rust-toolchain.toml # stable-msvc, 1.85
├── .cargo/config.toml # Windows build flags
├── docs/
│ ├── research-2026-06-19.md # full stack research (executive summary)
│ └── research/
│ └── raw-output.json # raw 24-agent findings + synthesis
├── examples/ # cargo run --example <name> (M1)
└── src/
├── main.rs # entry: env_logger + EventLoop
├── lib.rs # crate root, re-exports
├── app/ # AppState split into focused sandboxes (mod + new/default/selection/simulation/toggles/undo/scene_io)
├── loop_.rs # fixed-timestep accumulator (M1)
├── window/ # winit ApplicationHandler (mod.rs) + stateless input helpers (input.rs)
├── physics/ # rapier2d wrapper (M1), joints (M3)
├── scene/ # hecs World + Marble entity (M1)
├── render/ # skia-safe draw pipeline (M0) + camera (M2)
│ ├── scene/ # draw_scene split sandboxes (mod/objects/overlay/preview/palette/indicator/grid)
│ └── toolbar/ # left toolbar (mod.rs) + shape popup (popup.rs)
├── tools/ # one file per tool (M2: select/box/circle/polygon/erase, M3: hinge/spring, M4: knife/brush/thruster)
└── serialization/ # RON save/load (M3)
MIT OR Apache-2.0