LagrangianLive demo. Click, throw, drop.
Everything on this page moves with real physics from @sweberdev/lagrangian: springs solved exactly, friction, gravity, collisions. Nothing has a fixed duration. Interrupt anything and it continues from where it is and how fast it moves.
Springs
Click faster than CSS can keep up.A transition restarts. A spring keeps its momentum.
Both dots go to the other side when you click. The upper one uses a CSS transition, the lower one a Lagrangian spring. Click several times in a row and watch the plot: the transition stops dead and starts again, the spring turns around smoothly because it knows its velocity.
The spring
import { animate } from "@sweberdev/lagrangian"
dot.addEventListener("click", () => {
open = !open
// Interrupting keeps the current velocity.
animate(dot, { x: open ? 320 : 0 }, { duration: 0.6, bounce: 0.25 })
})Throw
Throws that land where they should.Velocity, friction, snap points and soft edges.
Swipe the cards. Lagrangian measures how fast you let go, lets the strip glide with friction like iOS scrolling and adjusts the glide so it ends exactly on a card. Pull past the first or last card and it resists, then springs back. The puck on the right has no snap points, only walls.
Cards
Drag and let go
Free throw
Drag and let go
The cards
import { draggable, nearest } from "@sweberdev/lagrangian"
draggable(strip, {
axis: "x",
bounds: { left: -4 * step, right: 0 },
snap: { x: [0, -step, -2 * step, -3 * step, -4 * step] },
})
draggable(puck, { bounds: box })World
Drop things. Grab them. Throw them.Gravity, collisions, friction and rolling.
Pick up any ball and throw it. Bigger balls are heavier, collisions keep momentum, and friction at the contact point makes balls roll. Switch to the Moon or turn gravity off. When everything rests for a second the world stops computing, so an idle page costs nothing.
0 bodies · at rest, no CPU used
The playground
import { World } from "@sweberdev/lagrangian"
const world = new World({ bounds: box }).start()
for (const el of box.querySelectorAll(".ball")) {
world.add({ x: 120, y: 0, radius: 24, restitution: 0.85, element: el })
}
world.bindPointer() // grab and throw
world.gravity = { x: 0, y: 1.62 } // the MoonDouble pendulum
Write your own equations of motion.Chaos from one Lagrangian.
Two double pendulums, started one thousandth of a radian apart. Their equations of motion come straight from the Lagrangian L = T − V and are stepped with RK4 at 240 Hz. For a few seconds they move as one, then they go completely different ways. The energy readout shows how little the integrator drifts.
Time 0.0 s · Energy drift 0.0000 %
The equations
import { loop, system } from "@sweberdev/lagrangian"
// State [θ1, θ2, ω1, ω2]; the accelerations follow
// from the Euler-Lagrange equations of L = T − V.
const pendulum = system(([a1, a2, w1, w2]) => {
const d = a1 - a2, den = 3 - Math.cos(2 * d)
return [
w1,
w2,
(-3 * g * Math.sin(a1) - g * Math.sin(a1 - 2 * a2)
- 2 * Math.sin(d) * (w2 * w2 + w1 * w1 * Math.cos(d))) / den,
(2 * Math.sin(d) * (2 * w1 * w1 + 2 * g * Math.cos(a1)
+ w2 * w2 * Math.cos(d))) / den,
]
}, [angle, angle, 0, 0])
loop.add((dt) => draw(pendulum.advance(dt)))Get it
About 7 kB of physics.Free and open source under the MIT licence.
Springs, throws, dragging and the world work with any framework. React hooks are in @sweberdev/lagrangian-react. The docs explain every option and the maths behind it.
pnpm add @sweberdev/lagrangian