Initial commit: video effects processing system

Add S-expression based video effects pipeline with modular effect
definitions, constructs, and recipe files.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
gilesb
2026-01-19 12:34:45 +00:00
commit 406cc7c0c7
171 changed files with 13406 additions and 0 deletions

164
effects/beam.py Normal file
View File

@@ -0,0 +1,164 @@
# /// script
# requires-python = ">=3.10"
# dependencies = ["numpy"]
# ///
"""
@effect beam
@version 1.0.0
@author artdag
@description
Beam effect. Creates animated light beams / lasers from a starting
point to an ending point with glow effect.
@param start_x float
@range 0 1
@default 0
Beam start X position (0-1).
@param start_y float
@range 0 1
@default 0.5
Beam start Y position (0-1).
@param end_x float
@range 0 1
@default 1
Beam end X position (0-1).
@param end_y float
@range 0 1
@default 0.5
Beam end Y position (0-1).
@param thickness float
@range 1 50
@default 5
Beam core thickness in pixels.
@param glow_radius float
@range 0 100
@default 20
Outer glow size in pixels.
@param color list
@default [0, 255, 255]
Beam color RGB (default cyan).
@param intensity float
@range 0 2
@default 1.0
Beam brightness.
@param pulse bool
@default false
Enable pulsing animation.
@param pulse_speed float
@range 0.1 10
@default 2.0
Pulse animation speed.
@example
(effect beam :start_x 0 :start_y 0.5 :end_x 1 :end_y 0.5)
@example
;; Reactive laser
(effect beam :intensity (bind bass :range [0.5 2]) :color [255 0 0])
"""
import numpy as np
def process_frame(frame: np.ndarray, params: dict, state: dict) -> tuple:
"""
Apply beam effect to a video frame.
Args:
frame: Input frame as numpy array (H, W, 3) RGB uint8
params: Effect parameters
state: Persistent state dict
Returns:
Tuple of (processed_frame, new_state)
"""
start_x = params.get("start_x", 0)
start_y = params.get("start_y", 0.5)
end_x = params.get("end_x", 1)
end_y = params.get("end_y", 0.5)
thickness = params.get("thickness", 5)
glow_radius = params.get("glow_radius", 20)
color = params.get("color", [0, 255, 255])
intensity = params.get("intensity", 1.0)
pulse = params.get("pulse", False)
pulse_speed = params.get("pulse_speed", 2.0)
t = params.get("_time", 0)
if state is None:
state = {}
h, w = frame.shape[:2]
result = frame.copy().astype(np.float32)
# Calculate beam endpoints in pixels
x1, y1 = int(start_x * w), int(start_y * h)
x2, y2 = int(end_x * w), int(end_y * h)
# Apply pulse modulation
if pulse:
pulse_mod = 0.5 + 0.5 * np.sin(t * pulse_speed * 2 * np.pi)
intensity = intensity * pulse_mod
# Create coordinate grids
y_coords, x_coords = np.mgrid[0:h, 0:w].astype(np.float32)
# Calculate distance from each pixel to the line segment
line_vec = np.array([x2 - x1, y2 - y1], dtype=np.float32)
line_len = np.sqrt(line_vec[0]**2 + line_vec[1]**2)
if line_len < 1:
return frame, state
line_unit = line_vec / line_len
# Vector from start to each pixel
px = x_coords - x1
py = y_coords - y1
# Project onto line
proj_len = px * line_unit[0] + py * line_unit[1]
proj_len = np.clip(proj_len, 0, line_len)
# Closest point on line
closest_x = x1 + proj_len * line_unit[0]
closest_y = y1 + proj_len * line_unit[1]
# Distance to closest point
dist = np.sqrt((x_coords - closest_x)**2 + (y_coords - closest_y)**2)
# Get beam color
if isinstance(color, (list, tuple)) and len(color) >= 3:
beam_color = np.array(color[:3], dtype=np.float32)
else:
beam_color = np.array([0, 255, 255], dtype=np.float32)
# Core beam (bright center)
core_mask = dist < thickness
core_intensity = intensity * (1 - dist[core_mask] / max(1, thickness))
for c in range(3):
result[core_mask, c] = np.clip(
result[core_mask, c] + beam_color[c] * core_intensity,
0, 255
)
# Glow (fading outer region)
glow_mask = (dist >= thickness) & (dist < thickness + glow_radius)
glow_dist = dist[glow_mask] - thickness
glow_intensity = intensity * 0.5 * (1 - glow_dist / max(1, glow_radius)) ** 2
for c in range(3):
result[glow_mask, c] = np.clip(
result[glow_mask, c] + beam_color[c] * glow_intensity,
0, 255
)
return result.astype(np.uint8), state