# How to Implement Custom Bézier Curves in Hyprland Animations

> Learn to implement custom bezier curves in Hyprland animations using the hl.curve() Lua helper. Customize your animations with precise control. Get started now!

- Repository: [Hypr Development/Hyprland](https://github.com/hyprwm/Hyprland)
- Tags: how-to-guide
- Published: 2026-07-26

---

**Hyprland allows you to define custom cubic Bézier curves using the `hl.curve()` Lua helper and apply them to any animation via the `bezier` field in `hl.animation()`.**

Hyprland's animation system provides fine-grained control over window transitions through user-defined Bézier curves. By leveraging the Lua configuration API, you can create custom easing functions that govern acceleration and deceleration across any animation leaf. This guide walks through the exact implementation details found in the Hyprland source code, from parsing in [`LuaBindingsConfigRules.cpp`](https://github.com/hyprwm/Hyprland/blob/main/LuaBindingsConfigRules.cpp) to runtime verification using `hyprctl`.

## How Bézier Curves Work in Hyprland

Under the hood, Hyprland implements the **classic cubic Bézier formula** using two control points you specify. When an animation leaf is created, the compositor looks up your curve name using `Animation::mgr()->bezierExists(name)` and applies the stored control points to drive the timing function.

The curve definitions are stored centrally in the **AnimationTree** managed by [`src/config/shared/animation/AnimationTree.hpp`](https://github.com/hyprwm/Hyprland/blob/main/src/config/shared/animation/AnimationTree.hpp) and [`src/config/shared/animation/AnimationTree.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/config/shared/animation/AnimationTree.cpp). This central storage allows the same curve name to be reused across multiple animation leaves—such as windows, layers, and fade effects—ensuring consistent timing throughout your desktop environment.

## Defining a Custom Bézier Curve

### Syntax and Structure

You define curves using the `hl.curve()` helper in your Lua configuration. According to the source in [`src/config/lua/bindings/LuaBindingsConfigRules.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/config/lua/bindings/LuaBindingsConfigRules.cpp), the parser validates that your table contains `type = "bezier"` and extracts the control points from the `points` array.

Each curve requires:
- A **unique name** (e.g., `"myFastEase"`)
- A **type field** set to `"bezier"`
- A **points array** containing two pairs of floats: `{ {x0, y0}, {x1, y1} }`

### Choosing Control Points

Control points are pairs of floats in the range **[0, 1]**:
- The **first point** `{x0, y0}` controls the initial acceleration (how quickly the animation starts).
- The **second point** `{x1, y1}` controls the final deceleration (how the animation eases to a stop).

Values outside the [0, 1] range may produce unexpected results, as the cubic Bézier implementation expects normalized coordinates.

```lua
-- Define a custom Bézier curve called "myFastEase"
hl.curve(
    "myFastEase",
    {
        type   = "bezier",
        points = { {0.15, 0.0}, {0.1, 1.0} }   -- (x0,y0) and (x1,y1)
    }
)

```

## Applying Curves to Animation Leaves

Once defined, reference your curve in any animation leaf using the `bezier` field. The `hl.animation()` function accepts the leaf type, speed, and your custom curve name.

```lua
hl.animation({
    leaf    = "windows",
    enabled = true,
    speed   = 5.0,
    bezier  = "myFastEase"
})

```

Because curves are stored centrally in the AnimationTree, you can apply `"myFastEase"` to multiple leaves—such as `windows`, `fade`, and `layersOut`—without redefining the control points.

## Verifying Your Configuration

After reloading your configuration with `hyprctl reload`, verify that your curves loaded correctly:

```bash
hyprctl animations

```

This command outputs all defined curves with their control points, as implemented in [`src/debug/HyprCtl.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/debug/HyprCtl.cpp). The output format lists the name, X0, Y0, X1, and Y1 values for each curve, allowing you to confirm that [`LuaBindingsConfigRules.cpp`](https://github.com/hyprwm/Hyprland/blob/main/LuaBindingsConfigRules.cpp) parsed your specification correctly.

## Complete Configuration Example

Here is a practical setup defining three distinct curves and applying them to different UI elements:

```lua
-- 1️⃣ Define custom curves
hl.curve("easeOutQuint", {
    type   = "bezier",
    points = { {0.23, 1.0}, {0.32, 1.0} }
})

hl.curve("linear", {
    type   = "bezier",
    points = { {0.0, 0.0}, {1.0, 1.0} }
})

hl.curve("quick", {
    type   = "bezier",
    points = { {0.15, 0.0}, {0.1, 1.0} }
})

-- 2️⃣ Wire the curves to animation leaves
hl.animation({ leaf = "windows",   enabled = true, speed = 4.8, bezier = "easeOutQuint" })
hl.animation({ leaf = "fade",      enabled = true, speed = 3.0, bezier = "linear" })
hl.animation({ leaf = "layersOut", enabled = true, speed = 1.5, bezier = "quick", style = "fade" })

```

## Summary

- **Define curves** with `hl.curve(name, {type="bezier", points={{x0,y0},{x1,y1}}})` in your Lua configuration.
- **Store centrally** in the AnimationTree ([`src/config/shared/animation/AnimationTree.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/config/shared/animation/AnimationTree.cpp)) for reuse across multiple animation types.
- **Apply curves** via the `bezier` field in `hl.animation()` to control timing for windows, fades, and layer shells.
- **Verify loading** using `hyprctl animations` to inspect parsed control points.
- **Parse location** is [`src/config/lua/bindings/LuaBindingsConfigRules.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/config/lua/bindings/LuaBindingsConfigRules.cpp), which validates the `type` field and extracts coordinates.

## Frequently Asked Questions

### What is the valid range for Bézier control points in Hyprland?

Control points must be pairs of floats in the range **[0, 1]**, representing the relative coordinates of the cubic Bézier control handles. Values outside this range are not standard for the easing implementation and may cause undefined behavior in the animation timing.

### Can I use the same custom curve for multiple animation types?

Yes. Once defined with `hl.curve()`, the curve is stored in the global AnimationTree and can be referenced by any animation leaf—such as `windows`, `fade`, or `layersOut`—through the `bezier` field. This ensures consistent acceleration and deceleration across different UI elements without duplicate definitions.

### How do I debug if my Bézier curve is not loading?

Run `hyprctl animations` to inspect the loaded curves and their control points. If your curve is missing, verify that your Lua table includes `type = "bezier"`, as the parser in [`src/config/lua/bindings/LuaBindingsConfigRules.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/config/lua/bindings/LuaBindingsConfigRules.cpp) strictly validates this field before extracting the points array.

### Where does Hyprland store the parsed Bézier definitions?

The parsed curves are stored in the global animation tree implemented in [`src/config/shared/animation/AnimationTree.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/config/shared/animation/AnimationTree.cpp), which provides the `bezierExists()` lookup method. When an animation leaf is instantiated, Hyprland queries this central store to retrieve the control points for the specified curve name.