v0.1 · Apache 2.0

A differentiable physics kernel for verifiable domain operations.

Lean put mathematics into a verifiable domain, rapidly accelerating the field by making discovery available to machines via machine-verifiable proofs. We make progress towards the domain of physics: the entire engine is a Lagrangian-graph implementation, allowing close fits to observational data. In our search for the most elegant solution, we created Lagra, where every solver is a program in a graph.

starting engine…
↗ open
avbd_stack_1000.lagra
starting engine…
↗ open
lagrangian_dam_break.lagra

Same engine binary, different active LagrangianNodes. Drag any block on the left; click play on the right to release the dam.

view .lagra source · avbd_stack_1000.lagra (block)
// AVBD Stack 1000 — one hundred 10-body columns for scaling benchmark.
scene avbd_stack_1000 {
    uses newtonian.classical_mechanics.avbd
    environment classical_gravity
    description "1000 unit rigid bodies: 100 columns of 10 stacked blocks for scaling analysis."
    wasm true
    floor { y: 0.5, k: 1400.0, damping: 2.0, friction: 0.5 }
    render "rigid_body"

    entity { type: avbd_block, position: [-13.5, 14.5, -13.5], mass: 1.0, size: 1.0 }
    entity { type: avbd_block, position: [-13.5, 13.0, -13.5], mass: 1.0, size: 1.0 }
    entity { type: avbd_block, position: [-13.5, 11.5, -13.5], mass: 1.0, size: 1.0 }
    // … 997 more avbd_block entities laid out in 100 columns of 10 …

    optimization { contact_broadphase: "sweep_prune_fast" }

    solver {
        backend:        "lagra_native",
        integrator:     "avbd",
        contact_model:  "augmented_lagrangian",
        contact_solver: "variational_block_descent",
        iterations:     10,
        warmstart:      true,
        dt:             0.001,
        steps_per_frame: 16
    }
    display { unit: "m" }
}
view .lagra source · lagrangian_dam_break.lagra (water)
// Dam break: left half raised, no wind, pure bond-driven collapse.
// Validates wavefront speed against lattice dispersion relation.
scene lagrangian_dam_break {
    uses newtonian.classical_mechanics.fluid_dynamics
    environment none
    description "Dam break: left half of water surface raised. Released under bond tension forces. Wavefront propagates rightward at c = a·sqrt(k/m)."
    wasm true
    render "fluid_water"
    floor { y: -0.500, k: 900.0, damping: 1.6, friction: 0.05 }

    entity { name: "water_00_00", type: water_surface_sample,
             position: [-2.800, 0.0, -2.800], mass: 1e6, size: 0.08 }
    entity { name: "water_00_01", type: water_surface_sample,
             position: [-2.520, 0.0, -2.800], mass: 1e6, size: 0.08 }
    // … 439 more water_surface_sample particles on a 21×21 lattice …

    bond harmonic [0, 1] { k: 22.0, r0: 0.280, damping: 0.024 }
    bond harmonic [1, 2] { k: 22.0, r0: 0.280, damping: 0.024 }
    // … ~1600 more harmonic bonds wiring nearest neighbours + diagonals …

    @expect fluid_surface_consistency
    @expect subluminal_velocity

    solver {
        backend:    "lagra_native",
        integrator: "symplectic_euler",
        dt:              0.005,
        steps_per_frame: 7
    }
    display { unit: "m" }
}

From .lagra source to live render

The entire source for the two-bead oscillator on the right. The compiler reads this file, the kernel walks the resulting graph, the solver advances state, the renderer reads pixels. Nothing else.

scene solver_walkthrough_oscillator {
    uses newtonian.classical_mechanics
    description "Two-bead harmonic oscillator."
    wasm true                  // landing-page bundle

    entity { type: ball_small,
        position: [-1.35, 0, 0],
        mass: 1.0, size: 0.35 }
    entity { type: ball_small,
        position: [ 1.35, 0, 0],
        mass: 1.0, size: 0.35 }

    bond harmonic [0, 1] {
        k: 2.0, r0: 2.0, damping: 0.04 }

    solver { dt: 0.002,
              steps_per_frame: 12 }
}
starting engine…
↗ open
solver_walkthrough_oscillator.lagra

Every token on the left maps to a typed object the engine evolves on the right.

Eight harmonic bonds, one Lagrangian

Nine particles, eight HarmonicBond nodes, one UniformAcceleration for gravity. The kernel doesn't know it's a "chain" — it just walks eight pairwise terms.

arrows show net force per bead · drag any bead · ▮▮ pauses · reset re-seeds

starting engine…
↗ open
pendulum_chain.lagra
view .lagra source · pendulum_chain.lagra
// Pendulum chain — 8 beads + 1 anchor, harmonic bonds, gravity.
entity bead   { mass: 1.0,   size: 0.4  }
entity anchor { mass: 1.0e8, size: 0.15 }   // huge mass → pinned

scene pendulum_chain {
    uses newtonian.classical_mechanics
    environment classical_gravity
    description "8-bead harmonic-bond chain swinging under gravity."
    wasm true

    // index 0 = anchor; indices 1..=8 = beads, each 0.55m below the previous.
    entity { type: anchor, position: [0.0,  3.5,  0.0] }
    entity { type: bead,   position: [0.0,  2.95, 0.0] }
    entity { type: bead,   position: [0.0,  2.40, 0.0] }
    // … 6 more beads stepping down by 0.55m each …

    bond harmonic [0, 1] { k: 120.0, r0: 0.55, damping: 0.10 }
    bond harmonic [1, 2] { k: 120.0, r0: 0.55, damping: 0.10 }
    // … 6 more bonds wiring consecutive beads …

    @expect momentum_conservation
    solver { dt: 0.003, steps_per_frame: 12 }
}

One kernel walks every .lagra node

SCENE · .lagra SOURCE LagrangianGraph · TYPED NODES scene oscillator { uses newtonian.classical_mechanics entity ball { pos:[-1,0,0], m:1.0 } entity ball { pos:[ 1,0,0], m:1.0 } bond harmonic [0,1] { k:2.0, r0:2.0 } environment classical_gravity @expect energy_conservation solver { dt:0.002 } } + runtime intervention (user / agent drag · not in .lagra) PRESET · activates KineticEnergy + Gravity + … vertex q₁ ∈ ℝ³ · m=1.0 · pos=(-1,0,0) vertex q₂ ∈ ℝ³ · m=1.0 · pos=( 1,0,0) HarmonicBond(q₁,q₂) · k=2.0 · r₀=2.0 · ∂V/∂q FieldGravity · acceleration_field=[0,-9.81,0] EnergyGate · |H(t) − H(0)|/|H(0)| · 1 of N Noether gates stepper · velocity_verlet · dt=0.002 (config) DragForce(q) · runtime · pointer / agent event ↓ action kernel walks every active node, sums ∂V/∂q ↓ F = − Σ ∂Vn / ∂q
ACTION KERNEL · ONE LOOP, EVERY STEP entity bond field intervention optional · user / agent action action kernel for node in active · sum ∂V/∂q F = − Σ ∂V/∂q integrate → q′, q̇′ new state loop · next step render → pixels optional VerifierGate → @expect optional · conservation check
preset entity bond field (in environment block) intervention (runtime · not in .lagra) verifier · @expect config / render

A master Lagrangian, verifiably consistent with physics at every step

Every major theory in physics is already a Lagrangian. The Standard Model, general relativity, classical mechanics, Navier–Stokes — each was accepted only after decades of consistency checks against everything else physics knows. Lagra speaks the language physics already speaks, so any scene composed in it inherits that consistency: the terms on the right have been validated by the field; only their composition is new.

It’s also cheap to step. Minimizing the action gives a trajectory directly: per step, the kernel walks the active nodes and sums local gradients — no global solve, no implicit per-step optimization. On average, one tick of the integrator is the cost of summing ∂V/∂q over active nodes.

And the loop is verifiable in both directions. Forward: a scene’s predictions must match experiment or reference data. Backward: conservation laws — energy, momentum, charge — fall out of the trajectory and can be derived from the engine’s output, or checked statistically frame-by-frame. If the kernel ever drifts off a Noether-derived invariant, the verifier fires.

MASTER EQUATION · .lagra TERMS PLUGGED IN ENTITY .lagra entity ball { mass:1.0, q₀=(-1,0,0) } entity ball { mass:1.0, q₁=( 1,0,0) } T = ½ Σᵢ mᵢ q̇ᵢ² kinetic · one term per entity PLUGGED IN ↓ T = ½(1.0)q̇₀² + ½(1.0)q̇₁² BOND · ENVIRONMENT .lagra bond harmonic [0,1] { k:2.0, r0:2.0 } environment classical_gravity ↪ environments.lagra: acceleration_field: [0, -9.81, 0] + runtime intervention (drag) — injected at sim time, not in .lagra V = Σₙ Vₙ(q) potential · one Vₙ per bond / environment-field PLUGGED IN ↓ V = ½(2.0)(|q₁−q₀|−2.0)² + 9.81(m₀y₀ + m₁y₁) L = T − V the Lagrangian · S = ∫ L dt kernel · F = − ∂V/∂q one loop over active nodes per step + state (q, q̇) solver velocity_verlet { // configs/solvers/velocity_verlet.lagra — a .lagra program program { // per particle, per step let ax = fx / mass; // fx from the kernel output above let vx' = vx + ax * dt; let px' = px + vx * dt + 0.5 * ax * dt * dt; } contract: separable_hamiltonian requires: [state, force, mass] } ↻ swap per scene · symplectic_euler · avbd · discrete_action · boris · mujoco_action_descent · … → q′, q̇′

Advancing Science & ML

Because the entire engine is governed by a single equation, that equation itself can be changed — and the consequences measured everywhere at once. Every change becomes a falsifiable claim against existing verifiers and lagra scenes.

The DNA helix on the right folds from first principles — 60 coarse-grained particles under Coulomb + harmonic bonds + base-pair stacking, no empirical Turner parameters in the force field.

Because the Lagrangian is differentiable end-to-end, gradients flow back through the kernel — the engine can train ML models against its own outputs, and pair with agentic harnesses like Lean for verifiable scene work. Measured recovery rows and exclusions live on the benchmarks page; see the Lagra harness for the CLI, MCP, and IDE entrypoints.

starting engine…
↗ open
dna_double_helix.lagra
view .lagra source · dna_double_helix.lagra
// CG Double Helix: 2 backbone strands (green) + 15 complementary base pairs (magenta-yellow).
scene dna_double_helix {
    uses molecular_dynamics
    environment none
    description "Simplified DNA double helix: 2 backbone strands with 15 complementary base pairs."
    wasm true

    entity { type: particle_green,   position: [8.5, 7.5, 4.5] }
    entity { type: particle_magenta, position: [7.8, 7.5, 4.5] }
    entity { type: particle_green,   position: [6.5, 7.5, 4.5] }
    entity { type: particle_yellow,  position: [7.2, 7.5, 4.5] }
    // … 56 more CG particles, arranged on the helical backbone …

    bond harmonic [0, 1] { k: 5.0, r0: 0.7 }   // backbone
    bond harmonic [2, 3] { k: 5.0, r0: 0.7 }
    bond harmonic [1, 3] { k: 2.0, r0: 0.6 }   // base-pair stack
    bond harmonic [0, 4] { k: 8.0, r0: 0.48 }  // rise
    // … dozens more bonds wiring the helical structure …

    nonbonded { type: lennard_jones, epsilon: 0.0003, sigma: 0.4, cutoff: 1.5 }
    thermostat { kT: 0, friction: 0.5 }
    @expect subluminal_velocity
    solver { dt: 0.005, steps_per_frame: 50 }
    display { unit: "fm" }
}

Games & performance

Interventions are a natural fit for game-style state. Instead of coding mechanics, you insert entities and forces into the equation — gameplay logic and physics share the same scripting surface.

One codebase: native, mobile, wasm. The bundle on this page is ~800 KB gzipped, full parser + solver + CPU rasterizer included. On native, an optional gpu feature adds a wgpu compute-shader overlay without changing the pipeline.

Jenga — pull a block, drop the tower

36 rigid bodies, 12 layers of 3, settled on a friction floor. Drag any block — the red dot attaches a damped-spring intervention through the same surface a gameplay scripting hook would use. AVBD's contact solver keeps the stack stable through small perturbations and topples it gracefully when you yank too hard.

starting engine…
↗ open
avbd_jenga_tower.lagra
view .lagra source · avbd_jenga_tower.lagra
// AVBD Jenga Tower — 36 blocks (12 layers × 3), alternating 90° per layer.
scene avbd_jenga_tower {
    uses newtonian.classical_mechanics.avbd
    environment classical_gravity
    description "Jenga tower: 12 layers of 3 blocks, alternating orientation."
    wasm true
    floor { y: 0.0, k: 1400.0, damping: 2.0, friction: 0.6 }
    render "rigid_body"

    entity { type: avbd_block, position: [-0.85, 0.25, 0.00], mass: 2.0, size: [0.80, 0.50, 2.40] }
    entity { type: avbd_block, position: [ 0.00, 0.25, 0.00], mass: 2.0, size: [0.80, 0.50, 2.40] }
    entity { type: avbd_block, position: [ 0.85, 0.25, 0.00], mass: 2.0, size: [0.80, 0.50, 2.40] }
    // … layer 2 rotates 90° via orientation quaternion [0, 0.707, 0, 0.707] …
    // … 33 more avbd_block entities stacked 12 layers high …

    @expect charge_conservation
    solver {
        backend:        "lagra_native",
        integrator:     "avbd",
        contact_model:  "augmented_lagrangian",
        contact_solver: "variational_block_descent",
        iterations:     10,
        warmstart:      true,
        dt:             0.001,
        steps_per_frame: 16
    }
    display { unit: "m" }
}

One open kernel and architecture under Apache 2.0

Physics engines are fragmented by license and by platform. PhysX, MuJoCo, ViennaRNA, Rosetta — each ships under a different non-commercial clause, and none of them target the same runtime as another.

Rust collapses that. One codebase compiles to native, mobile, and wasm — same kernel, same numerics, same renderer. The 6 MB bundle running in your tab right now is byte-identical to the desktop binary.

Lagra is the open substrate underneath — Apache 2.0, no per-seat clause, no commercial gate. Compose freely.

.lagra scene · bundle compile wasm + native one binary desktop native mobile browser HPC py env RL / py
consumed by
Developerswrite .lagra scenes, pull lagra-core as a crate, ship native + wasm from one source tree.
End usersresearchers + students open the desktop IDE, share mobile demo links, run scenes from the live page.
AgentsClaude Code, Codex, Albert — all of them speak the MCP server, hold typed handles to a live World.
Training pipelinesPython bridge → PyTorch / JAX. Trajectories are differentiable tensors of (particles × dims × steps); gradients flow back through the engine.

Drive it from anywhere

CLI harness for agent loops, a desktop IDE, and an MCP gateway so Claude Code / Codex / Albert can hold typed handles to a running World. Quick-start commands + the project catalog live in one place.

Tools & setup →
starting engine…
↗ open
avbd_bye_dominos.lagra