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

View File

@@ -0,0 +1,14 @@
;; ASCII Art effect - converts image to ASCII characters
;; @param char_size int [4, 32] default 8
;; @param alphabet string default "standard"
;; @param color_mode string default "color"
;; @param contrast float [1, 3] default 1.5
;; @param background list default (0 0 0)
(define-effect ascii_art
((char_size 8) (alphabet "standard") (color_mode "color") (contrast 1.5) (background (list 0 0 0)))
(let* ((sample (cell-sample frame char_size))
(colors (nth sample 0))
(luminances (nth sample 1))
(chars (luminance-to-chars luminances alphabet contrast)))
(render-char-grid frame chars colors char_size color_mode background)))

View File

@@ -0,0 +1,28 @@
;; ASCII Zones effect - different character sets for different brightness zones
;; Dark areas use simple chars, mid uses standard, bright uses blocks
;; @param char_size int [4, 32] default 8
;; @param dark_threshold int [0, 128] default 80
;; @param bright_threshold int [128, 255] default 180
;; @param color_mode string default "color"
(define-effect ascii_zones
((char_size 8) (dark_threshold 80) (bright_threshold 180) (color_mode "color"))
(let* ((sample (cell-sample frame char_size))
(colors (nth sample 0))
(luminances (nth sample 1))
;; Start with simple chars as base
(base-chars (luminance-to-chars luminances "simple" 1.2))
;; Map each cell to appropriate alphabet based on brightness zone
(zoned-chars (map-char-grid base-chars luminances
(lambda (r c ch lum)
(cond
;; Bright zones: use block characters
((> lum bright_threshold)
(alphabet-char "blocks" (floor (/ (- lum bright_threshold) 15))))
;; Dark zones: use simple sparse chars
((< lum dark_threshold)
(alphabet-char " .-" (floor (/ lum 30))))
;; Mid zones: use standard ASCII
(else
(alphabet-char "standard" (floor (/ lum 4)))))))))
(render-char-grid frame zoned-chars colors char_size color_mode (list 0 0 0))))

View File

@@ -0,0 +1,48 @@
;; Blend effect - combines two video streams
;; Multi-input effect: uses frame-a and frame-b
;; Params:
;; mode - blend mode (add, multiply, screen, overlay, difference, lighten, darken, alpha)
;; opacity - blend amount (0-1)
;; resize-mode - how to resize frame-b to match frame-a (fit, crop, stretch)
;; priority - which dimension takes priority (width, height)
;; pad-color - color for padding in fit mode [r g b]
(define-effect blend
((mode "overlay") (opacity 0.5) (resize-mode "fit") (priority "width") (pad-color (list 0 0 0)))
(let [a frame-a
a-w (width a)
a-h (height a)
b-raw frame-b
b-w (width b-raw)
b-h (height b-raw)
;; Calculate scale based on resize mode and priority
scale-w (/ a-w b-w)
scale-h (/ a-h b-h)
scale (if (= resize-mode "stretch")
1 ;; Will use explicit dimensions
(if (= resize-mode "crop")
(max scale-w scale-h) ;; Scale to cover, then crop
(if (= priority "width")
scale-w
scale-h)))
;; For stretch, use target dimensions directly
new-w (if (= resize-mode "stretch") a-w (round (* b-w scale)))
new-h (if (= resize-mode "stretch") a-h (round (* b-h scale)))
;; Resize b
b-resized (resize b-raw new-w new-h "linear")
;; Handle fit (pad) or crop to exact size
b (if (= resize-mode "crop")
;; Crop to center
(let [cx (/ (- new-w a-w) 2)
cy (/ (- new-h a-h) 2)]
(crop b-resized cx cy a-w a-h))
(if (and (= resize-mode "fit") (or (!= new-w a-w) (!= new-h a-h)))
;; Pad to center
(let [pad-x (/ (- a-w new-w) 2)
pad-y (/ (- a-h new-h) 2)
canvas (make-image a-w a-h pad-color)]
(paste canvas b-resized pad-x pad-y))
b-resized))]
(if (= mode "alpha")
(blend-images a b opacity)
(blend-images a (blend-mode a b mode) opacity))))

View File

@@ -0,0 +1,14 @@
;; Bloom effect - glow on bright areas
;; @param intensity float [0, 2] default 0.5
;; @param threshold int [0, 255] default 200
;; @param radius int [1, 50] default 15
(define-effect bloom
((intensity 0.5) (threshold 200) (radius 15))
(let* ((bright (map-pixels frame
(lambda (x y c)
(if (> (luminance c) threshold)
c
(rgb 0 0 0)))))
(blurred (blur bright radius)))
(blend-mode frame blurred "add")))

View File

@@ -0,0 +1,6 @@
;; Blur effect - gaussian blur
;; @param radius int [1, 50] default 5
(define-effect blur
((radius 5))
(blur frame (max 1 radius)))

View File

@@ -0,0 +1,7 @@
;; Brightness effect - adjusts overall brightness
;; @param amount float [-255, 255] default 0
;; Uses vectorized adjust primitive for fast processing
(define-effect brightness
((amount 0))
(adjust frame amount 1))

View File

@@ -0,0 +1,8 @@
;; Color adjustment effect - replaces TRANSFORM node
;; Params: brightness (-255 to 255), contrast (0 to 3+), saturation (0 to 2+)
(define-effect color-adjust
((brightness 0) (contrast 1) (saturation 1))
(-> frame
(adjust :brightness brightness :contrast contrast)
(shift-hsv :s saturation)))

View File

@@ -0,0 +1,11 @@
;; Color Cycle effect - animated hue rotation
;; @param speed float [0, 10] default 1
(define-effect color_cycle
((speed 1))
(let ((shift (* t speed 360)))
(map-pixels frame
(lambda (x y c)
(let* ((hsv (rgb->hsv c))
(new-h (mod (+ (first hsv) shift) 360)))
(hsv->rgb (list new-h (nth hsv 1) (nth hsv 2))))))))

View File

@@ -0,0 +1,7 @@
;; Contrast effect - adjusts image contrast
;; @param amount float [0.5, 3] default 1
;; Uses vectorized adjust primitive for fast processing
(define-effect contrast
((amount 1))
(adjust frame 0 amount))

View File

@@ -0,0 +1,28 @@
;; CRT effect - old monitor simulation
;; @param line_spacing int [1, 10] default 2
;; @param line_opacity float [0, 1] default 0.3
;; @param vignette float [0, 1] default 0.2
(define-effect crt
((line_spacing 2) (line_opacity 0.3) (vignette_amount 0.2))
(let* ((w (width frame))
(h (height frame))
(cx (/ w 2))
(cy (/ h 2))
(max-dist (sqrt (+ (* cx cx) (* cy cy)))))
(map-pixels frame
(lambda (x y c)
(let* (;; Scanline darkening
(scanline-factor (if (= 0 (mod y line_spacing))
(- 1 line_opacity)
1))
;; Vignette
(dx (- x cx))
(dy (- y cy))
(dist (sqrt (+ (* dx dx) (* dy dy))))
(vignette-factor (- 1 (* (/ dist max-dist) vignette_amount)))
;; Combined
(factor (* scanline-factor vignette-factor)))
(rgb (* (red c) factor)
(* (green c) factor)
(* (blue c) factor)))))))

View File

@@ -0,0 +1,13 @@
;; Datamosh effect - glitch block corruption
;; @param block_size int [8, 128] default 32
;; @param corruption float [0, 1] default 0.3
;; @param max_offset int [0, 200] default 50
;; @param color_corrupt bool default true
(define-effect datamosh
((block_size 32) (corruption 0.3) (max_offset 50) (color_corrupt true))
;; Get previous frame from state, or use current frame if none
(let ((prev (state-get "prev_frame" frame)))
(begin
(state-set "prev_frame" (copy frame))
(datamosh frame prev block_size corruption max_offset color_corrupt))))

View File

@@ -0,0 +1,17 @@
;; Echo effect - motion trails using frame buffer
;; @param num_echoes int [1, 20] default 4
;; @param decay float [0, 1] default 0.5
(define-effect echo
((num_echoes 4) (decay 0.5))
(let* ((buffer (state-get 'buffer (list)))
(new-buffer (take (cons frame buffer) (+ num_echoes 1))))
(begin
(state-set 'buffer new-buffer)
;; Blend frames with decay
(if (< (length new-buffer) 2)
frame
(let ((result (copy frame)))
;; Simple blend of first two frames for now
;; Full version would fold over all frames
(blend-images frame (nth new-buffer 1) (* decay 0.5)))))))

View File

@@ -0,0 +1,7 @@
;; Edge detection effect - highlights edges
;; @param low int [10, 100] default 50
;; @param high int [50, 300] default 150
(define-effect edge_detect
((low 50) (high 150))
(edges frame low high))

View File

@@ -0,0 +1,11 @@
;; Emboss effect - creates raised/3D appearance
;; @param strength float [0.5, 3] default 1
;; @param blend float [0, 1] default 0.3
(define-effect emboss
((strength 1) (blend 0.3))
(let* ((kernel (list (list (- strength) (- strength) 0)
(list (- strength) 1 strength)
(list 0 strength strength)))
(embossed (convolve frame kernel)))
(blend-images embossed frame blend)))

View File

@@ -0,0 +1,17 @@
;; Film Grain effect - adds film grain texture
;; @param intensity float [0, 1] default 0.2
;; @param colored bool default false
(define-effect film_grain
((intensity 0.2) (colored false))
(let ((grain-amount (* intensity 50)))
(map-pixels frame
(lambda (x y c)
(if colored
(rgb (clamp (+ (red c) (gaussian 0 grain-amount)) 0 255)
(clamp (+ (green c) (gaussian 0 grain-amount)) 0 255)
(clamp (+ (blue c) (gaussian 0 grain-amount)) 0 255))
(let ((n (gaussian 0 grain-amount)))
(rgb (clamp (+ (red c) n) 0 255)
(clamp (+ (green c) n) 0 255)
(clamp (+ (blue c) n) 0 255))))))))

View File

@@ -0,0 +1,14 @@
;; Fisheye effect - barrel/pincushion lens distortion
;; @param strength float [-1, 1] default 0.3
;; @param center_x float [0, 1] default 0.5
;; @param center_y float [0, 1] default 0.5
;; @param zoom_correct bool default true
(define-effect fisheye
((strength 0.3) (center_x 0.5) (center_y 0.5) (zoom_correct true))
(let* ((w (width frame))
(h (height frame))
(cx (* w center_x))
(cy (* h center_y))
(coords (fisheye-displace w h strength cx cy zoom_correct)))
(remap frame (coords-x coords) (coords-y coords))))

View File

@@ -0,0 +1,14 @@
;; Flip effect - flips image horizontally or vertically
;; @param horizontal bool default true
;; @param vertical bool default false
(define-effect flip
((horizontal true) (vertical false))
(let ((result frame))
(if horizontal
(set! result (flip-h result))
nil)
(if vertical
(set! result (flip-v result))
nil)
result))

View File

@@ -0,0 +1,5 @@
;; Grayscale effect - converts to grayscale
;; Uses vectorized mix-gray primitive for fast processing
(define-effect grayscale ()
(mix-gray frame 1))

View File

@@ -0,0 +1,9 @@
;; Hue shift effect - rotates hue values
;; @param degrees float [0, 360] default 0
;; @param speed float default 0 - rotation per second
;; Uses vectorized shift-hsv primitive for fast processing
(define-effect hue_shift
((degrees 0) (speed 0))
(let ((shift (+ degrees (* speed t))))
(shift-hsv frame shift 1 1)))

View File

@@ -0,0 +1,5 @@
;; Invert effect - inverts all colors
;; Uses vectorized invert-img primitive for fast processing
(define-effect invert ()
(invert-img frame))

View File

@@ -0,0 +1,18 @@
;; Kaleidoscope effect - mandala-like symmetry patterns
;; @param segments int [3, 16] default 6
;; @param rotation float [0, 360] default 0
;; @param rotation_speed float [-180, 180] default 0
;; @param center_x float [0, 1] default 0.5
;; @param center_y float [0, 1] default 0.5
;; @param zoom float [0.5, 3] default 1
(define-effect kaleidoscope
((segments 6) (rotation 0) (rotation_speed 0) (center_x 0.5) (center_y 0.5) (zoom 1))
(let* ((w (width frame))
(h (height frame))
(cx (* w center_x))
(cy (* h center_y))
;; Total rotation including time-based animation
(total_rot (+ rotation (* rotation_speed (or _time 0))))
(coords (kaleidoscope-displace w h segments total_rot cx cy zoom)))
(remap frame (coords-x coords) (coords-y coords))))

View File

@@ -0,0 +1,21 @@
;; Layer effect - composite frame-b over frame-a at position
;; Multi-input effect: uses frame-a (background) and frame-b (overlay)
;; Params: x, y (position), opacity (0-1), mode (blend mode)
(define-effect layer
((x 0) (y 0) (opacity 1.0) (mode "alpha"))
(let [bg (copy frame-a)
fg frame-b
;; Resize fg if needed to fit
fg-w (width fg)
fg-h (height fg)]
(if (= opacity 1.0)
;; Simple paste
(paste bg fg x y)
;; Blend with opacity
(let [blended (if (= mode "alpha")
(blend-images (crop bg x y fg-w fg-h) fg opacity)
(blend-images (crop bg x y fg-w fg-h)
(blend-mode (crop bg x y fg-w fg-h) fg mode)
opacity))]
(paste bg blended x y)))))

View File

@@ -0,0 +1,31 @@
;; Mirror effect - mirrors half of image
;; @param mode string default "left_right"
(define-effect mirror
((mode "left_right"))
(let* ((w (width frame))
(h (height frame))
(hw (floor (/ w 2)))
(hh (floor (/ h 2))))
(cond
((= mode "left_right")
(let ((left (crop frame 0 0 hw h))
(result (copy frame)))
(paste result (flip-h left) hw 0)))
((= mode "right_left")
(let ((right (crop frame hw 0 hw h))
(result (copy frame)))
(paste result (flip-h right) 0 0)))
((= mode "top_bottom")
(let ((top (crop frame 0 0 w hh))
(result (copy frame)))
(paste result (flip-v top) 0 hh)))
((= mode "bottom_top")
(let ((bottom (crop frame 0 hh w hh))
(result (copy frame)))
(paste result (flip-v bottom) 0 0)))
(else frame))))

View File

@@ -0,0 +1,22 @@
;; Neon Glow effect - glowing edge effect
;; @param edge_low int [10, 200] default 50
;; @param edge_high int [50, 300] default 150
;; @param glow_radius int [1, 50] default 15
;; @param glow_intensity float [0.5, 5] default 2
;; @param background float [0, 1] default 0.3
(define-effect neon_glow
((edge_low 50) (edge_high 150) (glow_radius 15)
(glow_intensity 2) (background 0.3))
(let* ((edge-img (edges frame edge_low edge_high))
(glow (blur edge-img glow_radius))
;; Intensify the glow
(bright-glow (map-pixels glow
(lambda (x y c)
(rgb (clamp (* (red c) glow_intensity) 0 255)
(clamp (* (green c) glow_intensity) 0 255)
(clamp (* (blue c) glow_intensity) 0 255))))))
(blend-mode (blend-images frame (make-image (width frame) (height frame) (list 0 0 0))
(- 1 background))
bright-glow
"screen")))

View File

@@ -0,0 +1,7 @@
;; Noise effect - adds random noise
;; @param amount float [0, 100] default 20
;; Uses vectorized add-noise primitive for fast processing
(define-effect noise
((amount 20))
(add-noise frame amount))

View File

@@ -0,0 +1,22 @@
;; Outline effect - shows only edges
;; @param thickness int [1, 10] default 2
;; @param threshold int [20, 300] default 100
;; @param color list default (0 0 0)
;; @param fill_mode string default "original"
(define-effect outline
((thickness 2) (threshold 100) (color (list 0 0 0)) (fill_mode "original"))
(let* ((edge-img (edges frame (/ threshold 2) threshold))
(dilated (if (> thickness 1)
(dilate edge-img thickness)
edge-img))
(base (cond
((= fill_mode "original") (copy frame))
((= fill_mode "white") (make-image (width frame) (height frame) (list 255 255 255)))
(else (make-image (width frame) (height frame) (list 0 0 0))))))
(map-pixels base
(lambda (x y c)
(let ((edge-val (luminance (pixel dilated x y))))
(if (> edge-val 128)
color
c))))))

View File

@@ -0,0 +1,11 @@
;; Pixelate effect - creates blocky pixels
;; @param block_size int [2, 64] default 8
(define-effect pixelate
((block_size 8))
(let* ((w (width frame))
(h (height frame))
(small-w (max 1 (floor (/ w block_size))))
(small-h (max 1 (floor (/ h block_size))))
(small (resize frame small-w small-h "area")))
(resize small w h "nearest")))

View File

@@ -0,0 +1,10 @@
;; Pixelsort effect - glitch art pixel sorting
;; @param sort_by string default "lightness"
;; @param threshold_low float [0, 255] default 50
;; @param threshold_high float [0, 255] default 200
;; @param angle float [0, 180] default 0
;; @param reverse bool default false
(define-effect pixelsort
((sort_by "lightness") (threshold_low 50) (threshold_high 200) (angle 0) (reverse false))
(pixelsort frame sort_by threshold_low threshold_high angle reverse))

View File

@@ -0,0 +1,11 @@
;; Posterize effect - reduces color levels
;; @param levels int [2, 32] default 8
(define-effect posterize
((levels 8))
(let ((step (floor (/ 256 levels))))
(map-pixels frame
(lambda (x y c)
(rgb (* (floor (/ (red c) step)) step)
(* (floor (/ (green c) step)) step)
(* (floor (/ (blue c) step)) step))))))

View File

@@ -0,0 +1,7 @@
;; Resize effect - replaces RESIZE node
;; Params: width, height, mode (linear, nearest, area)
;; Note: uses target-w/target-h to avoid conflict with width/height primitives
(define-effect resize-frame
((target-w 640) (target-h 480) (mode "linear"))
(resize frame target-w target-h mode))

View File

@@ -0,0 +1,12 @@
;; RGB Split effect - chromatic aberration
;; @param offset_x float [-50, 50] default 10
;; @param offset_y float [-50, 50] default 0
(define-effect rgb_split
((offset_x 10) (offset_y 0))
(let* ((r (channel frame 0))
(g (channel frame 1))
(b (channel frame 2))
(r-shifted (translate (merge-channels r r r) offset_x offset_y))
(b-shifted (translate (merge-channels b b b) (- offset_x) (- offset_y))))
(merge-channels (channel r-shifted 0) g (channel b-shifted 0))))

View File

@@ -0,0 +1,17 @@
;; Ripple effect - radial wave distortion from center
;; @param frequency float [1, 20] default 5
;; @param amplitude float [0, 50] default 10
;; @param center_x float [0, 1] default 0.5
;; @param center_y float [0, 1] default 0.5
;; @param decay float [0, 5] default 1
;; @param speed float [0, 10] default 1
(define-effect ripple
((frequency 5) (amplitude 10) (center_x 0.5) (center_y 0.5) (decay 1) (speed 1))
(let* ((w (width frame))
(h (height frame))
(cx (* w center_x))
(cy (* h center_y))
(phase (* (or _time 0) speed 2 pi))
(coords (ripple-displace w h frequency amplitude cx cy decay phase)))
(remap frame (coords-x coords) (coords-y coords))))

View File

@@ -0,0 +1,8 @@
;; Rotate effect - rotates image
;; @param angle float [-360, 360] default 0
;; @param speed float default 0 - rotation per second
(define-effect rotate
((angle 0) (speed 0))
(let ((total-angle (+ angle (* speed t))))
(rotate-img frame total-angle)))

View File

@@ -0,0 +1,7 @@
;; Saturation effect - adjusts color saturation
;; @param amount float [0, 3] default 1
;; Uses vectorized shift-hsv primitive for fast processing
(define-effect saturation
((amount 1))
(shift-hsv frame 0 amount 1))

View File

@@ -0,0 +1,13 @@
;; Scanlines effect - VHS-style horizontal line shifting
;; @param amplitude float [0, 100] default 10
;; @param frequency float [1, 100] default 10
;; @param randomness float [0, 1] default 0.5
(define-effect scanlines
((amplitude 10) (frequency 10) (randomness 0.5))
(map-rows frame
(lambda (y row)
(let* ((sine-shift (* amplitude (sin (/ (* y 6.28) (max 1 frequency)))))
(rand-shift (random (- amplitude) amplitude))
(shift (floor (lerp sine-shift rand-shift randomness))))
(roll row shift 0)))))

View File

@@ -0,0 +1,8 @@
;; Sepia effect - applies sepia tone
;; Classic warm vintage look
(define-effect sepia ()
(color-matrix frame
(list (list 0.393 0.769 0.189)
(list 0.349 0.686 0.168)
(list 0.272 0.534 0.131))))

View File

@@ -0,0 +1,9 @@
;; Sharpen effect - sharpens edges
;; @param amount float [0, 5] default 1
(define-effect sharpen
((amount 1))
(let ((kernel (list (list 0 (- amount) 0)
(list (- amount) (+ 1 (* 4 amount)) (- amount))
(list 0 (- amount) 0))))
(convolve frame kernel)))

View File

@@ -0,0 +1,14 @@
;; Strobe effect - holds frames for choppy look
;; @param frame_rate float [1, 60] default 12
(define-effect strobe
((frame_rate 12))
(let* ((held (state-get 'held nil))
(held-until (state-get 'held-until 0))
(frame-duration (/ 1 frame_rate)))
(if (or (= held nil) (>= t held-until))
(begin
(state-set 'held (copy frame))
(state-set 'held-until (+ t frame-duration))
frame)
held)))

View File

@@ -0,0 +1,15 @@
;; Swirl effect - spiral vortex distortion
;; @param strength float [-10, 10] default 1
;; @param radius float [0.1, 2] default 0.5
;; @param center_x float [0, 1] default 0.5
;; @param center_y float [0, 1] default 0.5
;; @param falloff string default "quadratic"
(define-effect swirl
((strength 1) (radius 0.5) (center_x 0.5) (center_y 0.5) (falloff "quadratic"))
(let* ((w (width frame))
(h (height frame))
(cx (* w center_x))
(cy (* h center_y))
(coords (swirl-displace w h strength radius cx cy falloff)))
(remap frame (coords-x coords) (coords-y coords))))

View File

@@ -0,0 +1,13 @@
;; Threshold effect - converts to black and white
;; @param level int [0, 255] default 128
;; @param invert bool default false
(define-effect threshold
((level 128) (invert false))
(map-pixels frame
(lambda (x y c)
(let* ((lum (luminance c))
(above (if invert (< lum level) (> lum level))))
(if above
(rgb 255 255 255)
(rgb 0 0 0))))))

View File

@@ -0,0 +1,27 @@
;; Tile Grid effect - tiles image in grid
;; @param rows int [1, 10] default 2
;; @param cols int [1, 10] default 2
;; @param gap int [0, 50] default 0
(define-effect tile_grid
((rows 2) (cols 2) (gap 0))
(let* ((w (width frame))
(h (height frame))
(tile-w (floor (/ (- w (* gap (- cols 1))) cols)))
(tile-h (floor (/ (- h (* gap (- rows 1))) rows)))
(tile (resize frame tile-w tile-h "area"))
(result (make-image w h (list 0 0 0))))
(begin
;; Manually place tiles using nested iteration
;; This is a simplified version - full version would loop
(paste result tile 0 0)
(if (> cols 1)
(paste result tile (+ tile-w gap) 0)
nil)
(if (> rows 1)
(paste result tile 0 (+ tile-h gap))
nil)
(if (and (> cols 1) (> rows 1))
(paste result tile (+ tile-w gap) (+ tile-h gap))
nil)
result)))

View File

@@ -0,0 +1,18 @@
;; Trails effect - persistent motion trails
;; @param persistence float [0, 0.99] default 0.8
(define-effect trails
((persistence 0.8))
(let* ((buffer (state-get 'buffer nil))
(current frame))
(if (= buffer nil)
(begin
(state-set 'buffer (copy frame))
frame)
(let* ((faded (blend-images buffer
(make-image (width frame) (height frame) (list 0 0 0))
(- 1 persistence)))
(result (blend-mode faded current "lighten")))
(begin
(state-set 'buffer result)
result)))))

View File

@@ -0,0 +1,21 @@
;; Vignette effect - darkens corners
;; @param strength float [0, 1] default 0.5
;; @param radius float [0.5, 2] default 1
(define-effect vignette
((strength 0.5) (radius 1))
(let* ((w (width frame))
(h (height frame))
(cx (/ w 2))
(cy (/ h 2))
(max-dist (* (sqrt (+ (* cx cx) (* cy cy))) radius)))
(map-pixels frame
(lambda (x y c)
(let* ((dx (- x cx))
(dy (- y cy))
(dist (sqrt (+ (* dx dx) (* dy dy))))
(factor (- 1 (* (/ dist max-dist) strength)))
(factor (clamp factor 0 1)))
(rgb (* (red c) factor)
(* (green c) factor)
(* (blue c) factor)))))))

View File

@@ -0,0 +1,20 @@
;; Wave effect - sine wave displacement distortion
;; @param amplitude float [0, 100] default 10
;; @param wavelength float [10, 500] default 50
;; @param speed float [0, 10] default 1
;; @param direction string default "horizontal"
(define-effect wave
((amplitude 10) (wavelength 50) (speed 1) (direction "horizontal"))
(let* ((w (width frame))
(h (height frame))
;; Use _time for animation phase
(phase (* (or _time 0) speed 2 pi))
;; Calculate frequency: waves per dimension
(freq (/ (if (= direction "vertical") w h) wavelength))
(axis (cond
((= direction "horizontal") "x")
((= direction "vertical") "y")
(else "both")))
(coords (wave-displace w h axis freq amplitude phase)))
(remap frame (coords-x coords) (coords-y coords))))

View File

@@ -0,0 +1,6 @@
;; Zoom effect - zooms in/out from center
;; @param amount float [0.1, 5] default 1
(define-effect zoom
((amount 1))
(scale-img frame amount amount))