diff --git a/README.md b/README.md index bfa3416..9672b40 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,19 @@ https://github.com/user-attachments/assets/4ad228f2-ed69-40dc-8ab3-287480fa4427 https://github.com/user-attachments/assets/a62bb9d3-9fe3-4a35-9c87-d9af3f45c999 -https://github.com/user-attachments/assets/burn.mp4 +https://github.com/user-attachments/assets/0e3e31c5-d14d-463c-a0a5-f1595a83658f -https://github.com/user-attachments/assets/burn-ashes.mp4 +https://github.com/user-attachments/assets/2fa9dd95-5988-48a3-8ec7-a72e72b74be5 -https://github.com/user-attachments/assets/glitch.mp4 +https://github.com/user-attachments/assets/0f4a8d7b-211a-4711-b184-8faadb9d32ff + +https://github.com/user-attachments/assets/7fac12ba-47f9-4aab-8827-c81bac32e7fc + +https://github.com/user-attachments/assets/501297a7-085a-4dcc-bf53-11acb6edee43 + +https://github.com/user-attachments/assets/fe469e11-6df0-4437-9597-28c9a7e1b2d7 + +https://github.com/user-attachments/assets/73ab4218-a1dd-434a-ae57-3ca8d237664e diff --git a/animations/bloom.kdl b/animations/bloom.kdl index f574011..6f4f882 100644 --- a/animations/bloom.kdl +++ b/animations/bloom.kdl @@ -8,7 +8,7 @@ animations { } window-open { - duration-ms 200 + duration-ms 250 curve "linear" custom-shader r" @@ -36,7 +36,7 @@ animations { } window-close { - duration-ms 300 + duration-ms 250 curve "linear" custom-shader r" diff --git a/animations/burn-ashes.kdl b/animations/burn-ashes.kdl index 07dc529..e1019a2 100644 --- a/animations/burn-ashes.kdl +++ b/animations/burn-ashes.kdl @@ -10,7 +10,7 @@ animations { window-open { - duration-ms 400 + duration-ms 700 curve "linear" custom-shader r" float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); } @@ -53,7 +53,7 @@ animations { } window-close { - duration-ms 600 + duration-ms 700 curve "linear" custom-shader r" float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); } diff --git a/animations/burn-multicolor.kdl b/animations/burn-multicolor.kdl index 7ac7cc2..9b2ec0f 100644 --- a/animations/burn-multicolor.kdl +++ b/animations/burn-multicolor.kdl @@ -9,7 +9,7 @@ animations { } window-open { - duration-ms 500 + duration-ms 700 curve "linear" custom-shader r" float hash(vec2 p) { @@ -60,7 +60,7 @@ animations { vec4 color = texture2D(niri_tex, coords_tex.st); float edge_dist = min(min(uv.x, 1.0 - uv.x), min(uv.y, 1.0 - uv.y)); - float n = noise(uv * 8.0 + niri_random_seed * 100.0) * 0.3; + float n = noise(uv * 9.0 + niri_random_seed * 100.0) * 0.3; float burn_line = edge_dist + n; float threshold = progress * 0.8; @@ -83,7 +83,7 @@ animations { } window-close { - duration-ms 500 + duration-ms 1200 curve "linear" custom-shader r" float hash(vec2 p) { @@ -134,7 +134,7 @@ animations { vec4 color = texture2D(niri_tex, coords_tex.st); float edge_dist = min(min(uv.x, 1.0 - uv.x), min(uv.y, 1.0 - uv.y)); - float n = noise(uv * 8.0 + niri_random_seed * 100.0) * 0.3; + float n = noise(uv * 9.0 + niri_random_seed * 100.0) * 0.3; float burn_line = edge_dist + n; float threshold = progress * 0.8; diff --git a/animations/burn.kdl b/animations/burn.kdl index 8505686..b35cbb0 100644 --- a/animations/burn.kdl +++ b/animations/burn.kdl @@ -6,7 +6,7 @@ animations { } window-open { - duration-ms 500 + duration-ms 700 curve "linear" custom-shader r" float hash(vec2 p) { @@ -52,7 +52,7 @@ animations { } window-close { - duration-ms 500 + duration-ms 1200 curve "linear" custom-shader r" float hash(vec2 p) { diff --git a/animations/config.toml b/animations/config.toml new file mode 100644 index 0000000..bd72499 --- /dev/null +++ b/animations/config.toml @@ -0,0 +1,157 @@ +# Nirimation Configuration +# Edit values here, then run: ./nirimation-apply +# +# Inspired by XansiVA/nirimation - using Niri's include system for modularity + +[animation] +# Which animation module to use +# Options: glitch, glitch-cyberpunk, burn, burn-ashes, burn-multicolor, bloom, +# pixelate, fold-window, pop-drop, ribbons, roll-drop, swipe-window, unravel +active = "glitch-cyberpunk" + +# Path to your niri config (usually doesn't need changing) +niri_config = "~/.config/niri/config.kdl" + +# ============================================ +# GLITCH SHADER SETTINGS +# ============================================ +[glitch] +open_duration_ms = 700 +close_duration_ms = 1000 +rgb_split = 0.04 # chromatic aberration intensity (0.02-0.08) +scanline_strength = 0.12 # scanline visibility (0.05-0.15) +scanline_density = 450.0 # number of scanlines (200-600) + +# ============================================ +# GLITCH CYBERPUNK SHADER SETTINGS +# ============================================ +[glitch-cyberpunk] +open_duration_ms = 500 +close_duration_ms = 700 +rgb_split = 0.06 # aggressive red-shifted split +scanline_strength = 0.12 # CRT scanlines +scanline_density = 450.0 # scanline count +noise_intensity = 0.15 # static noise overlay +tint_strength = 0.4 # red/magenta tint intensity +glow_intensity = 0.6 # neon edge glow + +# ============================================ +# BURN SHADER SETTINGS +# ============================================ +[burn] +open_duration_ms = 700 +close_duration_ms = 700 +edge_roughness = 8.0 # higher = finer burn edge pattern +glow_width = 0.08 # width of ember glow line +burn_reach = 0.8 # how far burn extends (0.0-1.0) + +# ============================================ +# BURN-ASHES SHADER SETTINGS +# ============================================ +[burn-ashes] +open_duration_ms = 700 +close_duration_ms = 700 +edge_roughness = 8.0 +glow_width = 0.08 +burn_reach = 0.8 +# Particle settings +ash_size = 150 # higher = smaller particles +ash_layers = 7 # more layers = more particles +ember_size = 280 # higher = smaller sparks +ember_layers = 8 # more layers = more sparks + +[burn-ashes.colors] +# Ember colors (randomly selected per window) +orange = true +blue = false +purple = false +green = false +pink = false +cyan = true +gold = false +red = true + +# ============================================ +# BLOOM SHADER SETTINGS +# ============================================ +[bloom] +open_duration_ms = 700 +close_duration_ms = 700 + +# ============================================ +# PIXELATE SHADER SETTINGS +# ============================================ +[pixelate] +open_duration_ms = 700 +close_duration_ms = 700 +pixel_size = 40.0 # size of pixels during effect + +# ============================================ +# BURN-MULTICOLOR SHADER SETTINGS +# ============================================ +[burn-multicolor] +open_duration_ms = 700 +close_duration_ms = 1200 +edge_roughness = 9.0 +glow_width = 0.08 +burn_reach = 0.8 + +[burn-multicolor.colors] +# Ember colors (randomly selected per window) +orange = true +blue = true +purple = true +green = true +pink = true +cyan = true +gold = true +red = true + +# ============================================ +# FOLD-WINDOW SHADER SETTINGS +# ============================================ +[fold-window] +open_duration_ms = 900 +close_duration_ms = 700 +perspective = 600.0 # 3D perspective depth (400-800) + +# ============================================ +# POP-DROP SHADER SETTINGS +# ============================================ +[pop-drop] +open_duration_ms = 700 +close_duration_ms = 700 +fall_distance = 1440.0 # how far window falls (pixels) +max_rotation = 0.5 # max rotation angle (radians) + +# ============================================ +# RIBBONS SHADER SETTINGS +# ============================================ +[ribbons] +open_duration_ms = 700 +close_duration_ms = 700 +ribbon_count = 30 # number of ribbon strips (10-30) + +# ============================================ +# ROLL-DROP SHADER SETTINGS +# ============================================ +[roll-drop] +open_duration_ms = 700 +close_duration_ms = 700 +fall_distance = 1440.0 +max_rotation = 0.5 + +# ============================================ +# SWIPE-WINDOW SHADER SETTINGS +# ============================================ +[swipe-window] +open_duration_ms = 700 +close_duration_ms = 700 + +# ============================================ +# UNRAVEL SHADER SETTINGS +# ============================================ +[unravel] +open_duration_ms = 700 +close_duration_ms = 700 +edge_thickness = 3.0 # thickness of expanding edge line (1-5) diff --git a/animations/explode.kdl b/animations/explode.kdl new file mode 100644 index 0000000..78e35c3 --- /dev/null +++ b/animations/explode.kdl @@ -0,0 +1,171 @@ +// explodes in varied patterns. Screen has a light orange flash for +// *dramatic effect* - inspired by C&C Red Alert + +animations { + window-open { + duration-ms 550 + curve "linear" + custom-shader r" + float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); } + float noise(vec2 p) { + vec2 i = floor(p); vec2 f = fract(p); + f = f * f * (3.0 - 2.0 * f); + return mix(mix(hash(i), hash(i + vec2(1.0, 0.0)), f.x), + mix(hash(i + vec2(0.0, 1.0)), hash(i + vec2(1.0, 1.0)), f.x), f.y); + } + float fbm(vec2 p) { + float f = 0.0; + f += 0.5 * noise(p); p *= 2.1; + f += 0.25 * noise(p); p *= 2.2; + f += 0.125 * noise(p); p *= 2.3; + f += 0.0625 * noise(p); + return f; + } + vec4 open_color(vec3 coords_geo, vec3 size_geo) { + if (coords_geo.x < 0.0 || coords_geo.x > 1.0 || coords_geo.y < 0.0 || coords_geo.y > 1.0) return vec4(0.0); + float progress = niri_clamped_progress; + vec2 uv = coords_geo.xy; + float scale = 0.8 + 0.2 * progress; + vec2 centered = (uv - 0.5) / scale + 0.5; + if (centered.x < 0.0 || centered.x > 1.0 || centered.y < 0.0 || centered.y > 1.0) return vec4(0.0); + vec3 coords_tex = niri_geo_to_tex * vec3(centered, 1.0); + vec4 color = texture2D(niri_tex, coords_tex.st); + return vec4(color.rgb, color.a * progress); + } + " + } + + window-close { + duration-ms 550 + curve "linear" + custom-shader r" + float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); } + float noise(vec2 p) { + vec2 i = floor(p); vec2 f = fract(p); + f = f * f * (3.0 - 2.0 * f); + return mix(mix(hash(i), hash(i + vec2(1.0, 0.0)), f.x), + mix(hash(i + vec2(0.0, 1.0)), hash(i + vec2(1.0, 1.0)), f.x), f.y); + } + float fbm(vec2 p) { + float f = 0.0; + f += 0.5 * noise(p); p *= 2.1; + f += 0.25 * noise(p); p *= 2.2; + f += 0.125 * noise(p); p *= 2.3; + f += 0.0625 * noise(p); + return f; + } + vec4 close_color(vec3 coords_geo, vec3 size_geo) { + float progress = niri_clamped_progress; + vec2 uv = coords_geo.xy; + vec2 center = vec2(0.5, 0.5); + vec2 dir = uv - center; + float dist = length(dir); + float angle = atan(dir.y, dir.x); + + // Quick expansion + float expand = pow(progress, 0.5) * 1.5; + + // ASYMMETRIC SHAPE - varying lobes and angles + float lobe1 = 0.3 * sin(angle * 2.0 + niri_random_seed * 6.28); + float lobe2 = 0.2 * sin(angle * 3.0 + niri_random_seed * 3.14 + 1.0); + float lobe3 = 0.15 * sin(angle * 5.0 + niri_random_seed * 4.0); + float asymmetry = 1.0 + lobe1 + lobe2 + lobe3; + + // Noise on top + float turb = fbm(vec2(angle * 2.0 + niri_random_seed * 10.0, progress * 3.0)) * 0.35; + float fire_edge = expand * asymmetry * (0.75 + turb); + + // Colors - classic C&C palette + vec3 white_hot = vec3(1.0, 1.0, 0.85); + vec3 yellow = vec3(1.0, 0.85, 0.2); + vec3 orange = vec3(1.0, 0.55, 0.1); + vec3 dark_orange = vec3(0.85, 0.35, 0.05); + vec3 red_brown = vec3(0.5, 0.15, 0.05); + vec3 grey_smoke = vec3(0.65, 0.62, 0.58); + + // Fire layers with chunkyness + float fire_turb = fbm((uv - center) * 8.0 - progress * 3.0 + niri_random_seed * 5.0); + float fire_turb2 = fbm((uv - center) * 12.0 + progress * 2.0 + niri_random_seed * 8.0); + + // Asymmetric core of animation, off-centered + vec2 core_offset = vec2( + 0.03 * sin(niri_random_seed * 5.0), + 0.03 * cos(niri_random_seed * 7.0) + ); + float core_dist = length(uv - center - core_offset); + + // Core glow + float core = smoothstep(fire_edge * 0.3, 0.0, core_dist); + // Inner yellow + float inner = smoothstep(fire_edge * 0.45, fire_edge * 0.15, dist); + // Orange body + float mid = smoothstep(fire_edge * 0.7, fire_edge * 0.3, dist); + // Outer burn + float outer = smoothstep(fire_edge, fire_edge * 0.5, dist); + + // Dark patches in fire (for the chunky look) + float dark_patches = fire_turb * fire_turb2; + dark_patches = smoothstep(0.12, 0.35, dark_patches) * mid * (1.0 - core); + + // Fire color + vec3 fire = red_brown; + fire = mix(fire, dark_orange, outer); + fire = mix(fire, orange, mid * (1.0 - dark_patches * 0.7)); + fire = mix(fire, yellow, inner); + fire = mix(fire, white_hot, core); + + // Add dark turbulent patches + fire = mix(fire, red_brown, dark_patches * 0.65); + + // Smoke ring - also asymmetric + float smoke_edge = fire_edge * (1.0 + 0.2 * sin(angle * 4.0 + niri_random_seed)); + float smoke_ring = smoothstep(smoke_edge * 0.8, smoke_edge * 1.2, dist); + smoke_ring *= smoothstep(smoke_edge * 1.7, smoke_edge * 1.1, dist); + float smoke_turb = fbm((uv - center) * 4.0 + niri_random_seed * 3.0); + smoke_ring *= (0.4 + smoke_turb * 0.6) * 0.6; + + // Debris particles + float debris = 0.0; + for (float i = 0.0; i < 30.0; i++) { + float a = hash(vec2(i, niri_random_seed)) * 6.28; + float spd = 0.3 + hash(vec2(i * 2.0, niri_random_seed)) * 0.7; + float dly = hash(vec2(i * 3.0, niri_random_seed)) * 0.15; + float t = clamp((progress - dly) / (1.0 - dly), 0.0, 1.0); + vec2 pos = center + vec2(cos(a), sin(a)) * t * spd * expand; + float d = length(uv - pos); + float size = 0.006 + hash(vec2(i * 4.0, niri_random_seed)) * 0.01; + debris += smoothstep(size, 0.0, d) * (1.0 - t * t); + } + + // Timing - quick flash, sustain, fades out completely + float flash = smoothstep(0.0, 0.12, progress); + float fadeout = pow(smoothstep(1.0, 0.2, progress), 2.0); + + // Fade to nothing + float fire_a = max(max(core, inner), max(mid, outer)) * flash * fadeout; + + // Combine + vec3 result = fire; + result = mix(result, grey_smoke, smoke_ring * fadeout); + result += vec3(1.0, 0.7, 0.2) * debris * fadeout; + + // Window gone immediately (couldn't figure this out for a while) + vec3 coords_tex = niri_geo_to_tex * vec3(uv, 1.0); + vec4 win = vec4(0.0); + if (uv.x >= 0.0 && uv.x <= 1.0 && uv.y >= 0.0 && uv.y <= 1.0) { + win = texture2D(niri_tex, coords_tex.st); + } + float win_fade = 1.0 - smoothstep(0.0, 0.03, progress); + win.a *= win_fade; + + // Everything fades out by end + float explosion_a = max(fire_a, smoke_ring * fadeout * 0.7); + float total_a = max(win.a, explosion_a); + float blend = clamp(fire_a + smoke_ring * 0.4 + smoothstep(0.0, 0.05, progress), 0.0, 1.0); + vec3 final_color = mix(win.rgb, result, blend); + + return vec4(final_color, total_a * fadeout); + } + " + } +} \ No newline at end of file diff --git a/animations/fold-window.kdl b/animations/fold-window.kdl index cd5c702..3642460 100644 --- a/animations/fold-window.kdl +++ b/animations/fold-window.kdl @@ -4,7 +4,7 @@ animations { } window-open { - duration-ms 400 + duration-ms 900 curve "ease-out-expo" custom-shader r" vec4 door_rise(vec3 coords_geo, vec3 size_geo) { @@ -54,7 +54,7 @@ animations { } window-close { - duration-ms 400 + duration-ms 700 curve "ease-out-expo" custom-shader r" vec4 bob_and_slide(vec3 coords_geo, vec3 size_geo) { diff --git a/animations/glitch-cyberpunk.kdl b/animations/glitch-cyberpunk.kdl new file mode 100644 index 0000000..aa5e498 --- /dev/null +++ b/animations/glitch-cyberpunk.kdl @@ -0,0 +1,123 @@ +// Glitch Cyberpunk! Red-tinted chromatic aberration with neon glow. +// Digital corruption effect inspired by cyberpunk. + +animations { + window-open { + duration-ms 500 + curve "linear" + custom-shader r" + float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); } + + vec4 open_color(vec3 coords_geo, vec3 size_geo) { + if (coords_geo.x < 0.0 || coords_geo.x > 1.0 || coords_geo.y < 0.0 || coords_geo.y > 1.0) return vec4(0.0); + float progress = niri_clamped_progress; + float glitch = 1.0 - progress; + vec2 uv = coords_geo.xy; + + // Horizontal displacement + float glitch_bar = step(0.90, hash(vec2(floor(uv.y * 20.0), niri_random_seed))); + float h_offset = glitch_bar * glitch * 0.15 * (hash(vec2(uv.y, niri_random_seed)) - 0.5); + uv.x += h_offset; + + // RGB split. red leads, cyan trails + float split = glitch * 0.12; + vec3 cr = niri_geo_to_tex * vec3(uv + vec2(split * 1.5, split * 0.3), 1.0); + vec3 cg = niri_geo_to_tex * vec3(uv, 1.0); + vec3 cb = niri_geo_to_tex * vec3(uv - vec2(split, -split * 0.2), 1.0); + + float r = texture2D(niri_tex, cr.st).r; + float g = texture2D(niri_tex, cg.st).g; + float b = texture2D(niri_tex, cb.st).b; + float a = texture2D(niri_tex, cg.st).a; + vec3 color = vec3(r, g, b); + + // Noiseyy + float noise = hash(uv * 500.0 + niri_random_seed) * glitch * 0.30; + color += noise; + + // Red/magenta tint overlay stronger during glitch + vec3 cyberpunk_tint = vec3(1.0, 0.2, 0.4); + color = mix(color, color * cyberpunk_tint, glitch * 0.4); + + // Boost red channel + color.r *= 1.0 + glitch * 0.3; + + // CRT scanlines with red tint + float scanline = 1.0 - 0.12 + 0.12 * sin(uv.y * 450.0); + color *= scanline; + + // Neon edge glow + float edge = min(min(uv.x, 1.0 - uv.x), min(uv.y, 1.0 - uv.y)); + float glow = smoothstep(0.0, 0.15, edge); + vec3 neon_red = vec3(1.0, 0.1, 0.3); + color += neon_red * (1.0 - glow) * glitch * 1.0; + + return vec4(color, a * progress); + } + " + } + + window-close { + duration-ms 700 + curve "linear" + custom-shader r" + float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); } + + vec4 close_color(vec3 coords_geo, vec3 size_geo) { + if (coords_geo.x < 0.0 || coords_geo.x > 1.0 || coords_geo.y < 0.0 || coords_geo.y > 1.0) return vec4(0.0); + float progress = niri_clamped_progress; + vec2 uv = coords_geo.xy; + + // Progressive horizontal bars + float glitch_bar = step(0.85 - progress * 0.1, hash(vec2(floor(uv.y * 25.0), niri_random_seed + progress))); + float h_offset = glitch_bar * progress * 0.20 * (hash(vec2(uv.y, niri_random_seed)) - 0.5); + uv.x += h_offset; + + // Vertical tear effect + float tear = step(0.97, hash(vec2(floor(uv.x * 40.0), niri_random_seed))); + uv.y += tear * progress * 0.08 * (hash(vec2(uv.x, niri_random_seed)) - 0.5); + + // RGB split + float split = progress * 0.15; + vec3 cr = niri_geo_to_tex * vec3(uv + vec2(split * 1.5, split * 0.4), 1.0); + vec3 cg = niri_geo_to_tex * vec3(uv + vec2(0.0, progress * 0.02), 1.0); + vec3 cb = niri_geo_to_tex * vec3(uv - vec2(split, -split * 0.3), 1.0); + + float r = texture2D(niri_tex, cr.st).r; + float g = texture2D(niri_tex, cg.st).g; + float b = texture2D(niri_tex, cb.st).b; + float a = texture2D(niri_tex, cg.st).a; + vec3 color = vec3(r, g, b); + + // Static noise + float noise = hash(uv * 600.0 + niri_random_seed + progress * 10.0) * progress * 0.40; + color += noise; + + // Red/magenta corruption + vec3 cyberpunk_tint = vec3(1.0, 0.15, 0.35); + color = mix(color, color * cyberpunk_tint, progress * 0.5); + + // Boost red, crush other channels as it dies + color.r *= 1.0 + progress * 0.5; + color.g *= 1.0 - progress * 0.3; + color.b *= 1.0 - progress * 0.2; + + // Scanlines intensify + float scanline = 1.0 - (0.08 + progress * 0.1) + (0.08 + progress * 0.1) * sin(uv.y * 500.0); + color *= scanline; + + // Neon edge glow + float edge = min(min(uv.x, 1.0 - uv.x), min(uv.y, 1.0 - uv.y)); + float glow = smoothstep(0.0, 0.08, edge); + vec3 neon_red = vec3(1.0, 0.05, 0.25); + color += neon_red * (1.0 - glow) * progress * 1.2; + + // Pixel flicker + float hot_pixel = step(0.990, hash(uv * 300.0 + progress * 5.0)); + color += vec3(1.0, 0.3, 0.5) * hot_pixel * progress; + + return vec4(color, a * (1.0 - progress)); + } + " + } +} diff --git a/animations/glitch.kdl b/animations/glitch.kdl index b876b04..dddb6ca 100644 --- a/animations/glitch.kdl +++ b/animations/glitch.kdl @@ -4,11 +4,11 @@ // Adjustable parameters: // - split (0.04): RGB split intensity - higher = more separation // - scanline (0.08): Scanline visibility - higher = more prominent -// - uv.y * 400.0: Scanline density - higher = more lines +// - uv.y * 450.0: Scanline density - higher = more lines animations { window-open { - duration-ms 400 + duration-ms 700 curve "linear" custom-shader r" vec4 open_color(vec3 coords_geo, vec3 size_geo) { @@ -30,7 +30,7 @@ animations { vec3 color = vec3(r, g, b); // CRT scanline effect - float scanline = 1.0 - 0.08 + 0.08 * sin(uv.y * 400.0); + float scanline = 1.0 - 0.12 + 0.12 * sin(uv.y * 450.0); return vec4(color * scanline, a * progress); } @@ -38,7 +38,7 @@ animations { } window-close { - duration-ms 600 + duration-ms 1000 curve "linear" custom-shader r" vec4 close_color(vec3 coords_geo, vec3 size_geo) { @@ -59,7 +59,7 @@ animations { vec3 color = vec3(r, g, b); // CRT scanline effect - float scanline = 1.0 - 0.08 + 0.08 * sin(uv.y * 400.0); + float scanline = 1.0 - 0.12 + 0.12 * sin(uv.y * 450.0); return vec4(color * scanline, a * (1.0 - progress)); } diff --git a/animations/nirimation b/animations/nirimation new file mode 100755 index 0000000..70e1953 --- /dev/null +++ b/animations/nirimation @@ -0,0 +1,138 @@ +#!/usr/bin/env bash +# nirimation - Simple animation switcher for Niri +# Usage: nirimation [animation-name|list|current|help] + +ANIM_DIR="$(dirname "$(realpath "$0")")" +NIRI_CONFIG="$HOME/.config/niri/config.kdl" + +show_help() { + echo "nirimation - animation swapper for Niri" + echo "" + echo "Usage: nirimation [command|animation-name]" + echo "" + echo "Commands:" + echo " list List available animations" + echo " current Show currently active animation" + echo " -h, --help Show this help message" + echo " Switch to the named animation" + echo " none Disable animations (comments out include)" + echo "" + echo "With no arguments, shows the current animation." + echo "" + echo "Setup:" + echo " On first run, nirimation will automatically comment out any" + echo " existing 'animations { }' block in your niri config and add" + echo " an include line. A backup is saved as config.kdl.bak." +} + +list_animations() { + echo "Available animations:" + for f in "$ANIM_DIR"/*.kdl; do + name=$(basename "$f" .kdl) + if grep -q "include.*$name.kdl" "$NIRI_CONFIG" 2>/dev/null; then + echo " * $name (active)" + else + echo " $name" + fi + done +} + +show_current() { + current=$(grep -oP 'include.*?/\K[^/]+\.kdl' "$NIRI_CONFIG" 2>/dev/null | sed 's/.kdl//') + if [ -n "$current" ]; then + echo "Current: $current" + else + echo "No animation include found in niri config" + echo "Run 'nirimation ' to set one (see 'nirimation list')" + fi +} + +switch_animation() { + local name="$1" + local kdl_file="$ANIM_DIR/$name.kdl" + + if [ ! -f "$kdl_file" ]; then + echo "Error: Animation '$name' not found" + echo "Run 'nirimation list' to see available animations" + exit 1 + fi + + if ! [ -f "$NIRI_CONFIG" ]; then + echo "Error: Niri config not found at $NIRI_CONFIG" + exit 1 + fi + + # Check if an include line exists (active or commented out) + if grep -qP '//\s*include\s+".*\.kdl"' "$NIRI_CONFIG"; then + # Uncomment and replace + sed -i -E "s|//\s*include \".*\.kdl\"|include \"$kdl_file\"|" "$NIRI_CONFIG" + elif grep -qP 'include\s+".*\.kdl"' "$NIRI_CONFIG"; then + # Replace existing include line + sed -i -E "s|include \".*\.kdl\"|include \"$kdl_file\"|" "$NIRI_CONFIG" + else + # First time setup — back up config, comment out animations block, add include + cp "$NIRI_CONFIG" "$NIRI_CONFIG.bak" + + # Comment out existing animations { } block if present + if grep -qP '^\s*animations\s*\{' "$NIRI_CONFIG"; then + awk ' + /^\s*animations\s*\{/ && !inside { inside=1; depth=0 } + inside { + depth += gsub(/{/, "{") + print "// " $0 + depth -= gsub(/}/, "}") + if (depth <= 0) inside=0 + next + } + { print } + ' "$NIRI_CONFIG.bak" > "$NIRI_CONFIG" + echo "Commented out existing animations block in niri config" + fi + + echo "" >> "$NIRI_CONFIG" + echo "include \"$kdl_file\"" >> "$NIRI_CONFIG" + echo "Backup saved to $NIRI_CONFIG.bak" + fi + + # Reload niri + niri msg action load-config-file + echo "Switched to: $name" + + # Apply config.toml parameters to the KDL file + if [ -f "$ANIM_DIR/.venv/bin/python3" ]; then + "$ANIM_DIR/.venv/bin/python3" "$ANIM_DIR/nirimation-apply" "$name" 2>/dev/null + else + "$ANIM_DIR/nirimation-apply" "$name" 2>/dev/null + fi +} + +case "${1:-}" in + -h|--help|help) + show_help + ;; + list) + list_animations + ;; + current) + show_current + ;; + "") + show_current + ;; + none|off) + if ! [ -f "$NIRI_CONFIG" ]; then + echo "Error: Niri config not found at $NIRI_CONFIG" + exit 1 + fi + if grep -qP '^\s*include\s+".*\.kdl"' "$NIRI_CONFIG"; then + sed -i -E 's|^(\s*)(include ".*\.kdl")|\1// \2|' "$NIRI_CONFIG" + niri msg action load-config-file + echo "Animations disabled" + else + echo "No active animation include found" + fi + ;; + *) + switch_animation "$1" + ;; +esac diff --git a/animations/nirimation-apply b/animations/nirimation-apply new file mode 100755 index 0000000..cdceb07 --- /dev/null +++ b/animations/nirimation-apply @@ -0,0 +1,233 @@ +#!/usr/bin/env python3 +""" +nirimation-apply - Apply config.toml parameter values to animation KDL files + +Usage: + nirimation-apply Apply params for the active animation + nirimation-apply Set active animation and apply its params + nirimation-apply list List available animations + nirimation-apply current Show active animation from config.toml + +This tool tunes shader parameters (durations, colors, intensities) by +writing config.toml values into the KDL files. Use the 'nirimation' bash +script to switch which animation is active in niri's config. +""" + +import argparse +import os +import re +import sys +import tomllib +from pathlib import Path + +SCRIPT_DIR = Path(os.path.realpath(__file__)).parent +CONFIG_FILE = SCRIPT_DIR / "config.toml" + +# Parameter patterns: maps (animation_names, param_name) to (regex, replacement) +# Use {val} as placeholder for the config value +PARAM_PATTERNS = { + # Common to all animations + "*": { + "open_duration_ms": (r'(window-open\s*\{\s*\n\s*duration-ms\s+)\d+', + r'\g<1>{val}'), + "close_duration_ms": (r'(window-close\s*\{\s*\n\s*duration-ms\s+)\d+', + r'\g<1>{val}'), + }, + # Glitch variants + ("glitch", "glitch-cyberpunk"): { + "rgb_split": (r'(\* )([\d.]+)(;.*split)', + r'\g<1>{val}\3'), + "scanline_strength": (r'1\.0 - ([\d.]+) \+ ([\d.]+) \* sin', + r'1.0 - {val} + {val} * sin'), + "scanline_density": (r'(uv\.y \* )([\d.]+)', + r'\g<1>{val}'), + }, + # Glitch-cyberpunk extras + ("glitch-cyberpunk",): { + "noise_intensity": (r'(noise_intensity\s*=\s*)([\d.]+)', + r'\g<1>{val}'), + "tint_strength": (r'(tint_strength\s*=\s*)([\d.]+)', + r'\g<1>{val}'), + "glow_intensity": (r'(glow_intensity\s*=\s*)([\d.]+)', + r'\g<1>{val}'), + }, + # Burn family + ("burn", "burn-ashes", "burn-multicolor"): { + "edge_roughness": (r'(uv \* )([\d.]+)( \+.*random_seed)', + r'\g<1>{val}\3'), + "glow_width": (r'(threshold [+-] )([\d.]+)', + r'\g<1>{val}'), + "burn_reach": (r'(progress \* )([\d.]+)(;.*threshold)', + r'\g<1>{val}\3'), + }, + # Burn-ashes particles + ("burn-ashes",): { + "ash_size": (r'(ash_size\s*=\s*)([\d.]+)', + r'\g<1>{val}'), + "ash_layers": (r'(ash_layers\s*=\s*)([\d.]+)', + r'\g<1>{val}'), + "ember_size": (r'(ember_size\s*=\s*)([\d.]+)', + r'\g<1>{val}'), + "ember_layers": (r'(ember_layers\s*=\s*)([\d.]+)', + r'\g<1>{val}'), + }, + ("pixelate",): { + "pixel_size": (r'(pixel_size = )([\d.]+)', + r'\g<1>{val}'), + }, + ("fold-window",): { + "perspective": (r'(float perspective = )([\d.]+)', + r'\g<1>{val}'), + }, + ("pop-drop", "roll-drop"): { + "fall_distance": (r'(float fall_distance = )([\d.]+)', + r'\g<1>{val}'), + "max_rotation": (r'(float max_angle = )([\d.]+)', + r'\g<1>{val}'), + }, + ("ribbons",): { + "ribbon_count": (r'(float ribbon_count = )([\d.]+)', + r'\g<1>{val}'), + }, + ("unravel",): { + "edge_thickness": (r'(float edge_thickness = )([\d.]+)', + r'\g<1>{val}'), + }, +} + + +def load_config(): + with open(CONFIG_FILE, "rb") as f: + return tomllib.load(f) + + +def save_config_active(animation_name): + content = CONFIG_FILE.read_text() + new_content = re.sub( + r'^(active\s*=\s*)"[^"]*"', + f'\\1"{animation_name}"', + content, + flags=re.MULTILINE, + ) + CONFIG_FILE.write_text(new_content) + + +def get_available_animations(): + return sorted(f.stem for f in SCRIPT_DIR.glob("*.kdl")) + + +def get_patterns_for(animation_name): + """Collect all matching param patterns for an animation.""" + patterns = {} + for key, params in PARAM_PATTERNS.items(): + if key == "*" or (isinstance(key, str) and key == animation_name) or \ + (isinstance(key, tuple) and animation_name in key): + patterns.update(params) + return patterns + + +def apply_params(animation_name, config_params): + """Apply config.toml parameter values to the animation's KDL file.""" + kdl_path = SCRIPT_DIR / f"{animation_name}.kdl" + if not kdl_path.exists(): + print(f"Error: {kdl_path} not found") + return False + + content = kdl_path.read_text() + original = content + changes = [] + + patterns = get_patterns_for(animation_name) + + for param_name, (pattern, replacement) in patterns.items(): + if param_name in config_params: + val = config_params[param_name] + repl = replacement.replace("{val}", str(val)) + content, n = re.subn(pattern, repl, content) + if n > 0: + changes.append(f"{param_name} = {val}") + + if content != original: + kdl_path.write_text(content) + if changes: + print("Parameters applied:") + for change in changes: + print(f" {change}") + return True + + print("No parameters changed") + return False + + +def cmd_list(): + animations = get_available_animations() + config = load_config() + current = config["animation"]["active"] + print("Available animations:") + for anim in animations: + marker = " *" if anim == current else "" + print(f" {anim}{marker}") + print("\n (* = active in config.toml)") + + +def cmd_current(): + config = load_config() + print(config["animation"]["active"]) + + +def cmd_apply(animation_name=None): + config = load_config() + + if animation_name: + available = get_available_animations() + if animation_name not in available: + print(f"Error: Unknown animation '{animation_name}'") + print(f"Available: {', '.join(available)}") + sys.exit(1) + save_config_active(animation_name) + config = load_config() + + animation_name = config["animation"]["active"] + anim_params = config.get(animation_name, {}) + + if not anim_params: + print(f"No parameters found for '{animation_name}' in config.toml") + return + + apply_params(animation_name, anim_params) + + +def main(): + parser = argparse.ArgumentParser( + description="Apply config.toml parameter values to animation KDL files", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog="""\ +Examples: + nirimation-apply Apply params for current animation + nirimation-apply glitch Set active to glitch and apply its params + nirimation-apply list List available animations + nirimation-apply current Show active animation + +Use 'nirimation' (bash) to switch animations in niri's config.""", + ) + parser.add_argument( + "command", + nargs="?", + default="apply", + help="Animation name, 'list', or 'current'", + ) + + args = parser.parse_args() + + if args.command == "list": + cmd_list() + elif args.command == "current": + cmd_current() + elif args.command == "apply": + cmd_apply() + else: + cmd_apply(args.command) + + +if __name__ == "__main__": + main() diff --git a/animations/pixelate.kdl b/animations/pixelate.kdl index e7f7b73..546c4bf 100644 --- a/animations/pixelate.kdl +++ b/animations/pixelate.kdl @@ -4,7 +4,7 @@ animations { curve "ease-out-cubic" } window-open { - duration-ms 400 + duration-ms 700 curve "linear" custom-shader r" vec4 pixelate_open(vec3 coords_geo, vec3 size_geo) { @@ -39,7 +39,7 @@ animations { " } window-close { - duration-ms 400 + duration-ms 700 curve "linear" custom-shader r" vec4 pixelate_close(vec3 coords_geo, vec3 size_geo) { diff --git a/animations/pop-drop.kdl b/animations/pop-drop.kdl index aa208c4..94e89bf 100644 --- a/animations/pop-drop.kdl +++ b/animations/pop-drop.kdl @@ -8,7 +8,7 @@ animations { } window-open { - duration-ms 175 + duration-ms 700 curve "ease-out-quad" custom-shader r" vec4 zoom_in(vec3 coords_geo, vec3 size_geo) { @@ -31,7 +31,7 @@ animations { // Original by Lemmy: https://github.com/ItsLemmy window-close { - duration-ms 200 + duration-ms 700 curve "linear" custom-shader r" vec4 fall_and_rotate(vec3 coords_geo, vec3 size_geo) { diff --git a/animations/ribbons.kdl b/animations/ribbons.kdl index 0a4c524..43379e4 100644 --- a/animations/ribbons.kdl +++ b/animations/ribbons.kdl @@ -1,14 +1,18 @@ -//This was a nightmare to make but here +// This was a nightmare to make but here // original code from; https://github.com/XansiVA +// Fixed: use raw strings (r"...") for GLSL shaders to match all other +// animations, and replaced ternary int-to-float cast with mix/step to +// avoid "implicit cast from int to float" (GL error C7011) on strict +// GLSL drivers, which caused the shader to silently fail to compile. animations { workspace-switch { spring damping-ratio=0.80 stiffness=523 epsilon=0.0001 } window-open { - duration-ms 600 + duration-ms 700 curve "ease-out-quad" - custom-shader " + custom-shader r" vec4 open_color(vec3 coords_geo, vec3 size_geo) { float progress = niri_clamped_progress; @@ -28,11 +32,12 @@ window-open { float y_pos = rotated.y + 0.5; // Equal-sized ribbons (20 total) - float ribbon_count = 20.0; + float ribbon_count = 30.0; float ribbon_index = floor(y_pos * ribbon_count); // Alternating pattern: even = left, odd = right - float direction = mod(ribbon_index, 2.0) == 0.0 ? -1.0 : 1.0; + float is_even = step(mod(ribbon_index, 2.0), 0.5); + float direction = is_even * -2.0 + 1.0; // Cascading delay float delay = ribbon_index / ribbon_count * 0.5; @@ -63,9 +68,9 @@ window-open { window-close { - duration-ms 800 + duration-ms 700 curve "ease-out-quad" - custom-shader " + custom-shader r" vec4 close_color(vec3 coords_geo, vec3 size_geo) { float progress = niri_clamped_progress; @@ -85,11 +90,12 @@ window-close { float y_pos = rotated.y + 0.5; // Equal-sized ribbons (20 total) - float ribbon_count = 20.0; + float ribbon_count = 30.0; float ribbon_index = floor(y_pos * ribbon_count); // Alternating pattern: even = left, odd = right - float direction = mod(ribbon_index, 2.0) == 0.0 ? -1.0 : 1.0; + float is_even = step(mod(ribbon_index, 2.0), 0.5); + float direction = is_even * -2.0 + 1.0; // Cascading delay float delay = ribbon_index / ribbon_count * 0.5; diff --git a/animations/showcase/burn-ashes.mp4 b/animations/showcase/burn-ashes.mp4 index 5df6d29..f2f7bfe 100644 Binary files a/animations/showcase/burn-ashes.mp4 and b/animations/showcase/burn-ashes.mp4 differ diff --git a/animations/showcase/burn-multicolor.mp4 b/animations/showcase/burn-multicolor.mp4 new file mode 100644 index 0000000..257e2f7 Binary files /dev/null and b/animations/showcase/burn-multicolor.mp4 differ diff --git a/animations/showcase/burn.mp4 b/animations/showcase/burn.mp4 index 62751c1..9f029ca 100644 Binary files a/animations/showcase/burn.mp4 and b/animations/showcase/burn.mp4 differ diff --git a/animations/showcase/explode.mp4 b/animations/showcase/explode.mp4 new file mode 100644 index 0000000..f832ed6 Binary files /dev/null and b/animations/showcase/explode.mp4 differ diff --git a/animations/showcase/glitch-cyberpunk.mp4 b/animations/showcase/glitch-cyberpunk.mp4 new file mode 100644 index 0000000..3673e96 Binary files /dev/null and b/animations/showcase/glitch-cyberpunk.mp4 differ diff --git a/animations/showcase/glitch.mp4 b/animations/showcase/glitch.mp4 index 46dd017..e1c4ddb 100644 Binary files a/animations/showcase/glitch.mp4 and b/animations/showcase/glitch.mp4 differ diff --git a/animations/showcase/nirimation-swap-example.mp4 b/animations/showcase/nirimation-swap-example.mp4 new file mode 100644 index 0000000..b728013 Binary files /dev/null and b/animations/showcase/nirimation-swap-example.mp4 differ