Hyprland Spring Animation Configuration: A Complete Guide to Physics-Based Motion

Configure physics-based spring animations in Hyprland using the hl.curve() API with mass, stiffness, and dampening parameters, then reference them via the spring field in any animation definition.

Hyprland spring animation configuration allows you to replace traditional easing curves with physical simulations that follow Hooke's law. The compositor supports two curve types—classic bezier curves and spring-based curves—with the latter providing natural, inertia-based motion for windows, workspaces, and UI transitions by solving differential equations at runtime.

How Spring Physics Work in Hyprland

Unlike static bezier curves, spring animations simulate a virtual mass attached to a spring. The animation manager calculates motion frame-by-frame using three tunable physical constants:

Parameter Description Typical Range
mass Inertia of the virtual mass; larger values delay acceleration 0.1 – 5.0
stiffness Restoring force of the spring; higher values create snappier motion 100 – 400
dampening Damping factor controlling oscillation; higher values reduce bounce 10 – 30

When you reference a spring in an animation, Hyprland delegates timing calculations to the spring solver rather than a predefined curve lookup. This produces organic deceleration and optional overshoot based on the damping ratio derived from your parameters.

Defining Spring Curves in Configuration

The hl.curve() Syntax

Springs are declared globally using the hl.curve() function with type = "spring". According to the source code in src/config/lua/bindings/LuaBindingsConfigRules.cpp (lines 447-448), the engine validates that referenced spring names exist at configuration load time—missing definitions trigger immediate startup errors.

Define a custom spring in your hyprland.lua:

-- Define a spring named "soft" with gentle, bouncy motion
hl.curve("soft", { 
    type = "spring", 
    mass = 1.5, 
    stiffness = 180, 
    dampening = 20 
})

The Config::animationTree() stores these definitions, making them available to the animation factory at Animation::mgr()->createAnimation.

Parameter Tuning Guidelines

Use lower stiffness (100-150) for relaxed, floating movements suitable for workspace transitions. Increase mass (2.0+) to create heavy, luxurious window movements, or decrease it below 1.0 for twitchy, responsive feedback. Adjust dampening above 25 to eliminate overshoot entirely, or keep it between 10-15 for iOS-style elastic snap-back effects.

Applying Springs to Animations

Once defined, reference your spring in any animation block using the spring key. The AnimationManager (implemented in src/managers/AnimationManager.cpp) automatically instantiates the physics solver when it detects a spring reference instead of a bezier curve.

Window Open and Close Animations

Target window lifecycle events via entries processed by src/desktop/view/animationControllers/WindowAnimationController.cpp:

hl.animation({
    leaf    = "windowsIn",
    enabled = true,
    speed   = 4.2,
    spring  = "soft",
    style   = "popin 85%"
})

This configuration replaces the default easing with your physics-based curve when windows spawn.

Workspace Transitions

Control workspace sliding and fading through src/state/WorkspacePlacementController.cpp:

hl.animation({
    leaf    = "workspace",
    enabled = true,
    speed   = 5.0,
    spring  = "soft"
})

Workspace animations benefit from springs with higher stiffness (300+) to ensure the desktop feels anchored and responsive during rapid switching.

Mixing Curve Types

Hyprland allows simultaneous use of bezier and spring curves. Maintain legacy easing for subtle fades while using springs for dramatic motion:

-- Retain bezier for opacity changes
hl.curve("default", { 
    type = "bezier", 
    points = {0.25, 0.1, 0.25, 1.0} 
})

-- Use spring for spatial movement
hl.curve("dynamic", { 
    type = "spring", 
    mass = 0.8, 
    stiffness = 250, 
    dampening = 22 
})

Source Code Implementation Details

The spring system integrates at multiple layers of the Hyprland codebase:

  • Validation: LuaBindingsConfigRules.cpp parses hl.curve() declarations and validates spring references in hl.animation() blocks, emitting configuration errors if names are undefined (lines 447-448).
  • Storage: Curve definitions reside in the configuration tree accessed via Config::animationTree().
  • Instantiation: AnimationManager.cpp handles the creation of animation objects, selecting between bezier interpolation and spring physics based on the presence of the spring field.
  • Window Hooks: WindowAnimationController.cpp and WorkspacePlacementController.cpp consume these animations to drive visual updates during state changes.

The physics solver implements the classic Hooke's law differential equation, guaranteeing deterministic, frame-rate-independent motion across all monitor refresh rates.

Summary

  • Declare springs with hl.curve("<name>", { type = "spring", mass = ..., stiffness = ..., dampening = ... }) before referencing them.
  • Reference springs in animation blocks via the spring key to activate physics-based motion instead of bezier curves.
  • Validate configuration: Undefined spring names cause startup errors in LuaBindingsConfigRules.cpp.
  • Tune physics: Adjust mass for inertia, stiffness for speed, and dampening for overshoot control to match your ergonomic preferences.
  • Scope freely: Use different springs for windows, workspaces, and UI elements by targeting specific animation leaves.

Frequently Asked Questions

What is the difference between bezier and spring curves in Hyprland?

Bezier curves are predefined mathematical functions that interpolate between start and end values using control points, producing consistent timing regardless of distance. Spring curves simulate physical mass-spring systems, where animation duration varies based on the "distance" to travel and the physics constants, creating natural acceleration and deceleration that responds dynamically to the amount of change.

How do I fix "spring not found" configuration errors?

This error originates from src/config/lua/bindings/LuaBindingsConfigRules.cpp when an animation references a spring name not defined in your configuration. Ensure you declare the spring with hl.curve("yourName", { type = "spring", ... }) before the hl.animation() block that references it via spring = "yourName". Check for typos in the name string, as the validator performs exact string matching at startup.

What are the optimal mass, stiffness, and dampening values for smooth animations?

For general desktop use, start with mass = 1.0, stiffness = 200, and dampening = 20. For snappy window management, use mass = 0.6, stiffness = 300, dampening = 25. For relaxed, cinematic motion, try mass = 2.5, stiffness = 120, dampening = 15. Values outside the documented ranges (mass > 5.0 or stiffness > 500) may cause instability or excessive duration according to the animation manager's solver implementation.

Can I use different springs for windows and workspaces?

Yes. Hyprland's animation tree supports distinct configurations per animation leaf. Define multiple springs—such as hl.curve("windowSpring", ...) and hl.curve("workspaceSpring", ...)—then reference the appropriate name in each hl.animation() block by setting its leaf parameter to "windowsIn" or "workspace" respectively. Each animation type maintains independent curve references in Config::animationTree().

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →