Animations
Motion when switching workspaces.
Sweets animates workspace switches, window opening, closing, and tiled layout changes. The animations are Sweets' own, built on the scene graph. There is no external effects dependency.
sweets.animations{ ... }
sweets.animations {
enabled = true, -- master switch (false = every animation instant)
workspace_switch = {
enabled = true,
duration = 220, -- ms, 0–5000 (0 = instant)
direction = "auto", -- "auto" | "horizontal" | "vertical"
curve = "0.33,1.0,0.68,1.0", -- cubic-bézier easing
},
window_open = {
enabled = true,
type = "fade", -- "fade" | "zoom"
initial_scale = 0.8, -- zoom only, 0.1–1.0
duration = 200, -- ms, 0–5000 (0 = instant)
curve = "0.33,1.0,0.68,1.0",
},
window_move = {
enabled = true,
duration = 250, -- ms, 0–5000 (0 = instant)
curve = "0.33,1.0,0.68,1.0",
},
window_close = {
enabled = true,
duration = 200, -- ms, 0–5000 (0 = instant)
curve = "0.33,1.0,0.68,1.0",
},
}| Key | Type | Default | Description |
|---|---|---|---|
enabled | bool | true | Master switch. false makes every animation instant |
workspace_switch | table | — | The workspace-switch slide |
window_open | table | — | The window-open fade or zoom |
window_move | table | — | The tiled move/resize animation |
window_close | table | — | The window-close fade |
Each animation has its own enabled. The master switch and the block's switch
must both be on for it to play.
workspace_switch
| Field | Type | Default | Description |
|---|---|---|---|
enabled | bool | true | Turn the workspace slide on or off |
duration | int | 220 | Slide length in ms, 0–5000 (0 = instant) |
direction | string | "auto" | "auto", "horizontal", or "vertical" |
curve | string | table | "0.33,1.0,0.68,1.0" | Easing (see Curves) |
window_open
| Field | Type | Default | Description |
|---|---|---|---|
enabled | bool | true | Turn the window-open effect on or off |
type | string | "fade" | "fade" or "zoom" |
initial_scale | number | 0.8 | Zoom starting scale, 0.1–1.0; ignored by fade |
duration | int | 200 | Effect length in ms, 0–5000 (0 = instant) |
curve | string | table | "0.33,1.0,0.68,1.0" | Easing (see Curves) |
When opening a tiled window also moves existing windows, the reveal edge follows
the window_move clock while the fade or zoom keeps its own window_open
duration. The two clocks are independent, so a slow open continues smoothly
after a faster neighbor settles.
The newcomer stays clipped to the area the moving edge uncovers, so it never
appears underneath a still-resizing neighbor. The seam keeps
sweets.gaps.inner spacing for the whole glide.
Zoom scales a temporary snapshot from the centre while the live window stays at its final geometry. Client input, layout state, and configure size stay stable throughout. The snapshot covers content, subsurfaces, and border, and cannot receive pointer input.
window_move
| Field | Type | Default | Description |
|---|---|---|---|
enabled | bool | true | Turn the tiled move/resize animation on or off |
duration | int | 250 | Animation length in ms, 0–5000 (0 = instant) |
curve | string | table | "0.33,1.0,0.68,1.0" | Easing (see Curves) |
When the layout changes — opening or closing a tiled window, switching layouts, resizing the master — the affected windows slide and resize to their new slots instead of snapping.
Behavior guarantees during a transition:
- A displaced neighbor keeps covering the space a new window will occupy, so no desktop background flashes through.
- Each animated window stays confined to its source monitor, including surface, border, and popups.
- Content is clipped at its natural resolution, not stretched, so text and terminal cells keep their shape.
- Configured gaps stay filled the whole time.
- Floating windows and interactive drag-resize are unaffected.
A rare mixed-axis change, where one dimension grows while the other shrinks, snaps the size but keeps the positional glide.
Layout switches
Switching between layouts uses frozen frames and moving cutouts: the windows
that are changing size move live, while the rest of the layout stays visually
stable behind them. This applies to every layout pair — including shared-slot
layouts like monocle and deck — and to all four master positions.
The result is that a switch never exposes the desktop, never flickers between live content and background, and never resizes windows that are not involved.
Rapid layout requests are coordinated per workspace. The current transition finishes one complete layout before the next begins, and only the latest requested layout is kept. Repeated cycle commands update that single pending target, so an even number cancels the switch and an odd number lands on the other layout.
With animations disabled, requests are immediate and bypass this coordination.
Maximize and fullscreen
Maximize, unmaximize, and both directions of the compositor fullscreen action
use the same duration and curve.
Application-requested fullscreen changes are instant in both directions. Wayland does not identify video surfaces, so this boundary keeps browser video and games responsive without disabling ordinary layout animation.
Switching directly between maximized and fullscreen animates between the two output boxes, so the window contracts or expands from its visible frame. Reversing mid-animation hands the current visual box to the new direction rather than restarting from a stale slot.
Fullscreen freezes and masks the floating layer, since it occludes it. Maximize deliberately leaves floating windows live above the maximized client.
Closing
Closing uses the reverse transition. The closing snapshot is cropped away by the expanding survivor and released when the move settles.
When the focused window closes, keyboard focus and the focused border move to
the next eligible window immediately. With pointer follow_focus on, the
pointer moves in the same step. The closing snapshot then finishes on its own.
window_close
| Field | Type | Default | Description |
|---|---|---|---|
enabled | bool | true | Turn the window-close fade on or off |
duration | int | 200 | Fade length in ms, 0–5000 (0 = instant) |
curve | string | table | "0.33,1.0,0.68,1.0" | Easing (see Curves) |
When a window closes, a temporary snapshot of its content and border fades out in place. The fade starts the moment the window actually goes away, so a close the application refuses — to show a "save changes?" dialog — never fades.
It runs concurrently with the survivors' window_move glide, so neighbors
reclaim the space while the old window melts away over them. The snapshot cannot
receive pointer input, never leaves its monitor, and is released when the fade
completes.
Set window_close = { enabled = false } or duration = 0 for instant
disappearance. Windows closing on a workspace you are not viewing never fade.
Snapshots that only hold a vacated slot for anti-flash purposes — drag starts, minimize, a tiled window turning floating — stay at full opacity. Only real closes fade.
Turning animations off
The top-level enabled = false makes everything instant with one switch. Your
settings are kept, so flipping it back to true restores them.
Each block also has its own enabled, and both must be on for the animation to
play. workspace_switch = { enabled = false } disables just the slide.
Curves
The curve controls acceleration feel independently of duration. It is a
cubic-bézier timing function — the same model as CSS cubic-bezier(),
Hyprland, and Mango.
Pass four numbers as "x1,y1,x2,y2" or a { x1, y1, x2, y2 } table. These are
the two control points of a curve whose endpoints are fixed at (0,0) and
(1,1).
local quick = "0.46,1.0,0.29,0.99"
local bounce = { 0.34, 1.56, 0.64, 1.0 }
sweets.animations {
workspace_switch = { duration = 200, curve = quick },
}Sweets does not reserve curve names. Use ordinary Lua variables, as above, to name and reuse curves.
The x values must stay within 0..1. The y values may go past 1 or below
0 to make the slide overshoot and settle back, for a springy feel. The
default 0.33,1.0,0.68,1.0 is an ease-out; linear is 0.0,0.0,1.0,1.0.
The slide
When you switch workspaces on the same output, the outgoing workspace slides off one edge while the incoming one slides in from the opposite edge. A higher-numbered workspace enters from the trailing edge (right or bottom), a lower-numbered one from the leading edge.
The slide is paced by the monitor's refresh rate, so it stays smooth on high-refresh and 4K displays and never spins the CPU when idle. Windows keep their arranged positions — only a temporary offset animates — so nothing is re-laid-out.
Triggering another switch mid-slide retargets the current slide smoothly. If a command changes geometry, visibility, grouping, or layout during the slide, Sweets finishes the slide first and then applies it. The command is never discarded.
Slide direction
| Value | Behavior |
|---|---|
"auto" | Horizontal (the default) |
"horizontal" | Slide left/right |
"vertical" | Slide up/down |
Multi-monitor. The sliding workspace is clipped to its own monitor and is never drawn on a neighboring screen, on any axis or arrangement. Only the output whose workspace changed animates.
Only interactive workspace switches animate. Switches caused by a config reload, monitor hotplug, or workspace reassignment apply instantly.
The slide animates window content and grouped-window tab strips together.
Window open
When a window maps for the first time, the whole window — content and border —
uses the configured fade or zoom effect. Like the slide, it is paced by the
monitor's refresh rate.
Set window_open = { enabled = false } or duration = 0 for instant
appearance. Windows mapping onto a workspace you are not viewing do not animate.
Fade is the default. Set type = "zoom" for an 80% to 100% centred zoom, and
adjust initial_scale for a subtler or stronger effect. Fully translucent
clients using fade may briefly reach full opacity as the fade ends, before
their own transparency re-applies.