Add composable ASCII art with per-cell effects and explicit effect loading

Implements ascii_fx_zone effect that allows applying arbitrary sexp effects
to each character cell via cell_effect lambdas. Each cell is rendered as a
small image that effects can operate on.

Key changes:
- New ascii_fx_zone effect with cell_effect parameter for per-cell transforms
- Zone context (row, col, lum, sat, hue, etc.) available in cell_effect lambdas
- Effects are now loaded explicitly from recipe declarations, not auto-loaded
- Added effects_registry to plan for explicit effect dependency tracking
- Updated effect definition syntax across all sexp effects
- New run_staged.py for executing staged recipes
- Example recipes demonstrating alternating rotation and blur/rgb_split patterns

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
gilesb
2026-01-19 21:58:05 +00:00
parent 406cc7c0c7
commit 6ceaa37ab6
62 changed files with 2687 additions and 185 deletions

View File

@@ -1,14 +1,16 @@
;; 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)))
:params (
(char_size :type int :default 8 :range [4 32])
(alphabet :type string :default "standard")
(color_mode :type string :default "color" :desc ""color", "mono", "invert", or any color name/hex")
(background_color :type string :default "black" :desc "background color name/hex")
(invert_colors :type int :default 0 :desc "swap foreground and background colors")
(contrast :type float :default 1.5 :range [1 3])
)
(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)))
(render-char-grid frame chars colors char_size color_mode background_color invert_colors)))

View File

@@ -0,0 +1,51 @@
;; ASCII Art FX - converts image to ASCII characters with per-character effects
(define-effect ascii_art_fx
:params (
;; Basic parameters
(char_size :type int :default 8 :range [4 32]
:desc "Size of each character cell in pixels")
(alphabet :type string :default "standard"
:desc "Character set to use")
(color_mode :type string :default "color"
:choices [color mono invert]
:desc "Color mode: color, mono, invert, or any color name/hex")
(background_color :type string :default "black"
:desc "Background color name or hex value")
(invert_colors :type int :default 0 :range [0 1]
:desc "Swap foreground and background colors (0/1)")
(contrast :type float :default 1.5 :range [1 3]
:desc "Character selection contrast")
;; Per-character effects
(char_jitter :type float :default 0 :range [0 20]
:desc "Position jitter amount in pixels")
(char_scale :type float :default 1.0 :range [0.5 2.0]
:desc "Character scale factor")
(char_rotation :type float :default 0 :range [0 180]
:desc "Rotation amount in degrees")
(char_hue_shift :type float :default 0 :range [0 360]
:desc "Hue shift in degrees")
;; Modulation sources
(jitter_source :type string :default "none"
:choices [none luminance inv_luminance saturation position_x position_y position_diag random center_dist]
:desc "What drives jitter modulation")
(scale_source :type string :default "none"
:choices [none luminance inv_luminance saturation position_x position_y position_diag random center_dist]
:desc "What drives scale modulation")
(rotation_source :type string :default "none"
:choices [none luminance inv_luminance saturation position_x position_y position_diag random center_dist]
:desc "What drives rotation modulation")
(hue_source :type string :default "none"
:choices [none luminance inv_luminance saturation position_x position_y position_diag random center_dist]
:desc "What drives hue shift modulation")
)
(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-fx frame chars colors luminances char_size
color_mode background_color invert_colors
char_jitter char_scale char_rotation char_hue_shift
jitter_source scale_source rotation_source hue_source)))

View File

@@ -0,0 +1,99 @@
;; Composable ASCII Art with Per-Zone Expression-Driven Effects
;;
;; Two modes of operation:
;;
;; 1. EXPRESSION MODE: Use zone-* variables in expression parameters
;; Zone variables available:
;; zone-row, zone-col: Grid position (integers)
;; zone-row-norm, zone-col-norm: Normalized position (0-1)
;; zone-lum: Cell luminance (0-1)
;; zone-sat: Cell saturation (0-1)
;; zone-hue: Cell hue (0-360)
;; zone-r, zone-g, zone-b: RGB components (0-1)
;;
;; Example:
;; (ascii-fx-zone frame
;; :cols 80
;; :char_hue (* zone-lum 180)
;; :char_rotation (* zone-col-norm 30))
;;
;; 2. CELL EFFECT MODE: Pass a lambda to apply arbitrary effects per-cell
;; The lambda receives (cell-image zone-dict) and returns modified cell.
;; Zone dict contains: row, col, row-norm, col-norm, lum, sat, hue, r, g, b,
;; char, color, cell_size, plus any bound analysis values.
;;
;; Any loaded sexp effect can be called on cells - each cell is just a small frame:
;; (blur cell radius) - Gaussian blur
;; (rotate cell angle) - Rotate by angle degrees
;; (brightness cell factor) - Adjust brightness
;; (contrast cell factor) - Adjust contrast
;; (saturation cell factor) - Adjust saturation
;; (hue_shift cell degrees) - Shift hue
;; (rgb_split cell offset_x offset_y) - RGB channel split
;; (invert cell) - Invert colors
;; (pixelate cell block_size) - Pixelate
;; (wave cell amplitude freq) - Wave distortion
;; ... and any other loaded effect
;;
;; Example:
;; (ascii-fx-zone frame
;; :cols 60
;; :cell_effect (lambda [cell zone]
;; (blur (rotate cell (* (get zone "energy") 45))
;; (if (> (get zone "lum") 0.5) 3 0))))
(define-effect ascii_fx_zone
:params (
(cols :type int :default 80 :range [20 200]
:desc "Number of character columns")
(char_size :type int :default nil :range [4 32]
:desc "Character cell size in pixels (overrides cols if set)")
(alphabet :type string :default "standard"
:desc "Character set: standard, blocks, simple, digits, or custom string")
(color_mode :type string :default "color"
:desc "Color mode: color, mono, invert, or any color name/hex")
(background :type string :default "black"
:desc "Background color name or hex value")
(contrast :type float :default 1.5 :range [0.5 3.0]
:desc "Contrast for character selection")
(char_hue :type any :default nil
:desc "Hue shift expression (evaluated per-zone with zone-* vars)")
(char_saturation :type any :default nil
:desc "Saturation multiplier expression (1.0 = unchanged)")
(char_brightness :type any :default nil
:desc "Brightness multiplier expression (1.0 = unchanged)")
(char_scale :type any :default nil
:desc "Character scale expression (1.0 = normal size)")
(char_rotation :type any :default nil
:desc "Character rotation expression (degrees)")
(char_jitter :type any :default nil
:desc "Position jitter expression (pixels)")
(cell_effect :type any :default nil
:desc "Lambda (cell zone) -> cell for arbitrary per-cell effects")
;; Convenience params for staged recipes (avoids compile-time expression issues)
(energy :type float :default nil
:desc "Energy multiplier (0-1) from audio analysis bind")
(rotation_scale :type float :default 0
:desc "Max rotation at top-right when energy=1 (degrees)")
)
;; The ascii-fx-zone special form handles expression params
;; If energy + rotation_scale provided, it builds: energy * scale * position_factor
;; where position_factor = 0 at bottom-left, 3 at top-right
;; If cell_effect provided, each character is rendered to a cell image,
;; passed to the lambda, and the result composited back
(ascii-fx-zone frame
:cols cols
:char_size char_size
:alphabet alphabet
:color_mode color_mode
:background background
:contrast contrast
:char_hue char_hue
:char_saturation char_saturation
:char_brightness char_brightness
:char_scale char_scale
:char_rotation char_rotation
:char_jitter char_jitter
:cell_effect cell_effect
:energy energy
:rotation_scale rotation_scale))

View File

@@ -1,12 +1,13 @@
;; 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"))
:params (
(char_size :type int :default 8 :range [4 32])
(dark_threshold :type int :default 80 :range [0 128])
(bright_threshold :type int :default 180 :range [128 255])
(color_mode :type string :default "color")
)
(let* ((sample (cell-sample frame char_size))
(colors (nth sample 0))
(luminances (nth sample 1))

View File

@@ -8,7 +8,13 @@
;; 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)))
:params (
(mode :type string :default "overlay")
(opacity :type float :default 0.5)
(priority :type string :default "width")
(list :type string :default 0 0 0)
)
)
(let [a frame-a
a-w (width a)
a-h (height a)
@@ -45,4 +51,4 @@
b-resized))]
(if (= mode "alpha")
(blend-images a b opacity)
(blend-images a (blend-mode a b mode) opacity))))
(blend-images a (blend-mode a b mode) opacity)))

View File

@@ -1,10 +1,11 @@
;; 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))
:params (
(intensity :type float :default 0.5 :range [0 2])
(threshold :type int :default 200 :range [0 255])
(radius :type int :default 15 :range [1 50])
)
(let* ((bright (map-pixels frame
(lambda (x y c)
(if (> (luminance c) threshold)

View File

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

View File

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

View File

@@ -1,8 +1,11 @@
;; 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))
:params (
(brightness :type int :default 0 :range [-255 255] :desc "Brightness adjustment")
(contrast :type float :default 1 :range [0 3] :desc "Contrast multiplier")
(saturation :type float :default 1 :range [0 2] :desc "Saturation multiplier")
)
(-> frame
(adjust :brightness brightness :contrast contrast)
(shift-hsv :s saturation)))

View File

@@ -1,8 +1,9 @@
;; Color Cycle effect - animated hue rotation
;; @param speed float [0, 10] default 1
(define-effect color_cycle
((speed 1))
:params (
(speed :type int :default 1 :range [0 10])
)
(let ((shift (* t speed 360)))
(map-pixels frame
(lambda (x y c)

View File

@@ -1,7 +1,8 @@
;; 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))
:params (
(amount :type int :default 1 :range [0.5 3])
)
(adjust frame 0 amount))

View File

@@ -1,10 +1,11 @@
;; 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))
:params (
(line_spacing :type int :default 2 :range [1 10])
(line_opacity :type float :default 0.3 :range [0 1])
(vignette_amount :type float :default 0.2)
)
(let* ((w (width frame))
(h (height frame))
(cx (/ w 2))

View File

@@ -1,11 +1,12 @@
;; 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))
:params (
(block_size :type int :default 32 :range [8 128])
(corruption :type float :default 0.3 :range [0 1])
(max_offset :type int :default 50 :range [0 200])
(color_corrupt :type bool :default true)
)
;; Get previous frame from state, or use current frame if none
(let ((prev (state-get "prev_frame" frame)))
(begin

View File

@@ -1,9 +1,10 @@
;; 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))
:params (
(num_echoes :type int :default 4 :range [1 20])
(decay :type float :default 0.5 :range [0 1])
)
(let* ((buffer (state-get 'buffer (list)))
(new-buffer (take (cons frame buffer) (+ num_echoes 1))))
(begin

View File

@@ -1,7 +1,8 @@
;; 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))
:params (
(low :type int :default 50 :range [10 100])
(high :type int :default 150 :range [50 300])
)
(edges frame low high))

View File

@@ -1,9 +1,10 @@
;; 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))
:params (
(strength :type int :default 1 :range [0.5 3])
(blend :type float :default 0.3 :range [0 1])
)
(let* ((kernel (list (list (- strength) (- strength) 0)
(list (- strength) 1 strength)
(list 0 strength strength)))

View File

@@ -1,9 +1,10 @@
;; 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))
:params (
(intensity :type float :default 0.2 :range [0 1])
(colored :type bool :default false)
)
(let ((grain-amount (* intensity 50)))
(map-pixels frame
(lambda (x y c)

View File

@@ -1,11 +1,12 @@
;; 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))
:params (
(strength :type float :default 0.3 :range [-1 1])
(center_x :type float :default 0.5 :range [0 1])
(center_y :type float :default 0.5 :range [0 1])
(zoom_correct :type bool :default true)
)
(let* ((w (width frame))
(h (height frame))
(cx (* w center_x))

View File

@@ -1,9 +1,10 @@
;; Flip effect - flips image horizontally or vertically
;; @param horizontal bool default true
;; @param vertical bool default false
(define-effect flip
((horizontal true) (vertical false))
:params (
(horizontal :type bool :default true)
(vertical :type bool :default false)
)
(let ((result frame))
(if horizontal
(set! result (flip-h result))

View File

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

View File

@@ -1,9 +1,10 @@
;; 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))
:params (
(degrees :type int :default 0 :range [0 360])
(speed :type int :default 0 :desc "rotation per second")
)
(let ((shift (+ degrees (* speed t))))
(shift-hsv frame shift 1 1)))

View File

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

View File

@@ -1,13 +1,14 @@
;; 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))
:params (
(segments :type int :default 6 :range [3 16])
(rotation :type int :default 0 :range [0 360])
(rotation_speed :type int :default 0 :range [-180 180])
(center_x :type float :default 0.5 :range [0 1])
(center_y :type float :default 0.5 :range [0 1])
(zoom :type int :default 1 :range [0.5 3])
)
(let* ((w (width frame))
(h (height frame))
(cx (* w center_x))

View File

@@ -3,7 +3,12 @@
;; Params: x, y (position), opacity (0-1), mode (blend mode)
(define-effect layer
((x 0) (y 0) (opacity 1.0) (mode "alpha"))
:params (
(x :type int :default 0)
(y :type int :default 0)
(opacity :type float :default 1.0)
(mode :type string :default "alpha")
)
(let [bg (copy frame-a)
fg frame-b
;; Resize fg if needed to fit

View File

@@ -1,8 +1,9 @@
;; Mirror effect - mirrors half of image
;; @param mode string default "left_right"
(define-effect mirror
((mode "left_right"))
:params (
(mode :type string :default "left_right")
)
(let* ((w (width frame))
(h (height frame))
(hw (floor (/ w 2)))

View File

@@ -1,13 +1,13 @@
;; 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))
:params (
(edge_low :type int :default 50 :range [10 200])
(edge_high :type int :default 150 :range [50 300])
(glow_radius :type int :default 15 :range [1 50])
(glow_intensity :type int :default 2 :range [0.5 5])
(background :type float :default 0.3 :range [0 1])
)
(let* ((edge-img (edges frame edge_low edge_high))
(glow (blur edge-img glow_radius))
;; Intensify the glow

View File

@@ -1,7 +1,8 @@
;; 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))
:params (
(amount :type int :default 20 :range [0 100])
)
(add-noise frame amount))

View File

@@ -1,11 +1,12 @@
;; 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"))
:params (
(thickness :type int :default 2 :range [1 10])
(threshold :type int :default 100 :range [20 300])
(color :type list :default (list 0 0 0)
)
(fill_mode "original"))
(let* ((edge-img (edges frame (/ threshold 2) threshold))
(dilated (if (> thickness 1)
(dilate edge-img thickness)

View File

@@ -1,8 +1,9 @@
;; Pixelate effect - creates blocky pixels
;; @param block_size int [2, 64] default 8
(define-effect pixelate
((block_size 8))
:params (
(block_size :type int :default 8 :range [2 64])
)
(let* ((w (width frame))
(h (height frame))
(small-w (max 1 (floor (/ w block_size))))

View File

@@ -1,10 +1,11 @@
;; 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))
:params (
(sort_by :type string :default "lightness")
(threshold_low :type int :default 50 :range [0 255])
(threshold_high :type int :default 200 :range [0 255])
(angle :type int :default 0 :range [0 180])
(reverse :type bool :default false)
)
(pixelsort frame sort_by threshold_low threshold_high angle reverse))

View File

@@ -1,8 +1,9 @@
;; Posterize effect - reduces color levels
;; @param levels int [2, 32] default 8
(define-effect posterize
((levels 8))
:params (
(levels :type int :default 8 :range [2 32])
)
(let ((step (floor (/ 256 levels))))
(map-pixels frame
(lambda (x y c)

View File

@@ -1,7 +1,10 @@
;; 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"))
:params (
(target-w :type int :default 640 :desc "Target width in pixels")
(target-h :type int :default 480 :desc "Target height in pixels")
(mode :type string :default "linear" :choices [linear nearest area] :desc "Interpolation mode")
)
(resize frame target-w target-h mode))

View File

@@ -1,9 +1,10 @@
;; 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))
:params (
(offset_x :type int :default 10 :range [-50 50])
(offset_y :type int :default 0 :range [-50 50])
)
(let* ((r (channel frame 0))
(g (channel frame 1))
(b (channel frame 2))

View File

@@ -1,13 +1,14 @@
;; 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))
:params (
(frequency :type int :default 5 :range [1 20])
(amplitude :type int :default 10 :range [0 50])
(center_x :type float :default 0.5 :range [0 1])
(center_y :type float :default 0.5 :range [0 1])
(decay :type int :default 1 :range [0 5])
(speed :type int :default 1 :range [0 10])
)
(let* ((w (width frame))
(h (height frame))
(cx (* w center_x))

View File

@@ -1,8 +1,9 @@
;; 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))
:params (
(angle :type int :default 0 :range [-360 360])
(speed :type int :default 0 :desc "rotation per second")
)
(let ((total-angle (+ angle (* speed t))))
(rotate-img frame total-angle)))

View File

@@ -1,7 +1,8 @@
;; 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))
:params (
(amount :type int :default 1 :range [0 3])
)
(shift-hsv frame 0 amount 1))

View File

@@ -1,10 +1,11 @@
;; 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))
:params (
(amplitude :type int :default 10 :range [0 100])
(frequency :type int :default 10 :range [1 100])
(randomness :type float :default 0.5 :range [0 1])
)
(map-rows frame
(lambda (y row)
(let* ((sine-shift (* amplitude (sin (/ (* y 6.28) (max 1 frequency)))))

View File

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

View File

@@ -1,8 +1,9 @@
;; Sharpen effect - sharpens edges
;; @param amount float [0, 5] default 1
(define-effect sharpen
((amount 1))
:params (
(amount :type int :default 1 :range [0 5])
)
(let ((kernel (list (list 0 (- amount) 0)
(list (- amount) (+ 1 (* 4 amount)) (- amount))
(list 0 (- amount) 0))))

View File

@@ -1,8 +1,9 @@
;; Strobe effect - holds frames for choppy look
;; @param frame_rate float [1, 60] default 12
(define-effect strobe
((frame_rate 12))
:params (
(frame_rate :type int :default 12 :range [1 60])
)
(let* ((held (state-get 'held nil))
(held-until (state-get 'held-until 0))
(frame-duration (/ 1 frame_rate)))

View File

@@ -1,12 +1,13 @@
;; 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"))
:params (
(strength :type int :default 1 :range [-10 10])
(radius :type float :default 0.5 :range [0.1 2])
(center_x :type float :default 0.5 :range [0 1])
(center_y :type float :default 0.5 :range [0 1])
(falloff :type string :default "quadratic")
)
(let* ((w (width frame))
(h (height frame))
(cx (* w center_x))

View File

@@ -1,9 +1,10 @@
;; 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))
:params (
(level :type int :default 128 :range [0 255])
(invert :type bool :default false)
)
(map-pixels frame
(lambda (x y c)
(let* ((lum (luminance c))

View File

@@ -1,10 +1,11 @@
;; 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))
:params (
(rows :type int :default 2 :range [1 10])
(cols :type int :default 2 :range [1 10])
(gap :type int :default 0 :range [0 50])
)
(let* ((w (width frame))
(h (height frame))
(tile-w (floor (/ (- w (* gap (- cols 1))) cols)))

View File

@@ -1,8 +1,9 @@
;; Trails effect - persistent motion trails
;; @param persistence float [0, 0.99] default 0.8
(define-effect trails
((persistence 0.8))
:params (
(persistence :type float :default 0.8 :range [0 0.99])
)
(let* ((buffer (state-get 'buffer nil))
(current frame))
(if (= buffer nil)

View File

@@ -1,9 +1,10 @@
;; 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))
:params (
(strength :type float :default 0.5 :range [0 1])
(radius :type int :default 1 :range [0.5 2])
)
(let* ((w (width frame))
(h (height frame))
(cx (/ w 2))

View File

@@ -1,11 +1,12 @@
;; 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"))
:params (
(amplitude :type int :default 10 :range [0 100])
(wavelength :type int :default 50 :range [10 500])
(speed :type int :default 1 :range [0 10])
(direction :type string :default "horizontal")
)
(let* ((w (width frame))
(h (height frame))
;; Use _time for animation phase

View File

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