Teaching a 350M-parameter language model to simulate 2D rigid body physics β from text alone.
30 scenario types Β· 900K training scenes Β· 180M frames Β· ~582 GB of physics data
Sub-pixel positional accuracy (<0.2%) on zero-shot held-out scenarios
| π― 30 diverse scenarios | Billiards, towers, pendulums, angry birds, orbit, hourglass and more |
| π Text-only input | No vision encoder, no GNN β pure next-token prediction |
| π Curriculum learning | 5 difficulty stages: 2-obj ballistics β 50-obj chaos |
| π¬ Zero-shot generalization | 6 held-out scenario types never seen during training |
| π Runs in the browser | WebGPU/WASM via transformers.js β no server needed |
| β‘ 350M params | Fine-tuned LiquidAI/LFM2-350M with LoRA |
All 30 Scenarios β click to collapse
| Billiards | Bowling | Explosion | Head-on | Projectile |
|---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
| Bridge | Dominos | Jenga | Pyramid | Tower |
|---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
| Funnel | Marble Run | Plinko | Ramp Roll | Ski Jump |
|---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
| Chain | Newton's Cradle | Pendulum | Seesaw | Wrecking Ball |
|---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
| Angry Birds | Basketball | Breakout | Pinball | Pong |
|---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
| Avalanche | Conveyor | Hourglass | Orbit | Wind |
|---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
AlexWortega/physics-scenarios-packed Β· AlexWortega/physics-scenarios-raw
| Split | Scenes | Scenario Types | Purpose |
|---|---|---|---|
| Train | 900,000 | 24 (seen) | Supervised fine-tuning |
| Val | 100,020 | 30 (all) | In-dist + zero-shot generalization |
Six scenario types are held out from training entirely:
pongΒ·bowlingΒ·ramp_rollΒ·angry_birdsΒ·hourglassΒ·newtons_cradle
Each scene = 1 JSONL header + 200 frame lines. The model receives frames 1..N and must predict frame N+1 in plain text:
Scene: Billiards: cue ball strikes a triangle of 21 balls.
Gravity: (0.0, 0.0)
Timestep: 0.01667
Type: billiards Difficulty: 3
Frame 1: All objects at rest.
obj_0: pos=(500.0000, 300.0000), vel=(800.0000, 0.0000)
obj_1: pos=(522.8631, 286.8000), vel=(0.0000, 0.0000)
...
Frame 2: Objects in motion.
obj_0: pos=(513.3340, 300.0000), vel=(800.0000, 0.0000)
...
Predict next frame:
| Format | Link | Size | Use case |
|---|---|---|---|
| Merged weights | AlexWortega/lfm2-scenarios | ~700 MB | Python / inference |
| ONNX q4 | AlexWortega/lfm2-scenarios-ONNX | 458 MB | Browser (WebGPU/WASM) |
| GGUF Q4_K_M | AlexWortega/lfm2-scenarios-GGUF | 216 MB | llama.cpp / local |
| Base model | LiquidAI/LFM2-350M | β | Reference |
| Component | Details |
|---|---|
| Base | LiquidAI/LFM2-350M (Liquid Foundation Model) |
| Fine-tuning | LoRA r=32 Ξ±=64 via Unsloth |
| Context | 8 192 tokens |
| Precision | bfloat16 |
| Optimizer | AdamW 8-bit, lr=2e-4 |
| Batch | 4 Γ grad-accum 8 = effective 32 |
Stage 0 difficulty 1 2β5 obj 50 000 examples β loss 0.562 β
Stage 1 difficulty 2 5β15 obj 50 000 examples β loss 0.609 β
Stage 2 difficulty 3 10β30 obj 50 000 examples β loss 0.622 β
Stage 3 difficulty 4 20β40 obj 50 000 examples β (training)
Stage 4 difficulty 5 30β50+ obj 50 000 examples β (pending)
- GPU β NVIDIA RTX A6000 (48 GB VRAM)
- Training time β ~7β14 h per curriculum stage
- Data generation β 22-core CPU, Pymunk, ~29 min for 900K scenes
- Logging β Weights & Biases
Evaluated on 6 held-out scenario types the model never saw during training.
GROUND TRUTH obj_0: pos=(339.8344, 143.2513) vel=(-531.0383, -319.9974)
PREDICTION obj_0: pos=(339.7855, 142.0493) vel=(-531.0383, -319.9974)
Ξx = 0.05 Ξy = 1.20 velocity: exact β
< 0.2% positional error on an 800 Γ 600 canvas.
GROUND TRUTH obj_3: pos=(600.8966, 25.1189) vel=( 0.0000, -179.8500)
PREDICTION obj_3: pos=(600.8966, 25.7189) vel=( 0.0000, -179.8500)
Ξx = 0.00 Ξy = 0.60 velocity: exact β
GROUND TRUTH obj_10: pos=(160.2332, 71.8437) vel=(264.8960, 0.0000)
PREDICTION obj_10: pos=(160.2332, 71.8594) vel=(264.8960, 0.0000)
Ξx = 0.00 Ξy = 0.02 velocity: exact β
- Velocities recovered near-perfectly β model learns the linear position-velocity relationship
- Sub-pixel positional errors even on multi-body collisions in unseen scenarios
- Generalizes compositionally β applies learned primitives (gravity, impulse, constraints) to new scenario types
Run the model entirely in your browser β no Python, no GPU, no server:
cd browser_demo
npm install
npm run dev # β http://localhost:5173The demo fetches lfm2-scenarios-ONNX (458 MB, q4) from HF CDN, runs autoregressive rollout via WebGPU (or WASM fallback), and renders each predicted frame on a Konva canvas with a live token-stream sidebar.
Source: browser_demo/
pip install llama-cpp-python matplotlib pillow
cd inference
# Single-step repro (mirrors browser prompt format exactly)
python repro.py billiards 5
# Multi-step rollout with drift/parse diagnostics
python multistep.py orbit 50
# Benchmark 1-frame vs 4-frame prompt speed (ONNX)
python bench.py
# Render all 30 demo scenarios Γ 200 frames β animated GIFs
python make_gifs.py --frames 200 --out ./gifsSource: inference/
# Generate dataset
python scripts/generate_scenarios_dataset.py \
--output-dir data_scenarios/train \
--num-scenes-per-type 37500 \
--num-workers 22
# Train with curriculum
python scripts/train_finetune.py \
--data-dir data_scenarios/train \
--output-dir checkpoints/lfm2 \
--curriculum-stages 5 \
--epochs-per-stage 1 \
--batch-size 4 --grad-accum 8 --lr 2e-4
# Evaluate on held-out scenarios
python scripts/run_evaluation.py \
--model finetune \
--checkpoint checkpoints/lfm2/stage4/adapter \
--output-dir evaluation_resultsPhysicsLLMEngine/
βββ src/
β βββ physics/
β β βββ scenario_generator.py # All 30 scenario generators
β β βββ scenario_registry.py # @register_scenario decorator
β β βββ simulation.py # Pymunk wrapper
β β βββ objects.py # Body/shape factories
β βββ data/
β β βββ formats.py # JSONL text serialization
β β βββ exporter.py # Scene β file pipeline
β βββ training/
β β βββ curriculum.py # Difficulty-based curriculum scanner
β β βββ data_loader.py # Physics-aware data loading
β βββ evaluation/
β βββ rollout.py # Autoregressive multi-step evaluator
β βββ runner.py # Full evaluation pipeline CLI
βββ scripts/
β βββ train_finetune.py # LFM2 + LoRA entry point
β βββ train_scratch.py # GPT from-scratch baseline
β βββ generate_scenarios_dataset.py
β βββ run_evaluation.py
βββ browser_demo/ # π WebGPU browser demo
β βββ src/
β β βββ transformersEngine.ts # transformers.js ONNX inference
β β βββ streamClient.ts # Autoregressive rollout + fitPrompt
β β βββ App.tsx # React UI (canvas + token stream)
β β βββ promptFormat.ts # Text serialization (mirrors training)
β βββ backend/
β βββ server.py # FastAPI scenario server
β βββ examples/ # 30 bundled JSONL demo scenarios
βββ inference/ # π₯οΈ Local Python inference tools
β βββ repro.py # Single-step repro
β βββ multistep.py # Multi-step rollout diagnostics
β βββ bench.py # ONNX speed benchmark
β βββ make_gifs.py # Batch rollout β GIFs
β βββ patch_onnx_v2.py # Patch LoRA weights into ONNX graph
βββ assets/
βββ gallery.png # Scenario gallery overview
βββ gifs/ # 30 scenario demo GIFs
- Complete curriculum stages 3β4 (hard + extreme difficulty)
- Full 200-step autoregressive rollout evaluation on all 30 scenarios
- GPT-from-scratch baseline with muP scaling
- Energy & momentum conservation analysis
- q4f16 ONNX for 2Γ faster WebGPU inference
@software{physicslmengine2026,
title = {PhysicsLLMEngine: Learning Rigid Body Dynamics via Next-Token Prediction},
author = {Wortega, Alex},
year = {2026},
url = {https://github.com/AlexWortega/PhysicsLLMEngine}
}| π€ Merged model | AlexWortega/lfm2-scenarios |
| π€ ONNX (WebGPU) | AlexWortega/lfm2-scenarios-ONNX |
| π€ GGUF (llama.cpp) | AlexWortega/lfm2-scenarios-GGUF |
| π€ Dataset (packed) | AlexWortega/physics-scenarios-packed |
| π€ Dataset (raw) | AlexWortega/physics-scenarios-raw |
| π Browser demo | browser_demo/ |
| π₯οΈ Inference scripts | inference/ |
| β‘ Base model | LiquidAI/LFM2-350M |
| π οΈ Training framework | Unsloth |
MIT License Β· ICML 2026






























