Gallery
Every example renders from code — what you see is what the data describes.
Artistic Expression

Calligraphy Flow
Animated variable-width calligraphic strokes.
View source
(defn ^{:example {:output "art-calligraphy-flow.gif"
:title "Calligraphy Flow"
:desc "Animated variable-width calligraphic strokes."}}
calligraphy-flow []
{:frames
(anim/frames 60
(fn [t]
(let [wave-y (fn [x] (* 40 (Math/sin (+ (* x 0.015) (* t 2 Math/PI)))))
paths (for [i (range 5)]
(let [base-y (+ 80 (* i 70))
pts (for [x (range 20 581 10)]
[x (+ base-y (wave-y (+ x (* i 50))))])]
{:node/type :shape/path
:path/commands (into [[:move-to (first pts)]]
(mapv (fn [p] [:line-to p]) (rest pts)))
:stroke/profile :pointed
:style/stroke {:color (nth (:sunset palette/palettes) (mod i 5))
:width (+ 6 (* 2 i))}}))]
{:image/size [600 450]
:image/background [:color/rgb 25 20 30]
:image/nodes (vec paths)})))
:fps 30})
Chromatic Scatter
Per-instance color variation driven by noise.
View source
(defn ^{:example {:output "art-chromatic-scatter.png"
:title "Chromatic Scatter"
:desc "Per-instance color variation driven by noise."}}
chromatic-scatter []
(let [pts (scatter/poisson-disk 30 30 540 340 18 42)
stops [[0.0 [:color/rgb 20 0 80]]
[0.3 [:color/rgb 180 0 100]]
[0.6 [:color/rgb 255 120 0]]
[1.0 [:color/rgb 255 230 80]]]
overrides (vary/by-noise pts 0.012 42
(fn [v]
(let [t (+ 0.5 (* 0.5 v))]
{:style/fill (palette/gradient-map stops t)
:node/opacity (+ 0.5 (* 0.5 t))})))]
{:image/size [600 400]
:image/background [:color/rgb 10 8 20]
:image/nodes
[{:node/type :scatter
:scatter/shape {:node/type :shape/circle
:circle/center [0.0 0.0]
:circle/radius 7.0}
:scatter/positions pts
:scatter/overrides overrides}]}))
Decorative Frame
Path decorators, hatching, and stipple accents.
View source
(defn ^{:example {:output "art-decorative-frame.png"
:title "Decorative Frame"
:desc "Path decorators, hatching, and stipple accents."}}
decorative-frame []
{:image/size [400 400]
:image/background [:color/rgb 250 245 235]
:image/nodes
[{:node/type :shape/rect
:rect/xy [80.0 80.0]
:rect/size [240.0 240.0]
:style/fill {:fill/type :hatch
:hatch/layers [{:angle 30 :spacing 12}
{:angle -30 :spacing 12}]
:hatch/stroke-width 0.5
:hatch/color [:color/rgb 180 160 130]
:hatch/background [:color/rgb 250 245 235]}}
{:node/type :path/decorated
:path/commands [[:move-to [50.0 50.0]]
[:line-to [350.0 50.0]]
[:line-to [350.0 350.0]]
[:line-to [50.0 350.0]]
[:line-to [50.0 50.0]]]
:decorator/shape (assoc (scene/regular-polygon [0.0 0.0] 8 4)
:style/fill [:color/rgb 120 80 40])
:decorator/spacing 25
:decorator/rotate? true}
{:node/type :shape/circle
:circle/center [50.0 50.0]
:circle/radius 15.0
:style/fill {:fill/type :stipple
:stipple/density 0.8 :stipple/radius 0.8 :stipple/seed 42
:stipple/color [:color/rgb 120 80 40]
:stipple/background [:color/rgb 250 245 235]}
:style/stroke {:color [:color/rgb 120 80 40] :width 1.5}}
{:node/type :shape/circle
:circle/center [350.0 50.0]
:circle/radius 15.0
:style/fill {:fill/type :stipple
:stipple/density 0.8 :stipple/radius 0.8 :stipple/seed 43
:stipple/color [:color/rgb 120 80 40]
:stipple/background [:color/rgb 250 245 235]}
:style/stroke {:color [:color/rgb 120 80 40] :width 1.5}}
{:node/type :shape/circle
:circle/center [50.0 350.0]
:circle/radius 15.0
:style/fill {:fill/type :stipple
:stipple/density 0.8 :stipple/radius 0.8 :stipple/seed 44
:stipple/color [:color/rgb 120 80 40]
:stipple/background [:color/rgb 250 245 235]}
:style/stroke {:color [:color/rgb 120 80 40] :width 1.5}}
{:node/type :shape/circle
:circle/center [350.0 350.0]
:circle/radius 15.0
:style/fill {:fill/type :stipple
:stipple/density 0.8 :stipple/radius 0.8 :stipple/seed 45
:stipple/color [:color/rgb 120 80 40]
:stipple/background [:color/rgb 250 245 235]}
:style/stroke {:color [:color/rgb 120 80 40] :width 1.5}}]})
Flow Mandala
Animated flow field with radial symmetry.
View source
(defn ^{:example {:output "art-flow-mandala.gif"
:title "Flow Mandala"
:desc "Animated flow field with radial symmetry."}}
flow-mandala []
{:frames
(anim/frames 40
(fn [t]
(let [paths (flow/flow-field 0 0 400 400
{:density 14 :steps 30 :step-length 2
:noise-scale (+ 0.004 (* 0.002 (Math/sin (* t 2 Math/PI))))
:seed 42})]
{:image/size [400 400]
:image/background [:color/rgb 10 10 20]
:image/nodes
[{:node/type :symmetry
:symmetry/type :radial
:symmetry/n 6
:symmetry/center [200 200]
:group/children
(vec (map-indexed
(fn [i path]
(-> path
(assoc :style/stroke
{:color (palette/gradient-map
[[0.0 [:color/rgb 80 40 200]]
[0.5 [:color/rgb 200 50 150]]
[1.0 [:color/rgb 255 200 80]]]
(mod (* i 0.03) 1.0))
:width 0.6})
(assoc :node/opacity
(+ 0.4 (* 0.4 (Math/sin (+ (* t 4 Math/PI) (* i 0.2))))))))
paths))}]})))
:fps 30})
Fractal Forest
L-system trees with distortion and brush strokes.
View source
(defn ^{:example {:output "art-fractal-forest.png"
:title "Fractal Forest"
:desc "L-system trees with distortion and brush strokes."}}
fractal-forest []
{:image/size [600 400]
:image/background [:color/rgb 220 230 210]
:image/nodes
[{:node/type :shape/rect
:rect/xy [0.0 340.0]
:rect/size [600.0 60.0]
:style/fill [:color/rgb 80 60 40]}
{:node/type :lsystem
:lsystem/axiom "F"
:lsystem/rules {"F" "FF+[+F-F-F]-[-F+F+F]"}
:lsystem/iterations 4
:lsystem/angle 22.5
:lsystem/length 3.5
:lsystem/origin [150 340]
:lsystem/heading -90.0
:style/stroke {:color [:color/rgb 60 40 20] :width 1}
:node/transform [[:transform/distort {:type :roughen :amount 1.5 :seed 42}]]}
{:node/type :lsystem
:lsystem/axiom "F"
:lsystem/rules {"F" "F[+F]F[-F][F]"}
:lsystem/iterations 4
:lsystem/angle 20.0
:lsystem/length 4.0
:lsystem/origin [350 340]
:lsystem/heading -90.0
:style/stroke {:color [:color/rgb 40 60 25] :width 1}
:node/transform [[:transform/distort {:type :roughen :amount 1 :seed 99}]]}
{:node/type :lsystem
:lsystem/axiom "F"
:lsystem/rules {"F" "F[-F]F[+F]F"}
:lsystem/iterations 4
:lsystem/angle 25.7
:lsystem/length 3.0
:lsystem/origin [500 340]
:lsystem/heading -90.0
:style/stroke {:color [:color/rgb 50 80 30] :width 0.8}
:node/transform [[:transform/distort {:type :roughen :amount 1.2 :seed 77}]]}]})
Ink Landscape
Hatching, path distortion, and calligraphic strokes.
View source
(defn ^{:example {:output "art-ink-landscape.png"
:title "Ink Landscape"
:desc "Hatching, path distortion, and calligraphic strokes."}}
ink-landscape []
(let [mountain-path [[:move-to [0.0 300.0]]
[:line-to [50.0 250.0]]
[:line-to [120.0 180.0]]
[:line-to [180.0 120.0]]
[:line-to [220.0 90.0]]
[:line-to [260.0 110.0]]
[:line-to [300.0 140.0]]
[:line-to [340.0 100.0]]
[:line-to [380.0 80.0]]
[:line-to [420.0 95.0]]
[:line-to [480.0 150.0]]
[:line-to [530.0 200.0]]
[:line-to [580.0 240.0]]
[:line-to [600.0 260.0]]
[:line-to [600.0 400.0]]
[:line-to [0.0 400.0]]
[:close]]
sun-rays (for [i (range 16)]
(let [angle (* i (/ Math/PI 8))
cx 480.0 cy 80.0
r1 35.0 r2 60.0]
{:node/type :shape/path
:path/commands [[:move-to [(+ cx (* r1 (Math/cos angle)))
(+ cy (* r1 (Math/sin angle)))]]
[:line-to [(+ cx (* r2 (Math/cos angle)))
(+ cy (* r2 (Math/sin angle)))]]]
:style/stroke {:color [:color/rgb 40 30 20] :width 1.5}}))]
{:image/size [600 400]
:image/background [:color/rgb 245 235 220]
:image/nodes
(into
[{:node/type :shape/circle
:circle/center [480.0 80.0]
:circle/radius 30.0
:style/fill {:fill/type :hatch
:hatch/angle 0
:hatch/spacing 3
:hatch/stroke-width 0.8
:hatch/color [:color/rgb 40 30 20]
:hatch/background [:color/rgb 245 235 220]}
:style/stroke {:color [:color/rgb 40 30 20] :width 1.5}}]
(concat
sun-rays
[{:node/type :shape/path
:path/commands mountain-path
:style/fill {:fill/type :hatch
:hatch/layers [{:angle 45 :spacing 6}
{:angle -30 :spacing 8}]
:hatch/stroke-width 0.7
:hatch/color [:color/rgb 40 30 20]}
:style/stroke {:color [:color/rgb 30 20 10] :width 2}
:node/transform [[:transform/distort {:type :roughen :amount 2 :seed 42}]]}
{:node/type :shape/path
:path/commands [[:move-to [0.0 350.0]]
[:curve-to [100.0 330.0] [200.0 310.0] [300.0 320.0]]
[:curve-to [400.0 330.0] [500.0 340.0] [600.0 330.0]]]
:stroke/profile :brush
:style/stroke {:color [:color/rgb 30 20 10] :width 8}}]))}))
Landscape Typography
Voronoi mosaic sunset inside text clip mask.
View source
(defn ^{:example {:output "art-landscape-type.png"
:title "Landscape Typography"
:desc "Voronoi mosaic sunset inside text clip mask."}}
landscape-type []
(let [pts (scatter/poisson-disk -30 -240 640 290 35 42)
cells (voronoi/voronoi-cells pts -30 -240 640 290)
colored-cells (vec (map-indexed
(fn [i cell]
(let [[_ py] (nth pts i)
t (/ (+ py 240.0) 290.0)
color (palette/gradient-map
[[0.0 [:color/rgb 10 10 50]]
[0.25 [:color/rgb 60 20 100]]
[0.5 [:color/rgb 200 60 40]]
[0.75 [:color/rgb 255 160 30]]
[1.0 [:color/rgb 255 240 180]]]
t)]
(assoc cell :style/fill color)))
cells))]
{:image/size [600 300]
:image/background [:color/rgb 10 10 20]
:image/nodes
[(scene/text-clip "EIDO"
{:font/family "SansSerif" :font/size 220 :font/weight :bold}
[30 230]
(conj colored-cells
{:node/type :scatter
:scatter/shape {:node/type :shape/circle
:circle/center [0.0 0.0]
:circle/radius 2.0
:style/fill [:color/rgb 255 255 230]}
:scatter/positions (scatter/poisson-disk -20 -230 630 120 22 77)
:scatter/jitter {:x 2 :y 2 :seed 33}}))]}))
Mandala
Radial symmetry with hatching and stippling.
View source
(defn ^{:example {:output "art-mandala.png"
:title "Mandala"
:desc "Radial symmetry with hatching and stippling."}}
mandala []
{:image/size [600 600]
:image/background [:color/rgb 245 235 215]
:image/nodes
[{:node/type :symmetry
:symmetry/type :radial
:symmetry/n 12
:symmetry/center [300 300]
:group/children
[{:node/type :shape/path
:path/commands [[:move-to [300.0 290.0]]
[:curve-to [330.0 200.0] [360.0 120.0] [300.0 50.0]]
[:curve-to [240.0 120.0] [270.0 200.0] [300.0 290.0]]]
:style/fill {:fill/type :hatch
:hatch/angle 60
:hatch/spacing 4
:hatch/stroke-width 0.6
:hatch/color [:color/rgb 140 60 30]}
:style/stroke {:color [:color/rgb 140 60 30] :width 0.8}}
{:node/type :shape/path
:path/commands [[:move-to [300.0 270.0]]
[:curve-to [315.0 200.0] [325.0 140.0] [300.0 80.0]]]
:stroke/profile :pointed
:style/stroke {:color [:color/rgb 180 80 30] :width 6}}]}
{:node/type :shape/circle
:circle/center [300.0 300.0]
:circle/radius 40.0
:style/fill {:fill/type :stipple
:stipple/density 0.6
:stipple/radius 1.0
:stipple/seed 42
:stipple/color [:color/rgb 140 60 30]
:stipple/background [:color/rgb 245 235 215]}
:style/stroke {:color [:color/rgb 140 60 30] :width 2}}]})
Noise Garden
Animated swaying flowers driven by noise.
View source
(defn ^{:example {:output "art-noise-garden.gif"
:title "Noise Garden"
:desc "Animated swaying flowers driven by noise."}}
noise-garden []
(let [pal (:forest palette/palettes)]
{:frames
(anim/frames 60
(fn [t]
(let [flowers
(for [i (range 40)]
(let [bx (+ 40 (mod (* i 137.508 1.1) 520))
by (+ 60 (mod (* i 97.3 1.3) 250))
sway (* 8 (Math/sin (+ (* t 2 Math/PI) (* i 0.7))))
rng (java.util.Random. (long (+ i 100)))
r (+ 5 (* (.nextDouble rng) 15))
color-idx (mod i 5)]
{:node/type :shape/circle
:circle/center [(+ bx sway) by]
:circle/radius r
:style/fill (nth pal color-idx)
:node/opacity (+ 0.6 (* 0.4 (Math/sin (+ (* t 4 Math/PI) i))))}))
stems
(for [i (range 40)]
(let [bx (+ 40 (mod (* i 137.508 1.1) 520))
by (+ 60 (mod (* i 97.3 1.3) 250))
sway (* 8 (Math/sin (+ (* t 2 Math/PI) (* i 0.7))))
rng (java.util.Random. (long (+ i 100)))]
{:node/type :shape/path
:path/commands [[:move-to [(+ bx sway) by]]
[:line-to [bx (+ by 40 (* (.nextDouble rng) 60))]]]
:style/stroke {:color (nth pal 0) :width 1.5}}))]
{:image/size [600 400]
:image/background [:color/rgb 250 248 240]
:image/nodes (vec (concat stems flowers))})))
:fps 30}))
Organic Mandala
L-system branches with radial symmetry and per-instance color.
View source
(defn ^{:example {:output "art-organic-mandala.png"
:title "Organic Mandala"
:desc "L-system branches with radial symmetry and per-instance color."}}
organic-mandala []
(let [branch-cmds (lsystem/lsystem->path-cmds
"F" {"F" "F[-F]F[+F]F"} 3 25.7 8.0 [300 300] -90.0)
n 10
overrides (vary/by-index n
(fn [i] {:style/stroke
{:color (palette/gradient-map
[[0.0 [:color/rgb 180 60 30]]
[0.5 [:color/rgb 255 180 50]]
[1.0 [:color/rgb 60 180 80]]]
(/ (double i) n))
:width 0.8}}))]
{:image/size [600 600]
:image/background [:color/rgb 15 12 20]
:image/nodes
[{:node/type :symmetry
:symmetry/type :radial
:symmetry/n n
:symmetry/center [300 300]
:symmetry/overrides overrides
:group/children
[{:node/type :shape/path
:path/commands branch-cmds
:style/stroke {:color [:color/rgb 200 100 50] :width 0.8}}]}]}))
Polka Pop
Pop art polka dot circles with pattern fills.
View source
(defn ^{:example {:output "art-polka-pop.png"
:title "Polka Pop"
:desc "Pop art polka dot circles with pattern fills."}}
polka-pop []
(let [pal (:neon palette/palettes)]
{:image/size [400 400]
:image/background [:color/rgb 20 20 20]
:image/nodes
(vec (map-indexed
(fn [i [x y r]]
{:node/type :shape/circle
:circle/center [x y]
:circle/radius r
:style/fill {:fill/type :pattern
:pattern/size [14 14]
:pattern/nodes
[{:node/type :shape/rect
:rect/xy [0.0 0.0]
:rect/size [14.0 14.0]
:style/fill (nth pal (mod i 5))}
{:node/type :shape/circle
:circle/center [7.0 7.0]
:circle/radius 3.0
:style/fill [:color/rgb 20 20 20]}]}
:style/stroke {:color [:color/rgb 255 255 255] :width 3}
:effect/shadow {:dx 5 :dy 5 :blur 10
:color [:color/rgb 0 0 0]
:opacity 0.5}})
[[120.0 150.0 80.0]
[280.0 120.0 60.0]
[200.0 280.0 90.0]
[330.0 300.0 45.0]
[80.0 320.0 40.0]]))}))
Risograph
Posterize filter, grain, and bold overlapping shapes.
View source
(defn ^{:example {:output "art-risograph.png"
:title "Risograph"
:desc "Posterize filter, grain, and bold overlapping shapes."}}
risograph []
{:image/size [400 400]
:image/background [:color/rgb 240 235 220]
:image/nodes
[{:node/type :group
:group/composite :src-over
:group/filter [:grain 0.15 42]
:group/children
[{:node/type :group
:group/composite :src-over
:group/filter [:posterize 4]
:group/children
[{:node/type :shape/circle
:circle/center [200.0 200.0]
:circle/radius 160.0
:style/fill [:color/rgb 230 80 60]}
{:node/type :shape/rect
:rect/xy [120.0 100.0]
:rect/size [180.0 200.0]
:style/fill [:color/rgba 40 80 180 0.7]}
{:node/type :shape/circle
:circle/center [250.0 250.0]
:circle/radius 100.0
:style/fill [:color/rgba 255 200 40 0.6]}]}]}]})
Shape Breath
Morphing circle to star with color shift.
View source
(defn ^{:example {:output "art-shape-breath.gif"
:title "Shape Breath"
:desc "Morphing circle to star with color shift."}}
shape-breath []
(let [circle-cmds (:path/commands (scene/regular-polygon [200.0 200.0] 140.0 60))
star-cmds (:path/commands (scene/star [200.0 200.0] 170.0 65.0 8))]
{:frames
(anim/frames 40
(fn [t]
(let [t-morph (anim/ping-pong t)
eased (anim/ease-in-out-cubic t-morph)
cmds (morph/morph-auto circle-cmds star-cmds eased)
color (palette/gradient-map
[[0.0 [:color/rgb 50 100 220]]
[1.0 [:color/rgb 230 80 80]]]
eased)]
{:image/size [400 400]
:image/background [:color/rgb 15 15 25]
:image/nodes
[{:node/type :shape/path
:path/commands cmds
:style/fill color
:effect/glow {:blur 15
:color color
:opacity 0.3}}]})))
:fps 24}))
Stained Glass
Voronoi cells with colored fills and dark outlines.
View source
(defn ^{:example {:output "art-stained-glass.png"
:title "Stained Glass"
:desc "Voronoi cells with colored fills and dark outlines."}}
stained-glass []
(let [pts (scatter/poisson-disk 20 20 460 360 50 42)
cells (voronoi/voronoi-cells pts 0 0 500 400)
rng (java.util.Random. 42)
warm-colors [[:color/rgb 200 50 50]
[:color/rgb 50 80 180]
[:color/rgb 220 180 40]
[:color/rgb 60 160 80]
[:color/rgb 180 60 160]
[:color/rgb 200 120 40]
[:color/rgb 80 180 200]]]
{:image/size [500 400]
:image/background [:color/rgb 30 20 15]
:image/nodes
(vec (map-indexed
(fn [_i cell]
(-> cell
(assoc :style/fill (nth warm-colors (mod (.nextInt rng 100) (count warm-colors))))
(assoc :style/stroke {:color [:color/rgb 20 15 10] :width 4})
(assoc :node/opacity 0.85)))
cells))}))
Starfield
Scatter, noise, glow, and nebula.
View source
(defn ^{:example {:output "art-starfield.png"
:title "Starfield"
:desc "Scatter, noise, glow, and nebula."}}
starfield []
(let [pal (:midnight palette/palettes)
star-positions (scatter/poisson-disk 0 0 600 400 15 42)
bright-positions (scatter/noise-field 0 0 600 400 30 99)]
{:image/size [600 400]
:image/background (nth pal 0)
:image/nodes
[{:node/type :shape/circle
:circle/center [200.0 180.0]
:circle/radius 120.0
:style/fill [:color/rgba 120 60 160 0.15]}
{:node/type :shape/circle
:circle/center [400.0 250.0]
:circle/radius 100.0
:style/fill [:color/rgba 60 80 180 0.12]}
{:node/type :scatter
:scatter/shape {:node/type :shape/circle
:circle/center [0.0 0.0]
:circle/radius 1.0
:style/fill [:color/rgba 200 200 220 0.6]}
:scatter/positions star-positions
:scatter/jitter {:x 2 :y 2 :seed 11}}
{:node/type :scatter
:scatter/shape {:node/type :shape/circle
:circle/center [0.0 0.0]
:circle/radius 2.5
:style/fill [:color/rgb 255 250 230]
:effect/glow {:blur 6
:color [:color/rgb 200 200 255]
:opacity 0.4}}
:scatter/positions bright-positions
:scatter/jitter {:x 1 :y 1 :seed 22}}]}))
Stipple Spheres
Stippled spheres with shadows and varying density.
View source
(defn ^{:example {:output "art-stipple-spheres.png"
:title "Stipple Spheres"
:desc "Stippled spheres with shadows and varying density."}}
stipple-spheres []
{:image/size [400 400]
:image/background [:color/rgb 245 240 230]
:image/nodes
[{:node/type :shape/circle
:circle/center [200.0 200.0]
:circle/radius 120.0
:style/fill {:fill/type :stipple
:stipple/density 0.7
:stipple/radius 1.2
:stipple/seed 42
:stipple/color [:color/rgb 30 30 30]
:stipple/background [:color/rgb 245 240 230]}
:style/stroke {:color [:color/rgb 30 30 30] :width 1.5}
:effect/shadow {:dx 6 :dy 6 :blur 12
:color [:color/rgb 0 0 0]
:opacity 0.2}}
{:node/type :shape/circle
:circle/center [320.0 120.0]
:circle/radius 50.0
:style/fill {:fill/type :stipple
:stipple/density 0.3
:stipple/radius 1.0
:stipple/seed 99
:stipple/color [:color/rgb 30 30 30]
:stipple/background [:color/rgb 245 240 230]}
:style/stroke {:color [:color/rgb 30 30 30] :width 1}}
{:node/type :shape/circle
:circle/center [130.0 320.0]
:circle/radius 35.0
:style/fill {:fill/type :stipple
:stipple/density 0.5
:stipple/radius 0.8
:stipple/seed 77
:stipple/color [:color/rgb 30 30 30]
:stipple/background [:color/rgb 245 240 230]}
:style/stroke {:color [:color/rgb 30 30 30] :width 1}}]})
Thermal Imaging
Gradient-mapped noise field in thermal colors.
View source
(defn ^{:example {:output "art-thermal.png"
:title "Thermal Imaging"
:desc "Gradient-mapped noise field in thermal colors."}}
thermal []
(let [thermal-stops [[0.0 [:color/rgb 0 0 30]]
[0.2 [:color/rgb 20 0 100]]
[0.4 [:color/rgb 180 0 80]]
[0.6 [:color/rgb 255 100 0]]
[0.8 [:color/rgb 255 220 50]]
[1.0 [:color/rgb 255 255 200]]]
cols 60 rows 40
cell-w (/ 600.0 cols)
cell-h (/ 400.0 rows)]
{:image/size [600 400]
:image/background [:color/rgb 0 0 0]
:image/nodes
(vec (for [row (range rows)
col (range cols)]
(let [x (* col cell-w)
y (* row cell-h)
v (noise/fbm noise/perlin2d (* x 0.008) (* y 0.008)
{:octaves 4 :seed 42})
t (+ 0.5 (* 0.5 v))
color (palette/gradient-map thermal-stops t)]
{:node/type :shape/rect
:rect/xy [x y]
:rect/size [cell-w cell-h]
:style/fill color})))}))
Topographic Map
Contour lines colored by elevation.
View source
(defn ^{:example {:output "art-topo-map.png"
:title "Topographic Map"
:desc "Contour lines colored by elevation."}}
topo-map []
(let [thresholds (mapv #(- (* % 0.15) 0.6) (range 9))
pal (palette/gradient-palette [:color/rgb 30 80 30] [:color/rgb 200 170 120] 9)]
{:image/size [500 400]
:image/background [:color/rgb 210 225 210]
:image/nodes
(vec (map-indexed
(fn [i threshold]
{:node/type :contour
:contour/bounds [0 0 500 400]
:contour/opts {:thresholds [threshold]
:resolution 3
:noise-scale 0.012
:seed 42}
:style/stroke {:color (nth pal i) :width (if (zero? (mod i 3)) 1.5 0.7)}})
thresholds))}))
Van Gogh Swirls
Flow fields with variable brush strokes and warm palette.
View source
(defn ^{:example {:output "art-van-gogh-swirls.png"
:title "Van Gogh Swirls"
:desc "Flow fields with variable brush strokes and warm palette."}}
van-gogh-swirls []
(let [pal (:fire palette/palettes)
paths (flow/flow-field 0 0 600 400
{:density 6 :steps 80 :step-length 1.5
:noise-scale 0.004 :seed 77})]
{:image/size [600 400]
:image/background [:color/rgb 15 15 40]
:image/nodes
(vec (map-indexed
(fn [i path]
(-> path
(assoc :stroke/profile :brush)
(assoc :style/stroke
{:color (nth pal (mod i 5))
:width (+ 3 (* 2 (noise/perlin2d (* i 0.1) 0.5)))})))
paths))}))
Venn Booleans
Path boolean operations on overlapping circles.
View source
(defn ^{:example {:output "art-venn-booleans.png"
:title "Venn Booleans"
:desc "Path boolean operations on overlapping circles."}}
venn-booleans []
(let [circle-a (:path/commands (scene/regular-polygon [160.0 200.0] 100.0 60))
circle-b (:path/commands (scene/regular-polygon [260.0 200.0] 100.0 60))
circle-c (:path/commands (scene/regular-polygon [210.0 130.0] 100.0 60))
ab-only (path/intersection circle-a circle-b)
ac-only (path/intersection circle-a circle-c)
bc-only (path/intersection circle-b circle-c)
abc (path/intersection ab-only circle-c)]
{:image/size [420 350]
:image/background [:color/rgb 245 240 230]
:image/nodes
[{:node/type :shape/path :path/commands circle-a
:style/fill [:color/rgba 220 60 60 0.4]
:style/stroke {:color [:color/rgb 180 40 40] :width 2}}
{:node/type :shape/path :path/commands circle-b
:style/fill [:color/rgba 60 60 220 0.4]
:style/stroke {:color [:color/rgb 40 40 180] :width 2}}
{:node/type :shape/path :path/commands circle-c
:style/fill [:color/rgba 60 180 60 0.4]
:style/stroke {:color [:color/rgb 40 140 40] :width 2}}
{:node/type :shape/path :path/commands abc
:style/fill [:color/rgb 255 255 255]}]}))
Wavy Text
Text with twist warp and radial gradient fill.
View source
(defn ^{:example {:output "art-wavy-text.png"
:title "Wavy Text"
:desc "Text with twist warp and radial gradient fill."}}
wavy-text []
{:image/size [500 300]
:image/background [:color/rgb 15 10 30]
:image/nodes
[{:node/type :group
:group/warp {:type :twist :center [250 150] :amount 0.08}
:group/children
[(-> (scene/text-outline "TWIST" {:font/family "Serif" :font/size 100
:font/weight :bold} [70 190])
(assoc :style/fill {:gradient/type :radial
:gradient/center [250 -50]
:gradient/radius 300
:gradient/stops [[0.0 [:color/rgb 255 100 200]]
[0.5 [:color/rgb 255 200 50]]
[1.0 [:color/rgb 50 200 255]]]})
(assoc :style/stroke {:color [:color/rgba 255 255 255 0.3] :width 1}))]}]})2D Scenes

Blooming Tree
A recursive fractal tree that grows from trunk to full canopy, then sways in the wind.
View source
(defn ^{:example {:output "tree.gif"
:title "Blooming Tree"
:desc "A recursive fractal tree that grows from trunk to full canopy, then sways in the wind."}}
blooming-tree []
{:frames
(anim/frames 90
(fn [t]
(let [rng (java.util.Random. 42)
growth (* t 3.0)
sway (* (max 0 (- t 0.4)) 8.0 (Math/sin (* t 6 Math/PI)))]
{:image/size [450 450]
:image/background [:color/rgb 20 20 30]
:image/nodes (vec (tree-branches rng 225 420 90 0 9 9 growth sway))})))
:fps 24})
Breathing Wave
A diagonal wave where cells expand and contract with staggered timing, hue shifting along the diagonal.
View source
(defn ^{:example {:output "breathing-grid.gif"
:title "Breathing Wave"
:desc "A diagonal wave where cells expand and contract with staggered timing, hue shifting along the diagonal."}}
breathing-wave []
{:frames
(anim/frames 50
(fn [t]
{:image/size [400 400]
:image/background [:color/rgb 245 243 238]
:image/nodes
(scene/grid 14 14
(fn [col row]
(let [delay (/ (+ col row) 26.0)
phase (mod (- (* t 2.0) delay) 1.0)
breath (/ (+ 1.0 (Math/sin (* phase 2.0 Math/PI))) 2.0)
size (+ 3 (* 10 breath))
hue (mod (+ (* (+ col row) 14) (* t 120)) 360)]
{:node/type :shape/circle
:circle/center [(+ 18 (* col 27)) (+ 18 (* row 27))]
:circle/radius size
:style/fill [:color/hsl hue (+ 0.5 (* 0.4 breath)) 0.48]})))}))
:fps 24})
Cellular Automaton
Evolving cellular patterns driven by sine wave interference, rendered as glowing colored cells.
View source
(defn ^{:example {:output "cellular.gif"
:title "Cellular Automaton"
:desc "Evolving cellular patterns driven by sine wave interference, rendered as glowing colored cells."}}
cellular-automaton []
{:frames
(anim/frames 40
(fn [t]
{:image/size [400 400]
:image/background [:color/rgb 10 10 18]
:image/nodes
(scene/grid 25 25
(fn [col row]
(let [sum (+ (Math/sin (+ (* col 0.7) (* t 5)))
(Math/cos (+ (* row 0.8) (* t 4)))
(Math/sin (+ (* (+ col row) 0.4) (* t 3)))
(Math/cos (+ (* (Math/abs (- col row)) 0.6) (* t 6))))]
(when (> sum 0.5)
(let [glow (max 0 (/ (- sum 0.5) 3.5))
hue (mod (+ (* col 8) (* row 8) (* t 200)) 360)]
{:node/type :shape/rect
:rect/xy [(+ 2 (* col 15.8)) (+ 2 (* row 15.8))]
:rect/size [14 14]
:node/opacity (+ 0.6 (* 0.4 glow))
:style/fill [:color/hsl hue 0.9 (+ 0.35 (* 0.3 glow))]})))))}))
:fps 15})
Dancing Bars
Vertical bars with height, position, and color driven by overlapping sine waves.
View source
(defn ^{:example {:output "dancing-bars.gif"
:title "Dancing Bars"
:desc "Vertical bars with height, position, and color driven by overlapping sine waves."}}
dancing-bars []
{:frames
(anim/frames 50
(fn [t]
{:image/size [400 400]
:image/background [:color/rgb 10 10 18]
:image/nodes
(vec (for [col (range 30)]
(let [x-norm (/ col 29.0)
wave1 (Math/sin (+ (* x-norm 4 Math/PI) (* t 2 Math/PI)))
wave2 (Math/sin (+ (* x-norm 6 Math/PI) (* t 2 Math/PI 1.7)))
combined (/ (+ wave1 wave2) 2.0)
height (+ 40 (* 140 (/ (+ combined 1.0) 2.0)))
y-center (+ 200 (* 60 (Math/sin (+ (* x-norm 3 Math/PI)
(* t 2 Math/PI 0.5)))))
width (+ 4 (* 6 (/ (+ combined 1.0) 2.0)))
hue (mod (+ (* x-norm 360) (* t 200)) 360)]
{:node/type :shape/rect
:rect/xy [(- (* (+ 0.5 col) (/ 400.0 30)) (/ width 2))
(- y-center (/ height 2))]
:rect/size [width height]
:style/fill [:color/hsl hue 0.85
(+ 0.35 (* 0.3 (/ (+ combined 1.0) 2.0)))]})))}))
:fps 24})
Kaleidoscope
Eight-fold rotational symmetry with orbiting, pulsing dots.
View source
(defn ^{:example {:output "kaleidoscope.gif"
:title "Kaleidoscope"
:desc "Eight-fold rotational symmetry with orbiting, pulsing dots."}}
kaleidoscope []
{:frames
(anim/frames 60
(fn [t]
{:image/size [400 400]
:image/background [:color/rgb 5 5 12]
:image/nodes
(vec (for [sym (range 8)
shape (range 6)]
(let [angle (+ (* sym (/ Math/PI 4)) (* t Math/PI 0.25))
shape-r (+ 30 (* shape 25))
shape-angle (+ angle (* shape 0.8)
(* (Math/sin (+ (* t 4 Math/PI) (* shape 1.2))) 0.3))
x (+ 200 (* shape-r (Math/cos shape-angle)))
y (+ 200 (* shape-r (Math/sin shape-angle)))
hue (mod (+ (* sym 45) (* shape 30) (* t 180)) 360)
size (+ 5 (* 8 (/ (+ 1 (Math/sin (+ (* t 3 Math/PI) shape sym))) 2.0)))]
{:node/type :shape/circle
:circle/center [x y]
:circle/radius size
:node/opacity 0.75
:style/fill [:color/hsl hue 0.85 0.55]})))}))
:fps 24})
Koch Snowflake
A Koch snowflake that gains detail with each frame, the boundary growing ever more intricate.
View source
(defn ^{:example {:output "koch.gif"
:title "Koch Snowflake"
:desc "A Koch snowflake that gains detail with each frame, the boundary growing ever more intricate."}}
koch-snowflake-scene []
{:frames
(anim/frames 60
(fn [t]
(let [depth (int (Math/floor (+ 0.5 (* t 5))))
c (color/resolve-color [:color/hsl (* t 40) 0.8 0.5])]
{:image/size [450 450]
:image/background [:color/rgb 20 20 30]
:image/nodes
[(assoc (koch-snowflake 225 235 180 depth)
:style/fill [:color/rgb (:r c) (:g c) (:b c)])]})))
:fps 10})
Lissajous Curve
A 3:2 Lissajous figure traced with a rainbow trail that fades with age.
View source
(defn ^{:example {:output "lissajous.gif"
:title "Lissajous Curve"
:desc "A 3:2 Lissajous figure traced with a rainbow trail that fades with age."}}
lissajous-curve []
{:frames
(anim/frames 60
(fn [t]
{:image/size [400 400]
:image/background [:color/rgb 5 5 12]
:image/nodes
(vec (for [j (range 200)]
(let [s (/ j 200.0)
phase (* (+ t s) 2 Math/PI)
x (+ 200 (* 160 (Math/sin (* phase 3))))
y (+ 200 (* 160 (Math/cos (* phase 2))))
age (- 1.0 s)
hue (mod (* (+ t s) 720) 360)]
{:node/type :shape/circle
:circle/center [x y]
:circle/radius (+ 1 (* 5 age age))
:node/opacity (* age age)
:style/fill [:color/hsl hue 0.9 (+ 0.3 (* 0.4 age))]})))}))
:fps 30})
Op Art
Concentric rings that wobble to create an optical illusion, using only black and white.
View source
(defn ^{:example {:output "op-art.gif"
:title "Op Art"
:desc "Concentric rings that wobble to create an optical illusion, using only black and white."}}
op-art []
{:frames
(anim/frames 50
(fn [t]
{:image/size [400 400]
:image/background [:color/rgb 255 255 255]
:image/nodes
(vec (for [ring (reverse (range 40))]
(let [phase (+ (* t 2 Math/PI) (* ring 0.3))
wobble (* 15 (Math/sin phase))
r (+ (* ring 7) wobble)]
{:node/type :shape/circle
:circle/center [(+ 200 (* 5 (Math/sin (+ phase 1.5))))
(+ 200 (* 5 (Math/cos phase)))]
:circle/radius (max 1 r)
:style/fill (if (even? ring)
[:color/rgb 0 0 0]
[:color/rgb 255 255 255])})))}))
:fps 24})
Particle Galaxy
300 particles orbiting with Keplerian speeds and 3 spiral arms.
View source
(defn ^{:example {:output "galaxy.gif"
:title "Particle Galaxy"
:desc "300 particles orbiting with Keplerian speeds and 3 spiral arms."}}
particle-galaxy []
{:frames
(anim/frames 60
(fn [t]
{:image/size [500 500]
:image/background [:color/rgb 5 5 12]
:image/nodes
(vec (for [p (range 300)]
(let [arm (mod p 3)
base-r (+ 15 (* (Math/sqrt (/ p 300.0)) 220))
speed (/ 1.0 (Math/sqrt (max 1 base-r)))
angle (+ (* t 2 Math/PI speed 3)
(/ (* p 137.508) 50.0)
(* arm (/ (* 2 Math/PI) 3))
(* (/ base-r 300.0) 1.5))
r (+ base-r (* 8 (Math/sin (+ (* t 6 Math/PI) (* p 137.508)))))
x (+ 250 (* r (Math/cos angle)))
y (+ 250 (* r (Math/sin angle)))
hue (mod (+ 200 (* -200 (/ base-r 230.0))) 360)
bright (- 1.0 (* 0.5 (/ base-r 230.0)))
size (max 1 (- 4 (* 2.5 (/ base-r 230.0))))]
{:node/type :shape/circle
:circle/center [x y]
:circle/radius size
:node/opacity (min 1.0 bright)
:style/fill [:color/hsl hue 0.8 (* 0.55 bright)]})))}))
:fps 24})
Pendulum Wave
15 pendulums with increasing frequencies create wave patterns.
View source
(defn ^{:example {:output "pendulum-wave.gif"
:title "Pendulum Wave"
:desc "15 pendulums with increasing frequencies create wave patterns."}}
pendulum-wave []
{:frames
(anim/frames 80
(fn [t]
{:image/size [500 400]
:image/background [:color/rgb 245 243 238]
:image/nodes
(vec (for [p (range 15)]
(let [freq (+ 6 p)
angle (* (Math/sin (* t 2 Math/PI freq)) 0.9)
pivot-x (+ 30 (* p 31.4))
bob-x (+ pivot-x (* 250 (Math/sin angle)))
bob-y (* 250 (Math/cos angle))
hue (* p 24)]
{:node/type :group
:group/children
[{:node/type :shape/path
:path/commands [[:move-to [pivot-x 20]]
[:line-to [bob-x (+ 20 bob-y)]]]
:style/stroke {:color [:color/rgb 80 80 80] :width 1}}
{:node/type :shape/circle
:circle/center [bob-x (+ 20 bob-y)]
:circle/radius 10
:style/fill [:color/hsl hue 0.75 0.5]}]})))}))
:fps 30})
Sierpinski Triangle
The classic fractal, built up one recursion depth at a time with shifting colors.
View source
(defn ^{:example {:output "sierpinski.gif"
:title "Sierpinski Triangle"
:desc "The classic fractal, built up one recursion depth at a time with shifting colors."}}
sierpinski-triangle []
{:frames
(anim/frames 60
(fn [t]
(let [depth (int (Math/floor (+ 1 (* t 6))))]
{:image/size [500 450]
:image/background [:color/rgb 20 20 30]
:image/nodes (vec (sierpinski 250 30 460 410 40 410 depth 0 t))})))
:fps 10})
Sine Interference
Three overlapping sine waves at different frequencies create organic, shifting patterns.
View source
(defn ^{:example {:output "sine-field.gif"
:title "Sine Interference"
:desc "Three overlapping sine waves at different frequencies create organic, shifting patterns."}}
sine-interference []
{:frames
(anim/frames 50
(fn [t]
{:image/size [400 400]
:image/background [:color/rgb 10 10 18]
:image/nodes
(scene/grid 20 20
(fn [col row]
(let [x (/ col 19.0)
y (/ row 19.0)
v (/ (+ (Math/sin (+ (* x 8) (* t 2 Math/PI)))
(Math/sin (+ (* y 6) (* t 2 Math/PI 1.3)))
(Math/sin (+ (* (+ x y) 5) (* t 2 Math/PI 0.7)))) 3.0)
pulse (/ (+ v 1.0) 2.0)
radius (+ 3 (* 7 pulse))
hue (mod (+ (* pulse 200) (* t 360) 180) 360)]
{:node/type :shape/circle
:circle/center [(+ 12 (* col 19.8)) (+ 12 (* row 19.8))]
:circle/radius radius
:style/fill [:color/hsl hue 0.9 (+ 0.35 (* 0.3 pulse))]})))}))
:fps 24})
Spiral Rainbow
A rotating spiral wave where hue follows the angle and pulse follows the distance from center.
View source
(defn ^{:example {:output "spiral-grid.gif"
:title "Spiral Rainbow"
:desc "A rotating spiral wave where hue follows the angle and pulse follows the distance from center."}}
spiral-rainbow []
{:frames
(anim/frames 60
(fn [t]
{:image/size [400 400]
:image/background [:color/rgb 10 10 18]
:image/nodes
(scene/grid 20 20
(fn [col row]
(let [cx (- (/ col 9.5) 1.0)
cy (- (/ row 9.5) 1.0)
dist (Math/sqrt (+ (* cx cx) (* cy cy)))
angle (Math/atan2 cy cx)
spiral (mod (+ (* dist 2.0)
(* angle (/ 1.0 Math/PI))
(- (* t 3.0))) 1.0)
pulse (/ (+ 1.0 (Math/sin (* spiral 2.0 Math/PI))) 2.0)
radius (+ 2 (* 8 pulse))
hue (mod (+ (* angle (/ 180.0 Math/PI)) 180 (* t 360)) 360)]
{:node/type :shape/circle
:circle/center [(+ 12 (* col 19.8)) (+ 12 (* row 19.8))]
:circle/radius radius
:style/fill [:color/hsl hue 0.9 (+ 0.3 (* 0.35 pulse))]})))}))
:fps 24})
Star Burst
Rotating gradient stars with staggered pulsing, using radial gradients and cubic easing.
View source
(defn ^{:example {:output "star-burst.gif"
:title "Star Burst"
:desc "Rotating gradient stars with staggered pulsing, using radial gradients and cubic easing."}}
star-burst []
{:frames
(anim/frames 60
(fn [t]
{:image/size [400 400]
:image/background [:color/name "midnightblue"]
:image/nodes
(vec
(for [i (range 6)
:let [rotation (* (+ t (* i 0.167)) 2 Math/PI (/ 1.0 6))
pulse (anim/ease-in-out-cubic
(anim/ping-pong (mod (+ t (* i 0.12)) 1.0)))
outer (+ 60 (* 50 pulse))
inner (* outer 0.4)
hue (mod (+ (* i 60) (* t 360)) 360)]]
{:node/type :group
:node/transform [[:transform/translate 200 200]
[:transform/rotate rotation]]
:node/opacity (+ 0.5 (* 0.5 pulse))
:group/children
[(merge (scene/star [0 0] outer inner 5)
{:style/fill
{:gradient/type :radial
:gradient/center [0 0]
:gradient/radius outer
:gradient/stops
[[0.0 [:color/hsl hue 0.95 0.7]]
[1.0 [:color/hsl (mod (+ hue 60) 360) 0.9 0.35]]]}})]}))}))
:fps 30})
Tentacles
Eight arms spiral outward from the center, wobbling and shifting color along their length.
View source
(defn ^{:example {:output "tentacles.gif"
:title "Tentacles"
:desc "Eight arms spiral outward from the center, wobbling and shifting color along their length."}}
tentacles []
{:frames
(anim/frames 60
(fn [t]
{:image/size [400 400]
:image/background [:color/rgb 10 10 18]
:image/nodes
(vec (for [arm (range 8)]
(let [base-angle (+ (* arm (/ (* 2 Math/PI) 8)) (* t Math/PI 0.3))
hue (* arm 45)]
{:node/type :group
:group/children
(vec (for [seg (range 25)]
(let [seg-t (/ seg 24.0)
r (* seg-t 180)
wobble (* 30 seg-t
(Math/sin (+ (* seg-t 8) (* t 2 Math/PI)
(* arm 0.7))))
angle (+ base-angle
(* seg-t 0.8
(Math/sin (+ (* t 2 Math/PI) arm))))
x (+ 200 (* (+ r wobble) (Math/cos angle)))
y (+ 200 (* (+ r wobble) (Math/sin angle)))
size (max 1 (- 10 (* seg-t 8)))
seg-hue (mod (+ hue (* seg-t 90) (* t 120)) 360)]
{:node/type :shape/circle
:circle/center [x y]
:circle/radius size
:node/opacity (- 1.0 (* seg-t 0.6))
:style/fill [:color/hsl seg-hue 0.85 0.55]})))})))}))
:fps 24})3D Scenes

Camera: Perspective FOV
Field-of-view control for perspective projection; closer objects appear larger.
View source
(defn ^{:example {:output "3d-perspective-fov.png"
:title "Camera: Perspective FOV"
:desc "Field-of-view control for perspective projection; closer objects appear larger."}}
camera-perspective-fov []
{:image/size [400 400]
:image/background [:color/rgb 245 243 238]
:image/nodes
(render-camera-scene
(s3d/look-at
(s3d/perspective {:scale 55 :origin [200 210]
:distance (s3d/fov->distance (/ Math/PI 3) (/ 200.0 55))})
[4 4 7] [0 0.5 0]))})
Camera: look-at
Point the camera from a position toward a target, no manual yaw/pitch math.
View source
(defn ^{:example {:output "3d-look-at.png"
:title "Camera: look-at"
:desc "Point the camera from a position toward a target, no manual yaw/pitch math."}}
camera-look-at []
{:image/size [400 400]
:image/background [:color/rgb 245 243 238]
:image/nodes
(render-camera-scene
(s3d/look-at (s3d/orthographic {:scale 55 :origin [200 210]})
[4 3.5 6] [0 0.5 0]))})
Camera: orbit
Camera on a sphere around a target, orbiting the scene.
View source
(defn ^{:example {:output "3d-orbit.gif"
:title "Camera: orbit"
:desc "Camera on a sphere around a target, orbiting the scene."}}
camera-orbit []
{:frames (anim/frames 90
(fn [t]
{:image/size [400 400]
:image/background [:color/rgb 245 243 238]
:image/nodes
(render-camera-scene
(s3d/orbit (s3d/orthographic {:scale 55 :origin [200 210]})
[0 0.5 0] 8 (* t 2.0 Math/PI) -0.4))}))
:fps 30})
Isometric City
An 8x8 grid of buildings with randomized heights and hue-shifted colors.
View source
(defn ^{:example {:output "3d-city.png"
:title "Isometric City"
:desc "An 8x8 grid of buildings with randomized heights and hue-shifted colors."}}
isometric-city []
(let [n 6 spacing 2.4 offset (* -0.5 n spacing)
proj (s3d/isometric {:scale 22 :origin [200 220]})
light {:light/direction [1 3 0.5] :light/ambient 0.3 :light/intensity 0.7}
rng (java.util.Random. 42)
mesh
(into []
(for [gx (range n) gz (range n)
:let [x (+ offset (* gx spacing))
z (+ offset (* gz spacing))
h (+ 0.8 (* 4.0 (.nextDouble rng)))
hue (* 360.0 (/ (+ gx gz) (* 2.0 n)))
r (int (+ 110 (* 70 (Math/sin (* hue 0.0174)))))
g (int (+ 120 (* 50 (Math/sin (* (+ hue 120) 0.0174)))))
b (int (+ 140 (* 60 (Math/sin (* (+ hue 240) 0.0174)))))
building (-> (s3d/cube-mesh [0 0 0] 1)
(s3d/scale-mesh [2.0 h 2.0])
(s3d/translate-mesh [x 0 z]))]
face building]
(assoc face :face/style
{:style/fill [:color/rgb r g b]
:style/stroke {:color [:color/rgb (- r 35) (- g 35) (- b 35)]
:width 0.3}})))]
{:image/size [400 400]
:image/background [:color/rgb 225 230 238]
:image/nodes [(s3d/render-mesh proj mesh {:light light})]}))
Isometric Scene
Three primitives — cube, cylinder, and sphere — with shared directional lighting.
View source
(defn ^{:example {:output "3d-scene.png"
:title "Isometric Scene"
:desc "Three primitives — cube, cylinder, and sphere — with shared directional lighting."}}
isometric-scene []
(let [proj (s3d/isometric {:scale 35 :origin [200 210]})
light {:light/direction [1 2 0.5] :light/ambient 0.3 :light/intensity 0.7}]
{:image/size [400 400]
:image/background [:color/rgb 245 243 238]
:image/nodes
[(s3d/cube proj [-1.5 0 -1.5] 2
{:style {:style/fill [:color/rgb 90 140 200]
:style/stroke {:color [:color/rgb 50 80 130] :width 0.5}}
:light light})
(s3d/cylinder proj [2 0 -1.5] 0.9 2.2
{:style {:style/fill [:color/rgb 200 100 80]
:style/stroke {:color [:color/rgb 130 55 40] :width 0.5}}
:light light :segments 20})
(s3d/sphere proj [0 1.3 1.8] 1.0
{:style {:style/fill [:color/rgb 100 180 100]
:style/stroke {:color [:color/rgb 50 110 50] :width 0.3}}
:light light :segments 16 :rings 8})]}))
New Primitives
Cone, torus, and sphere combined with merge-meshes and perspective projection.
View source
(defn ^{:example {:output "3d-cone-torus.png"
:title "New Primitives"
:desc "Cone, torus, and sphere combined with merge-meshes and perspective projection."}}
new-primitives []
(let [light {:light/direction [1 2 0.5] :light/ambient 0.3 :light/intensity 0.7}
proj (s3d/look-at
(s3d/perspective {:scale 55 :origin [200 210]
:distance (s3d/fov->distance (/ Math/PI 3) (/ 200.0 55))})
[3 3 5] [0 0.8 0])]
{:image/size [400 400]
:image/background [:color/rgb 245 243 238]
:image/nodes
[(s3d/render-mesh proj
(s3d/merge-meshes
[(s3d/cone-mesh 0.8 2.0 48)
{:style/fill [:color/rgb 220 160 60]}]
[(-> (s3d/torus-mesh 1.2 0.3 48 24)
(s3d/translate-mesh [0 0.3 0]))
{:style/fill [:color/rgb 70 130 200]}]
[(-> (s3d/sphere-mesh 0.5 32 16)
(s3d/translate-mesh [-2.0 0.5 0.5]))
{:style/fill [:color/rgb 200 80 80]}])
{:light light})]}))
Rotating Cube
A cube tumbling in space with directional lighting.
View source
(defn ^{:example {:output "3d-rotating-cube.gif"
:title "Rotating Cube"
:desc "A cube tumbling in space with directional lighting."}}
rotating-cube []
{:frames (anim/frames 60
(fn [t]
(let [proj (s3d/isometric {:scale 70 :origin [200 200]})
mesh (-> (s3d/cube-mesh [-1 -1 -1] 2)
(s3d/rotate-mesh :y (* t 2.0 Math/PI))
(s3d/rotate-mesh :x (* t 0.7 Math/PI)))]
{:image/size [400 400]
:image/background [:color/rgb 20 20 30]
:image/nodes
[(s3d/render-mesh proj mesh
{:style {:style/fill [:color/rgb 70 130 210]
:style/stroke {:color [:color/rgb 140 180 240] :width 1}}
:light {:light/direction [1 2 0.5]
:light/ambient 0.25
:light/intensity 0.75}})]})))
:fps 30})
Rotating Torus
A parametric torus spinning on its axis with diffuse shading.
View source
(defn ^{:example {:output "3d-rotating-torus.gif"
:title "Rotating Torus"
:desc "A parametric torus spinning on its axis with diffuse shading."}}
rotating-torus []
{:frames (anim/frames 60
(fn [t]
(let [proj (s3d/isometric {:scale 55 :origin [200 200]})
mesh (-> (s3d/torus-mesh 1.8 0.7 24 12)
(s3d/rotate-mesh :x 0.4)
(s3d/rotate-mesh :y (* t 2.0 Math/PI)))]
{:image/size [400 400]
:image/background [:color/rgb 20 20 30]
:image/nodes
[(s3d/render-mesh proj mesh
{:style {:style/fill [:color/rgb 220 160 60]
:style/stroke {:color [:color/rgb 160 110 30] :width 0.5}}
:light {:light/direction [1 2 1]
:light/ambient 0.2
:light/intensity 0.8}
:cull-back false})]})))
:fps 30})
Torus
A golden torus with fine mesh detail and smooth Lambertian shading.
View source
(defn ^{:example {:output "3d-torus.png"
:title "Torus"
:desc "A golden torus with fine mesh detail and smooth Lambertian shading."}}
torus []
(let [proj (s3d/isometric {:scale 55 :origin [200 200]})
mesh (-> (s3d/torus-mesh 1.8 0.7 32 16)
(s3d/rotate-mesh :x 0.6))]
{:image/size [400 400]
:image/background [:color/rgb 245 243 238]
:image/nodes
[(s3d/render-mesh proj mesh
{:style {:style/fill [:color/rgb 220 170 60]}
:light {:light/direction [1 2 1]
:light/ambient 0.25
:light/intensity 0.75}
:cull-back false})]}))
Utah Teapot
Classic computer graphics test model loaded from OBJ, rendered with isometric projection.
View source
(defn ^{:example {:output "3d-teapot.png"
:title "Utah Teapot"
:desc "Classic computer graphics test model loaded from OBJ, rendered with isometric projection."}}
utah-teapot []
(let [teapot (-> (obj/parse-obj (slurp "resources/teapot.obj") {})
(s3d/translate-mesh [-1.085 0.0 -7.875])
(s3d/scale-mesh 0.1)
(s3d/rotate-mesh :x (- (/ Math/PI 2))))]
{:image/size [400 400]
:image/background [:color/rgb 245 243 238]
:image/nodes
[(s3d/render-mesh
(s3d/isometric {:scale 90 :origin [200 210]})
(s3d/rotate-mesh teapot :y 0.8)
{:style {:style/fill [:color/rgb 175 185 195]
:style/stroke {:color [:color/rgb 135 145 155] :width 0.15}}
:light {:light/direction [1 2 1]
:light/ambient 0.3
:light/intensity 0.7}
:cull-back false})]}))
Utah Teapot Spin
Orbiting camera animation around the Utah teapot.
View source
(defn ^{:example {:output "3d-teapot-spin.gif"
:title "Utah Teapot Spin"
:desc "Orbiting camera animation around the Utah teapot."}}
utah-teapot-spin []
(let [teapot (-> (obj/parse-obj (slurp "resources/teapot.obj") {})
(s3d/translate-mesh [-1.085 0.0 -7.875])
(s3d/scale-mesh 0.1)
(s3d/rotate-mesh :x (- (/ Math/PI 2))))]
{:frames (anim/frames 90
(fn [t]
(let [proj (s3d/orbit (s3d/orthographic {:scale 90 :origin [200 190]})
(s3d/mesh-center teapot) 4
(* t 2.0 Math/PI) -0.45)]
{:image/size [400 400]
:image/background [:color/rgb 245 243 238]
:image/nodes
[(s3d/render-mesh proj teapot
{:style {:style/fill [:color/rgb 175 185 195]
:style/stroke {:color [:color/rgb 135 145 155] :width 0.15}}
:light {:light/direction [1 2 1]
:light/ambient 0.3
:light/intensity 0.7}
:cull-back false})]})))
:fps 30}))
Wireframe
A torus rendered as wireframe with deduplicated, depth-sorted edges.
View source
(defn ^{:example {:output "3d-wireframe.png"
:title "Wireframe"
:desc "A torus rendered as wireframe with deduplicated, depth-sorted edges."}}
wireframe []
{:image/size [400 400]
:image/background [:color/rgb 245 243 238]
:image/nodes
[(s3d/render-mesh
(s3d/look-at (s3d/orthographic {:scale 55 :origin [200 200]})
[3 2.5 4] [0 0 0])
(s3d/torus-mesh 1.5 0.6 20 10)
{:wireframe true
:style {:style/stroke {:color [:color/rgb 60 80 120] :width 0.5}}})]})
Wireframe Overlay
Solid shading and wireframe combined; wireframe renders at 40% opacity over the solid torus.
View source
(defn ^{:example {:output "3d-torus-wireframe-overlay.gif"
:title "Wireframe Overlay"
:desc "Solid shading and wireframe combined; wireframe renders at 40% opacity over the solid torus."}}
wireframe-overlay []
{:frames (anim/frames 60
(fn [t]
(let [proj (s3d/orbit (s3d/orthographic {:scale 50 :origin [200 200]})
[0 0 0] 5 (* t 2.0 Math/PI) -0.3)
mesh (s3d/torus-mesh 1.5 0.6 20 10)]
{:image/size [400 400]
:image/background [:color/rgb 20 20 30]
:image/nodes
[(s3d/render-mesh proj mesh
{:style {:style/fill [:color/rgb 60 100 160]}
:light {:light/direction [1 2 1]
:light/ambient 0.2
:light/intensity 0.8}
:cull-back false})
{:node/type :group
:group/composite :src-over
:node/opacity 0.4
:group/children
[(s3d/render-mesh proj mesh
{:wireframe true
:style {:style/stroke {:color [:color/rgb 180 210 255]
:width 0.4}}})]}]})))
:fps 30})Mixed 2D/3D

Crystal Garden
Faceted 3D crystal spires with swaying 2D grass and rising sparkle particles.
View source
(defn ^{:example {:output "mixed-crystal-garden.gif"
:title "Crystal Garden"
:desc "Faceted 3D crystal spires with swaying 2D grass and rising sparkle particles."}}
crystal-garden []
(let [frame-fn
(fn [t]
(let [proj (s3d/perspective
{:scale 70 :origin [300 300]
:yaw (* 0.2 (Math/sin (* t 2 Math/PI)))
:pitch -0.55 :distance 7.0})
light {:light/direction [0.0 0.6 1.0]
:light/ambient 0.35
:light/intensity 0.65}
;; Crystal definitions
crystals [{:x -2.5 :z 0.3 :h 2.5 :r 0.5 :fill [230 50 180]}
{:x -1.0 :z -0.5 :h 3.8 :r 0.6 :fill [40 160 220]}
{:x 0.0 :z 0.2 :h 4.5 :r 0.55 :fill [140 60 220]}
{:x 1.2 :z -0.3 :h 3.2 :r 0.5 :fill [40 200 140]}
{:x 2.3 :z 0.4 :h 4.0 :r 0.55 :fill [220 180 40]}]
;; 3D crystal cones with gentle sway
gems (mapv (fn [{:keys [x z h r fill]}]
(let [sway (* 0.15 (Math/sin (+ (* t 3 Math/PI) (* x 0.5))))
[cr cg cb] fill
mesh (-> (s3d/cone-mesh r h 6)
(s3d/rotate-mesh :z sway)
(s3d/translate-mesh [x 0 z]))]
(s3d/render-mesh proj mesh
{:style {:style/fill [:color/rgb cr cg cb]
:style/stroke {:color [:color/rgb (+ cr 20) (+ cg 40) (+ cb 20)]
:width 0.5}}
:light light})))
crystals)
;; 2D grass blades
grass (mapv (fn [i]
(let [gx (+ 15 (* i 8))
gh (* 14 (+ 0.4 (* 0.6 (Math/sin (+ (* i 0.8) (* t 5))))))
sway (* 5 (Math/sin (+ (* i 0.4) (* t 7))))
g (+ 90 (int (* 60 (Math/sin (+ (* i 0.3) 1.0)))))]
{:node/type :shape/line
:line/from [gx 320]
:line/to [(+ gx sway) (- 320 gh)]
:style/stroke {:color [:color/rgb 35 g 30] :width 1.5}}))
(range 72))
;; 2D rising sparkles
sparkles (mapv (fn [i]
(let [phase (mod (+ (* i 0.618) t) 1.0)
hue (mod (+ (* i 55) (* t 90)) 360)]
{:node/type :shape/circle
:circle/center [(+ 100 (* i 90) (* 20 (Math/sin (+ (* i 2.7) (* t 4)))))
(- 310 (* 280 phase))]
:circle/radius (+ 1.0 (* 3.5 (- 1.0 phase)))
:node/opacity (* 0.9 (- 1.0 phase))
:style/fill [:color/hsl hue 0.9 0.75]}))
(range 6))]
{:image/size [600 400]
:image/background [:color/rgb 8 10 18]
:image/nodes (into [] (concat grass gems sparkles))}))]
{:frames (anim/frames 60 frame-fn) :fps 30}))
Neon Orbit
Rotating 3D torus wreathed in orbiting 2D color halos and pulsing concentric rings.
View source
(defn ^{:example {:output "mixed-neon-orbit.gif"
:title "Neon Orbit"
:desc "Rotating 3D torus wreathed in orbiting 2D color halos and pulsing concentric rings."}}
neon-orbit []
(let [frame-fn
(fn [t]
(let [cx 250
cy 250
angle (* t 2.0 Math/PI)
proj (s3d/perspective
{:scale 90 :origin [cx cy]
:yaw angle :pitch -0.35 :distance 5.5})
light {:light/direction [0.6 1.0 0.4]
:light/ambient 0.2
:light/intensity 0.8}
;; 3D torus — hot magenta
torus-3d (s3d/torus proj [0 0 0] 1.8 0.5
{:style {:style/fill [:color/rgb 255 50 160]
:style/stroke {:color [:color/rgb 255 120 200]
:width 0.4}}
:light light
:ring-segments 36
:tube-segments 18})
;; Orbiting 2D halos — rainbow palette
n-halos 10
halos (scene/radial n-halos cx cy 170
(fn [x y a]
(let [i (int (/ (* a n-halos) (* 2 Math/PI)))
phase (+ angle (* i 0.6))
pulse (+ 0.5 (* 0.5 (Math/sin phase)))
hue (mod (+ (* i 36) (* t 360)) 360)
r (+ 6 (* 16 pulse))]
{:node/type :shape/circle
:circle/center [x y]
:circle/radius r
:node/opacity (* 0.75 pulse)
:style/fill [:color/hsl hue 0.95 0.6]})))
;; Pulsing concentric rings
rings (mapv (fn [i]
(let [r (+ 30 (* i 30))
phase (- (* t 2 Math/PI) (* i 0.4))
alpha (* 0.2 (+ 0.5 (* 0.5 (Math/sin phase))))
hue (mod (+ (* i 50) (* t 120)) 360)]
{:node/type :shape/circle
:circle/center [cx cy]
:circle/radius r
:node/opacity alpha
:style/stroke {:color [:color/hsl hue 0.7 0.6]
:width 1.5}}))
(range 8))]
{:image/size [500 500]
:image/background [:color/rgb 10 6 22]
:image/nodes (into [] (concat rings [torus-3d] halos))}))]
{:frames (anim/frames 60 frame-fn) :fps 30}))
Solar System
Shaded 3D planet with 2D orbital ellipses, trailing moons, and a twinkling star field.
View source
(defn ^{:example {:output "mixed-solar-system.gif"
:title "Solar System"
:desc "Shaded 3D planet with 2D orbital ellipses, trailing moons, and a twinkling star field."}}
solar-system []
(let [frame-fn
(fn [t]
(let [cx 250 cy 250
angle (* t 2.0 Math/PI)
proj (s3d/perspective
{:scale 100 :origin [cx cy]
:yaw 0.3 :pitch -0.55 :distance 5.0})
light {:light/direction [1.0 0.8 0.5]
:light/ambient 0.12 :light/intensity 0.88}
;; 3D rotating planet
planet (let [mesh (-> (s3d/sphere-mesh 1.2 24 12)
(s3d/rotate-mesh :y angle))]
(s3d/render-mesh proj mesh
{:style {:style/fill [:color/rgb 30 90 190]
:style/stroke {:color [:color/rgb 40 110 210]
:width 0.3}}
:light light}))
;; 2D orbital data
orbits [{:rx 105 :hue 20 :speed 1.0 :moon-r 7}
{:rx 150 :hue 80 :speed 0.7 :moon-r 5}
{:rx 195 :hue 160 :speed 0.45 :moon-r 8}
{:rx 235 :hue 280 :speed 0.3 :moon-r 4}]
;; 2D dashed orbit ellipses
rings (mapv (fn [{:keys [rx hue]}]
{:node/type :shape/ellipse
:ellipse/center [cx cy]
:ellipse/rx rx
:ellipse/ry (* rx 0.35)
:node/opacity 0.25
:style/stroke {:color [:color/hsl hue 0.5 0.5]
:width 1 :dash [5 5]}})
orbits)
;; 2D moon trails
trails (into []
(for [{:keys [rx speed hue]} orbits
trail (range 12)]
(let [ry (* rx 0.35)
ma (- (* angle speed) (* trail 0.06))
fade (/ 1.0 (+ 1.0 (* 1.5 trail)))]
{:node/type :shape/circle
:circle/center [(+ cx (* rx (Math/cos ma)))
(+ cy (* ry (Math/sin ma)))]
:circle/radius (* 3.5 fade)
:node/opacity (* 0.4 fade)
:style/fill [:color/hsl hue 0.7 0.6]})))
;; 2D moons
moons (mapv (fn [{:keys [rx speed moon-r hue]}]
(let [ry (* rx 0.35)
ma (* angle speed)]
{:node/type :shape/circle
:circle/center [(+ cx (* rx (Math/cos ma)))
(+ cy (* ry (Math/sin ma)))]
:circle/radius moon-r
:style/fill [:color/hsl hue 0.8 0.55]
:style/stroke {:color [:color/hsl hue 0.6 0.75]
:width 1}}))
orbits)
;; 2D twinkling stars
stars (mapv (fn [i]
(let [blink (+ 0.2 (* 0.8 (Math/abs
(Math/sin (+ (* i 1.1) (* t 5))))))]
{:node/type :shape/circle
:circle/center [(mod (* i 137.508) 500)
(mod (* i 91.123) 500)]
:circle/radius (if (zero? (mod i 9)) 1.8 0.7)
:node/opacity blink
:style/fill [:color/rgb 255 255 255]}))
(range 50))]
{:image/size [500 500]
:image/background [:color/rgb 4 4 12]
:image/nodes (into [] (concat stars rings trails [planet] moons))}))]
{:frames (anim/frames 60 frame-fn) :fps 30}))Particles

3D Fountain with Orbiting Camera
Particles in 3D space projected through an orbiting perspective camera, depth-sorted with pillars.
View source
(defn ^{:example {:output "particle-fountain-3d.gif"
:title "3D Fountain with Orbiting Camera"
:desc "Particles in 3D space projected through an orbiting perspective camera, depth-sorted with pillars."}}
fountain-3d []
(let [config {:particle/emitter {:emitter/type :circle
:emitter/position [0.0 0.0 0.0]
:emitter/radius 0.3
:emitter/rate 50
:emitter/direction [0 1 0]
:emitter/spread 0.25
:emitter/speed [3 6]}
:particle/lifetime [1.0 2.0]
:particle/forces [{:force/type :gravity
:force/acceleration [0 -5 0]}]
:particle/size [2 4 3 1]
:particle/opacity [0.3 0.9 0.6 0.0]
:particle/color [[:color/rgb 150 220 255]
[:color/rgb 80 160 255]
[:color/rgb 30 80 200]]
:particle/seed 42
:particle/max-count 300}
;; Simulate once — raw states with 3D positions
sim-states (vec (particle/states config 90 {:fps 30}))
pillar-positions [[3 0 0] [-3 0 0] [0 0 3] [0 0 -3]
[2.1 0 2.1] [-2.1 0 2.1]
[2.1 0 -2.1] [-2.1 0 -2.1]]
frame-fn
(fn [t]
(let [i (int (* t 89))
;; Camera orbits a full circle
proj (s3d/perspective {:scale 45 :origin [200 270]
:yaw (* t 2.0 Math/PI)
:pitch -0.4 :distance 10})
light {:light/direction [0.5 0.8 0.4]
:light/ambient 0.25 :light/intensity 0.75}
;; Re-render particles with this frame's projection
particles (particle/render-frame
(nth sim-states i) config
{:projection proj})
;; Static 3D pillars
pillars (mapv
(fn [pos]
(s3d/cylinder proj pos 0.25 2.5
{:style {:style/fill [:color/rgb 140 120 100]
:style/stroke {:color [:color/rgb 80 70 60]
:width 0.3}}
:light light :segments 8}))
pillar-positions)]
{:image/size [400 400]
:image/background [:color/rgb 8 8 20]
;; depth-sort interleaves particles and mesh faces
:image/nodes (s3d/depth-sort pillars particles)}))]
{:frames (anim/frames 90 frame-fn) :fps 30}))
Campfire
Fire and ember particles over log silhouettes with a pulsing glow.
View source
(defn ^{:example {:output "particle-campfire.gif"
:title "Campfire"
:desc "Fire and ember particles over log silhouettes with a pulsing glow."}}
campfire []
(let [;; Fire rising from the base
fire-frames (vec (particle/simulate
(particle/with-position particle/fire [200 320])
60 {:fps 30}))
;; Slow-rising embers with a separate seed
ember-config (-> particle/sparks
(particle/with-position [200 325])
(particle/with-seed 77)
(assoc :particle/emitter
{:emitter/type :area
:emitter/position [200 325]
:emitter/size [60 8]
:emitter/rate 8
:emitter/direction [0 -1]
:emitter/spread 0.6
:emitter/speed [20 60]})
(assoc :particle/lifetime [1.0 3.0])
(assoc :particle/size [1 2 1])
(assoc :particle/opacity [0.0 1.0 0.8 0.0])
(assoc :particle/color [[:color/rgb 255 200 50]
[:color/rgb 255 120 20]
[:color/rgb 200 60 0]])
(assoc :particle/forces
[{:force/type :gravity
:force/acceleration [0 -20]}
{:force/type :wind
:force/direction [1 0]
:force/strength 5}]))
ember-frames (vec (particle/simulate ember-config 60 {:fps 30}))
frame-fn
(fn [t]
(let [i (int (* t 59))
glow (+ 0.15 (* 0.05 (Math/sin (* t 8 Math/PI))))]
{:image/size [400 400]
:image/background [:color/rgb 8 5 15]
:image/nodes
(into
[{:node/type :shape/circle
:circle/center [200 330]
:circle/radius (+ 80 (* 20 (Math/sin (* t 6 Math/PI))))
:style/fill [:color/rgb 255 80 0]
:node/opacity glow}
{:node/type :shape/rect
:rect/xy [155 335] :rect/size [90 12]
:style/fill [:color/rgb 30 15 5]
:node/transform [[:transform/rotate -0.15]]}
{:node/type :shape/rect
:rect/xy [165 340] :rect/size [80 10]
:style/fill [:color/rgb 25 12 3]
:node/transform [[:transform/rotate 0.1]]}]
(concat (nth fire-frames i)
(nth ember-frames i)))}))]
{:frames (anim/frames 60 frame-fn) :fps 30}))
Fireworks
Three staggered bursts in red, blue, and gold.
View source
(defn ^{:example {:output "particle-fireworks.gif"
:title "Fireworks"
:desc "Three staggered bursts in red, blue, and gold."}}
fireworks []
(let [make-burst
(fn [pos seed colors]
(vec (particle/simulate
(-> particle/sparks
(particle/with-position pos)
(particle/with-seed seed)
(assoc :particle/emitter
{:emitter/type :point
:emitter/position pos
:emitter/burst 60
:emitter/direction [0 -1]
:emitter/spread Math/PI
:emitter/speed [80 250]})
(assoc :particle/lifetime [0.5 1.5])
(assoc :particle/size [3 4 2 1])
(assoc :particle/opacity [1.0 0.9 0.5 0.0])
(assoc :particle/color colors))
90 {:fps 30})))
burst1 (make-burst [120 150] 11
[[:color/rgb 255 100 100] [:color/rgb 255 50 50]
[:color/rgb 200 0 0]])
burst2 (make-burst [280 120] 22
[[:color/rgb 100 200 255] [:color/rgb 50 150 255]
[:color/rgb 0 80 200]])
burst3 (make-burst [200 180] 33
[[:color/rgb 255 220 80] [:color/rgb 255 180 0]
[:color/rgb 200 100 0]])
frame-fn
(fn [t]
(let [i (int (* t 89))]
{:image/size [400 400]
:image/background [:color/rgb 5 5 15]
:image/nodes
(into []
(concat (nth burst1 i)
(if (>= i 10) (nth burst2 (- i 10)) [])
(if (>= i 20) (nth burst3 (- i 20)) [])))}))]
{:frames (anim/frames 90 frame-fn) :fps 30}))
Snowfall
Gentle snow drifting over moonlit mountain silhouettes.
View source
(defn ^{:example {:output "particle-snowfall.gif"
:title "Snowfall"
:desc "Gentle snow drifting over moonlit mountain silhouettes."}}
snowfall []
(let [snow-config (-> particle/snow
(assoc-in [:particle/emitter :emitter/position] [-20 -10])
(assoc-in [:particle/emitter :emitter/position-to] [420 -10])
(assoc :particle/max-count 200))
snow-frames (vec (particle/simulate snow-config 90 {:fps 30}))
mountain1 [[-10 400] [50 280] [120 310] [160 240] [220 290]
[260 220] [300 260] [350 200] [410 300] [410 400]]
mountain2 [[-10 400] [30 320] [100 340] [180 280] [250 310]
[320 260] [380 300] [410 340] [410 400]]
frame-fn
(fn [t]
(let [i (int (* t 89))]
{:image/size [400 400]
:image/background [:color/rgb 15 20 40]
:image/nodes
(into
[{:node/type :shape/path
:path/commands (into [[:move-to (first mountain2)]]
(conj (mapv (fn [p] [:line-to p])
(rest mountain2))
[:close]))
:style/fill [:color/rgb 25 35 55]}
{:node/type :shape/path
:path/commands (into [[:move-to (first mountain1)]]
(conj (mapv (fn [p] [:line-to p])
(rest mountain1))
[:close]))
:style/fill [:color/rgb 35 45 70]}
{:node/type :shape/circle
:circle/center [320 80] :circle/radius 25
:style/fill [:color/rgb 220 225 240]
:node/opacity 0.8}
{:node/type :shape/circle
:circle/center [320 80] :circle/radius 50
:style/fill [:color/rgb 180 190 220]
:node/opacity 0.1}]
(nth snow-frames i))}))]
{:frames (anim/frames 90 frame-fn) :fps 30}))
Volcanic Eruption
3D lava and smoke particles erupting from a scene3d cone mesh.
View source
(defn ^{:example {:output "particle-volcano-3d.gif"
:title "Volcanic Eruption"
:desc "3D lava and smoke particles erupting from a scene3d cone mesh."}}
volcanic-eruption []
(let [proj (s3d/perspective {:scale 50 :origin [200 340]
:yaw 0.3 :pitch -0.2 :distance 10})
light {:light/direction [0.5 0.8 0.3]
:light/ambient 0.3 :light/intensity 0.7}
lava (vec (particle/simulate
{:particle/emitter {:emitter/type :sphere
:emitter/position [0 3.5 0]
:emitter/radius 0.4
:emitter/rate 35
:emitter/direction [0 1 0]
:emitter/spread 0.5
:emitter/speed [3 8]}
:particle/lifetime [0.6 1.8]
:particle/forces [{:force/type :gravity
:force/acceleration [0 -4 0]}]
:particle/size [3 5 4 2]
:particle/opacity [0.5 1.0 0.8 0.0]
:particle/color [[:color/rgb 255 255 150]
[:color/rgb 255 200 20]
[:color/rgb 255 60 0]
[:color/rgb 150 20 0]]
:particle/projection proj
:particle/seed 42
:particle/max-count 250}
90 {:fps 30}))
volcano (s3d/cone proj [0 0 0] 3.0 3.5
{:style {:style/fill [:color/rgb 80 50 30]
:style/stroke {:color [:color/rgb 60 35 20]
:width 0.5}}
:light light :segments 16})
frame-fn
(fn [t]
{:image/size [400 400]
:image/background [:color/rgb 12 8 18]
:image/nodes (into [volcano]
(nth lava (int (* t 89))))})]
{:frames (anim/frames 90 frame-fn) :fps 30}))Typography

Animated Circular Text
Text following a circular path, rotating around a gradient sphere.
View source
(defn ^{:example {:output "text-circular.gif"
:title "Animated Circular Text"
:desc "Text following a circular path, rotating around a gradient sphere."}}
animated-circular-text []
(let [frame-fn
(fn [t]
(let [r 130 cx 200 cy 200]
{:image/size [400 400]
:image/background [:color/rgb 245 243 238]
:image/nodes
[{:node/type :shape/text-on-path
:text/content "EIDO\u00B7DATA\u00B7ART\u00B7"
:text/font {:font/family "SansSerif" :font/size 20 :font/weight :bold}
:text/path (let [steps 64
offset (* t 2 Math/PI)]
(into [[:move-to [(+ cx (* r (Math/cos offset)))
(+ cy (* r (Math/sin offset)))]]]
(map (fn [i]
(let [a (+ offset (* (/ (inc i) steps)
2 Math/PI))]
[:line-to [(+ cx (* r (Math/cos a)))
(+ cy (* r (Math/sin a)))]]))
(range steps))))
:text/spacing 2
:style/fill [:color/rgb 60 60 80]}
{:node/type :shape/circle
:circle/center [cx cy]
:circle/radius 80
:style/fill {:gradient/type :radial
:gradient/center [cx cy]
:gradient/radius 80
:gradient/stops [[0.0 [:color/rgb 255 120 100]]
[1.0 [:color/rgb 200 60 120]]]}}]}))]
{:frames (anim/frames 90 frame-fn) :fps 30}))
Gradient Text with Shadow
Layered text using text-stack — a shadow layer offset by a few pixels, then a gradient fill on top.
View source
(defn ^{:example {:output "text-gradient-shadow.png"
:title "Gradient Text with Shadow"
:desc "Layered text using text-stack — a shadow layer offset by a few pixels, then a gradient fill on top."}}
gradient-text-with-shadow []
{:image/size [500 200]
:image/background [:color/rgb 15 15 25]
:image/nodes
[(scene/text-stack "Typography" [40 140]
{:font/family "Serif" :font/size 72 :font/weight :bold}
[;; shadow layer
{:style/fill [:color/rgba 0 0 0 0.4]
:node/transform [[:transform/translate 3 3]]}
;; gradient fill
{:style/fill {:gradient/type :linear
:gradient/from [0 60]
:gradient/to [500 140]
:gradient/stops [[0.0 [:color/rgb 255 100 120]]
[0.5 [:color/rgb 255 220 100]]
[1.0 [:color/rgb 100 200 255]]]}}])]})
Neon Glow
A blurred copy behind crisp text creates a glow effect using :group/filter [:blur 8].
View source
(defn ^{:example {:output "text-neon-glow.png"
:title "Neon Glow"
:desc "A blurred copy behind crisp text creates a glow effect using :group/filter [:blur 8]."}}
neon-glow []
{:image/size [500 200]
:image/background [:color/rgb 10 10 20]
:image/nodes
[;; glow layer (blurred)
{:node/type :group
:group/composite :src-over
:group/filter [:blur 8]
:group/children
[{:node/type :shape/text
:text/content "NEON"
:text/font {:font/family "SansSerif" :font/size 80 :font/weight :bold}
:text/origin [70 130]
:style/fill [:color/rgb 0 255 200]}]}
;; crisp text on top
{:node/type :shape/text
:text/content "NEON"
:text/font {:font/family "SansSerif" :font/size 80 :font/weight :bold}
:text/origin [70 130]
:style/fill [:color/rgb 200 255 240]}]})
Per-Glyph Rainbow
Each glyph styled independently using :shape/text-glyphs, cycling hue by 40 degrees per character.
View source
(defn ^{:example {:output "text-rainbow-glyphs.png"
:title "Per-Glyph Rainbow"
:desc "Each glyph styled independently using :shape/text-glyphs, cycling hue by 40 degrees per character."}}
per-glyph-rainbow []
{:image/size [600 160]
:image/background [:color/rgb 20 20 35]
:image/nodes
[{:node/type :shape/text-glyphs
:text/content "CHROMATIC"
:text/font {:font/family "SansSerif" :font/size 64 :font/weight :bold}
:text/origin [20 110]
:text/glyphs (vec (map-indexed
(fn [i _]
{:glyph/index i
:style/fill [:color/hsl (mod (* i 40) 360) 0.85 0.6]})
"CHROMATIC"))
:style/fill [:color/rgb 255 255 255]}]})
Rotating 3D Extruded Text
Each letter extruded in 3D with phase-shifted sine waves driving vertical bounce and pitch rock.
View source
(defn ^{:example {:output "text-3d-rotate.gif"
:title "Rotating 3D Extruded Text"
:desc "Each letter extruded in 3D with phase-shifted sine waves driving vertical bounce and pitch rock."}}
rotating-3d-text []
(let [letters "EIDO"
font {:font/family "SansSerif" :font/size 36 :font/weight :bold}
glyph-data (text/text->glyph-paths letters font)
advance (text/text-advance letters font)
half-w (/ advance 2.0)
letter-xs (mapv (fn [i]
(let [[gx] (:position (nth glyph-data i))
next-gx (if (< i (dec (count letters)))
((:position (nth glyph-data (inc i))) 0)
advance)]
(* 3.0 (- (+ gx (/ (- next-gx gx) 2.0)) half-w))))
(range (count letters)))
frame-fn
(fn [t]
{:image/size [600 350]
:image/background [:color/rgb 20 22 35]
:image/nodes
(vec (map-indexed
(fn [i c]
(let [;; vertical wave (2 cycles)
wave (* 20.0 (Math/sin (- (* t 2 Math/PI 2) (* i 1.0))))
;; pitch rock (2 cycles, offset phase)
rock (* 0.4 (Math/sin (+ (* t 2 Math/PI 2)
(* i 1.5) (* Math/PI 0.5))))
proj (s3d/perspective
{:scale 3.0
:origin [(+ 300 (nth letter-xs i))
(- 175 wave)]
:yaw 0.0
:pitch (+ (* Math/PI 0.5) rock)
:distance 250})]
(s3d/text-3d proj (str c) font 12
{:style {:style/fill [:color/rgb 255 140 60]}
:light {:light/direction [0 -1 0.5]
:light/ambient 0.25
:light/intensity 0.75}})))
letters))})]
{:frames (anim/frames 60 frame-fn) :fps 30}))Showcase

Aurora Borealis
Shimmering curtains of light driven by layered 3D noise.
View source
(defn ^{:example {:output "showcase-aurora.gif"
:title "Aurora Borealis"
:desc "Shimmering curtains of light driven by layered 3D noise."}}
aurora []
{:frames
(anim/frames 80
(fn [t]
(let [w 600 h 350
curtains
(vec
(for [layer (range 4)
x (range 0 w 3)]
(let [nx (* x 0.006)
ny (* layer 1.5)
nz (* t 2.0)
wave (noise/perlin3d nx ny nz {:seed (+ 10 layer)})
y-base (+ 80 (* layer 30) (* 60 wave))
height (+ 80 (* 50 (Math/abs wave)))
hue (+ 120 (* layer 25) (* 30 wave))
alpha (* (- 0.35 (* layer 0.06))
(+ 0.6 (* 0.4 (Math/sin (+ (* x 0.02) (* t 6))))))]
{:node/type :shape/line
:line/from [x y-base]
:line/to [x (+ y-base height)]
:node/opacity alpha
:style/stroke {:color [:color/hsl hue 0.8 0.6]
:width 3}})))
stars
(vec
(for [i (range 60)]
(let [sx (mod (* i 137.508) w)
sy (mod (* i 59.7) (* h 0.5))
blink (+ 0.3 (* 0.7 (Math/abs (Math/sin (+ (* i 2.1) (* t 8))))))]
{:node/type :shape/circle
:circle/center [sx sy]
:circle/radius (if (zero? (mod i 7)) 1.5 0.6)
:node/opacity blink
:style/fill [:color/rgb 255 255 240]})))]
{:image/size [w h]
:image/background [:color/rgb 5 5 20]
:image/nodes (into stars curtains)})))
:fps 24})
Bauhaus Composition
Bold geometric abstraction in primary colors with overlapping forms.
View source
(defn ^{:example {:output "showcase-bauhaus.png"
:title "Bauhaus Composition"
:desc "Bold geometric abstraction in primary colors with overlapping forms."}}
bauhaus []
(let [rng (java.util.Random. 42)]
{:image/size [500 500]
:image/background [:color/rgb 245 240 230]
:image/nodes
[;; Large red circle
{:node/type :shape/circle
:circle/center [170 200]
:circle/radius 120
:style/fill [:color/rgb 210 35 35]}
;; Blue rectangle
{:node/type :shape/rect
:rect/xy [250 100]
:rect/size [180 280]
:style/fill [:color/rgb 30 60 160]}
;; Yellow triangle
(assoc (scene/triangle [350 80] [480 350] [220 350])
:style/fill [:color/rgb 240 200 30])
;; Black lines — constructivist grid
{:node/type :shape/line :line/from [0 350] :line/to [500 350]
:style/stroke {:color [:color/rgb 20 20 20] :width 4}}
{:node/type :shape/line :line/from [250 0] :line/to [250 500]
:style/stroke {:color [:color/rgb 20 20 20] :width 4}}
{:node/type :shape/line :line/from [0 180] :line/to [500 180]
:style/stroke {:color [:color/rgb 20 20 20] :width 2}}
;; Small accent circles
{:node/type :shape/circle
:circle/center [400 150]
:circle/radius 30
:style/fill [:color/rgb 20 20 20]}
{:node/type :shape/circle
:circle/center [120 400]
:circle/radius 45
:style/fill [:color/rgb 240 200 30]
:style/stroke {:color [:color/rgb 20 20 20] :width 3}}
;; White overlay circle for depth
{:node/type :shape/circle
:circle/center [300 300]
:circle/radius 65
:style/fill [:color/rgb 245 240 230]
:style/stroke {:color [:color/rgb 20 20 20] :width 3}}
;; Thin diagonal
{:node/type :shape/line :line/from [0 0] :line/to [500 500]
:style/stroke {:color [:color/rgb 20 20 20] :width 1}}]}))
Bioluminescent Jellyfish
Pulsing jellyfish with flowing tentacles and soft glow.
View source
(defn ^{:example {:output "showcase-jellyfish.gif"
:title "Bioluminescent Jellyfish"
:desc "Pulsing jellyfish with flowing tentacles and soft glow."}}
jellyfish []
{:frames
(anim/frames 60
(fn [t]
(let [cx 250 cy 160
pulse (+ 0.85 (* 0.15 (Math/sin (* t 2 Math/PI))))
bell-r (* 70 pulse)
;; Bell dome — layered translucent arcs
bell (vec
(for [i (range 8)]
(let [r (- bell-r (* i 4))
hue (+ 260 (* i 8))
alpha (- 0.25 (* i 0.025))]
{:node/type :shape/arc
:arc/center [cx cy]
:arc/rx r
:arc/ry (* r 0.65)
:arc/start 180 :arc/extent 180
:arc/mode :chord
:node/opacity alpha
:style/fill [:color/hsl hue 0.7 0.6]})))
;; Tentacles — sine-driven flowing curves
tentacles
(vec
(for [arm (range 7)]
(let [base-x (+ cx (* (- arm 3) 18))
pts (for [seg (range 20)]
(let [seg-t (/ seg 19.0)
sway (* 25 seg-t
(Math/sin (+ (* seg-t 4)
(* t 2 Math/PI)
(* arm 0.9))))
x (+ base-x sway)
y (+ (+ cy (* bell-r 0.4)) (* seg-t 180))]
[x y]))
hue (+ 250 (* arm 15))]
{:node/type :shape/path
:path/commands (into [[:move-to (first pts)]]
(mapv #(vector :line-to %) (rest pts)))
:stroke/profile :pointed
:node/opacity (- 0.6 (* arm 0.05))
:style/stroke {:color [:color/hsl hue 0.6 0.55]
:width (- 5 (* arm 0.5))}})))
;; Bioluminescent specks
specks (vec
(for [i (range 20)]
(let [angle (+ (* i 0.314) (* t 1.5))
r (+ 30 (* 60 (Math/abs (Math/sin (+ (* i 0.7) t)))))
sx (+ cx (* r (Math/cos angle)))
sy (+ cy (* r 0.5 (Math/sin angle)) 40)
alpha (* 0.7 (Math/abs (Math/sin (+ (* i 1.3) (* t 5)))))]
{:node/type :shape/circle
:circle/center [sx sy]
:circle/radius 2
:node/opacity alpha
:style/fill [:color/hsl 180 0.9 0.7]})))]
{:image/size [500 450]
:image/background [:color/rgb 3 5 20]
:image/nodes (into [] (concat tentacles bell specks))})))
:fps 24})
Breathing Mandala
Layered radial symmetry that expands and contracts with color cycling.
View source
(defn ^{:example {:output "showcase-breathing-mandala.gif"
:title "Breathing Mandala"
:desc "Layered radial symmetry that expands and contracts with color cycling."}}
breathing-mandala []
{:frames
(anim/frames 50
(fn [t]
(let [cx 250 cy 250
breath (anim/ping-pong t)
eased (anim/ease-in-out-cubic breath)
layers
(vec
(for [layer (range 4)]
(let [n-petals (+ 6 (* layer 2))
base-r (+ 30 (* layer 45))
r (* base-r (+ 0.7 (* 0.3 eased)))
rotation (+ (* layer 0.2) (* t Math/PI 0.5))]
{:node/type :symmetry
:symmetry/type :radial
:symmetry/n n-petals
:symmetry/center [cx cy]
:group/children
[(let [petal-len (* r 0.6)
petal-w (* r 0.2)
hue (mod (+ (* layer 60) (* t 120)) 360)]
{:node/type :shape/path
:path/commands [[:move-to [cx (- cy (* r 0.3))]]
[:curve-to [(+ cx petal-w) (- cy (* r 0.5))]
[(+ cx petal-w) (- cy r)]
[cx (- cy r petal-len)]]
[:curve-to [(- cx petal-w) (- cy r)]
[(- cx petal-w) (- cy (* r 0.5))]
[cx (- cy (* r 0.3))]]]
:node/opacity (- 0.6 (* layer 0.1))
:style/fill [:color/hsl hue 0.7 0.5]
:style/stroke {:color [:color/hsl hue 0.5 0.35] :width 0.5}})]
:node/transform [[:transform/rotate rotation]]})))
;; Center dot
center {:node/type :shape/circle
:circle/center [cx cy]
:circle/radius (* 15 (+ 0.8 (* 0.2 eased)))
:style/fill [:color/rgb 255 240 200]}]
{:image/size [500 500]
:image/background [:color/rgb 12 10 18]
:image/nodes (conj layers center)})))
:fps 20})
Cosmic Eye
A radial iris pattern with orbiting rings and shifting nebula colors.
View source
(defn ^{:example {:output "showcase-cosmic-eye.gif"
:title "Cosmic Eye"
:desc "A radial iris pattern with orbiting rings and shifting nebula colors."}}
cosmic-eye []
{:frames
(anim/frames 60
(fn [t]
(let [cx 250 cy 250
;; Iris — dense radial lines with noise-driven color
iris
(vec
(for [i (range 180)]
(let [angle (+ (* i (/ (* 2 Math/PI) 180)) (* t 0.3))
r1 35
r2 (+ 80 (* 25 (noise/perlin2d
(* 2 (Math/cos angle))
(* 2 (Math/sin angle))
{:seed 42})))
x1 (+ cx (* r1 (Math/cos angle)))
y1 (+ cy (* r1 (Math/sin angle)))
x2 (+ cx (* r2 (Math/cos angle)))
y2 (+ cy (* r2 (Math/sin angle)))
hue (+ 30 (* 20 (Math/sin (+ (* i 0.1) (* t 3)))))]
{:node/type :shape/line
:line/from [x1 y1]
:line/to [x2 y2]
:node/opacity 0.7
:style/stroke {:color [:color/hsl hue 0.9 0.45]
:width 1.2}})))
;; Pupil
pupil {:node/type :shape/circle
:circle/center [cx cy]
:circle/radius 33
:style/fill [:color/rgb 5 5 10]
:effect/glow {:blur 10 :color [:color/rgb 0 0 0] :opacity 0.5}}
;; Pupil highlight
highlight {:node/type :shape/circle
:circle/center [(- cx 8) (- cy 10)]
:circle/radius 8
:node/opacity 0.6
:style/fill [:color/rgb 255 255 255]}
;; Outer rings
rings
(vec
(for [i (range 5)]
(let [r (+ 90 (* i 25))
phase (+ (* t 2 Math/PI) (* i 0.8))
alpha (- 0.3 (* i 0.04))
hue (+ 200 (* i 30) (* 30 (Math/sin phase)))]
{:node/type :shape/circle
:circle/center [cx cy]
:circle/radius r
:node/opacity alpha
:style/stroke {:color [:color/hsl hue 0.5 0.5]
:width (- 2.5 (* i 0.3))}})))
;; Nebula glow behind
nebula
(vec
(for [i (range 6)]
(let [angle (+ (* i (/ Math/PI 3)) (* t 0.5))
r (+ 100 (* 40 (Math/sin (+ (* i 1.2) (* t 2)))))
nx (+ cx (* r (Math/cos angle)))
ny (+ cy (* r (Math/sin angle)))
hue (+ 220 (* i 25))]
{:node/type :shape/circle
:circle/center [nx ny]
:circle/radius 80
:node/opacity 0.06
:style/fill [:color/hsl hue 0.7 0.5]})))]
{:image/size [500 500]
:image/background [:color/rgb 5 5 12]
:image/nodes (into [] (concat nebula rings iris [pupil highlight]))})))
:fps 24})
Crystal Geode
Voronoi tessellation with gem-like gradient fills and dark edges.
View source
(defn ^{:example {:output "showcase-geode.png"
:title "Crystal Geode"
:desc "Voronoi tessellation with gem-like gradient fills and dark edges."}}
geode []
(let [pts (scatter/poisson-disk 30 30 470 470 28 77)
cells (voronoi/voronoi-cells pts 0 0 500 500)
rng (java.util.Random. 77)
gem-stops [[0.0 [:color/rgb 20 0 40]]
[0.2 [:color/rgb 80 20 120]]
[0.4 [:color/rgb 40 100 180]]
[0.6 [:color/rgb 60 200 180]]
[0.8 [:color/rgb 180 80 200]]
[1.0 [:color/rgb 255 200 255]]]]
{:image/size [500 500]
:image/background [:color/rgb 10 5 15]
:image/nodes
(vec
(for [[i cell] (map-indexed vector cells)]
(let [[px py] (nth pts i)
;; Distance from center determines color
dx (- px 250) dy (- py 250)
dist (/ (Math/sqrt (+ (* dx dx) (* dy dy))) 350.0)
t (min 1.0 dist)
color (palette/gradient-map gem-stops t)]
(-> cell
(assoc :style/fill color)
(assoc :style/stroke {:color [:color/rgb 8 3 12] :width 2.5})
(assoc :node/opacity (+ 0.7 (* 0.3 (- 1.0 t))))))))}))
Digital Rain
Cascading columns of characters fading into the dark.
View source
(defn ^{:example {:output "showcase-matrix.gif"
:title "Digital Rain"
:desc "Cascading columns of characters fading into the dark."}}
digital-rain []
{:frames
(anim/frames 40
(fn [t]
(let [w 400 h 500
cols 30
col-w (/ (double w) cols)
drops
(vec
(for [col (range cols)
row (range 30)]
(let [;; Each column falls at a different speed
rng (java.util.Random. (long col))
speed (+ 0.5 (* (.nextDouble rng) 1.5))
head-y (mod (* (+ t (* col 0.03)) speed h) (* h 1.3))
y (- head-y (* row 16))
x (+ 2 (* col col-w))
;; Fade based on distance from head
fade (max 0 (- 1.0 (* row 0.06)))
brightness (if (zero? row) 1.0 (* 0.7 fade))]
(when (and (> y -10) (< y (+ h 10)) (> fade 0.05))
{:node/type :shape/rect
:rect/xy [x y]
:rect/size [(- col-w 2) 12]
:rect/corner-radius 1
:node/opacity (* fade 0.8)
:style/fill [:color/rgb
(int (* 30 brightness))
(int (* 255 brightness))
(int (* 60 brightness))]}))))]
{:image/size [w h]
:image/background [:color/rgb 0 5 0]
:image/nodes (vec (remove nil? drops))})))
:fps 15})
Generative Textile
Woven pattern with noise-driven color and hatched texture.
View source
(defn ^{:example {:output "showcase-textile.png"
:title "Generative Textile"
:desc "Woven pattern with noise-driven color and hatched texture."}}
textile []
(let [cols 20 rows 20
cell-w 25 cell-h 25
pal (:earth palette/palettes)]
{:image/size [500 500]
:image/background [:color/rgb 240 235 225]
:image/nodes
(vec
(for [row (range rows)
col (range cols)]
(let [x (* col cell-w)
y (* row cell-h)
v (noise/perlin2d (* col 0.15) (* row 0.15) {:seed 42})
color-idx (mod (int (* (+ 0.5 (* 0.5 v)) 4.9)) 5)
;; Alternate hatch direction per checkerboard
angle (if (even? (+ row col)) 45 -45)]
{:node/type :shape/rect
:rect/xy [x y]
:rect/size [cell-w cell-h]
:style/fill {:fill/type :hatch
:hatch/angle angle
:hatch/spacing 3
:hatch/stroke-width 0.6
:hatch/color (nth pal color-idx)
:hatch/background [:color/rgb 240 235 225]}
:style/stroke {:color [:color/rgba 0 0 0 0.1] :width 0.5}})))}))
Heartbeat
An ECG-style trace that pulses across the screen.
View source
(defn ^{:example {:output "showcase-heartbeat.gif"
:title "Heartbeat"
:desc "An ECG-style trace that pulses across the screen."}}
heartbeat []
{:frames
(anim/frames 60
(fn [t]
(let [w 500 h 200
;; ECG waveform function
ecg (fn [x]
(let [phase (mod x 1.0)]
(cond
(< phase 0.1) 0
(< phase 0.15) (* -15 (/ (- phase 0.1) 0.05))
(< phase 0.2) (* 60 (/ (- phase 0.15) 0.05))
(< phase 0.25) (* -20 (/ (- phase 0.2) 0.05))
(< phase 0.35) (* 5 (Math/sin (* (/ (- phase 0.25) 0.1) Math/PI)))
:else 0)))
pts (for [i (range 200)]
(let [x-norm (/ (double i) 200)
x (* x-norm w)
wave-x (+ (* x-norm 2.0) (* t 2.0))
y (+ (/ h 2.0) (ecg (mod wave-x 1.0)))]
[x y]))
;; Trail — fading line
trail {:node/type :shape/path
:path/commands (into [[:move-to (first pts)]]
(mapv #(vector :line-to %) (rest pts)))
:style/stroke {:color [:color/rgb 0 220 100] :width 2}}
;; Glow dot at the leading edge
[lead-x lead-y] (last pts)
dot {:node/type :shape/circle
:circle/center [lead-x lead-y]
:circle/radius 4
:style/fill [:color/rgb 0 255 120]}
;; Grid lines
grid (vec
(concat
(for [i (range 0 (inc w) 25)]
{:node/type :shape/line
:line/from [i 0] :line/to [i h]
:node/opacity 0.08
:style/stroke {:color [:color/rgb 0 200 100] :width 0.5}})
(for [i (range 0 (inc h) 25)]
{:node/type :shape/line
:line/from [0 i] :line/to [w i]
:node/opacity 0.08
:style/stroke {:color [:color/rgb 0 200 100] :width 0.5}})))]
{:image/size [w h]
:image/background [:color/rgb 5 10 5]
:image/nodes (into grid [trail dot])})))
:fps 24})
Moire Interference
Two rotating line grids creating mesmerizing interference patterns.
View source
(defn ^{:example {:output "showcase-moire.gif"
:title "Moire Interference"
:desc "Two rotating line grids creating mesmerizing interference patterns."}}
moire []
{:frames
(anim/frames 90
(fn [t]
(let [size 400
cx 200 cy 200
angle1 (* t Math/PI 0.5)
angle2 (- (* t Math/PI 0.5))
make-grid
(fn [angle alpha]
(vec
(for [i (range -30 31)]
(let [offset (* i 8)
cos-a (Math/cos angle) sin-a (Math/sin angle)
x1 (+ cx (* offset cos-a) (* -300 sin-a))
y1 (+ cy (* offset sin-a) (* 300 cos-a))
x2 (+ cx (* offset cos-a) (* 300 sin-a))
y2 (+ cy (* offset sin-a) (* -300 cos-a))]
{:node/type :shape/line
:line/from [x1 y1]
:line/to [x2 y2]
:node/opacity alpha
:style/stroke {:color [:color/rgb 255 255 255]
:width 1.5}}))))]
{:image/size [size size]
:image/background [:color/rgb 0 0 0]
:image/nodes (into (make-grid angle1 0.5)
(make-grid angle2 0.5))})))
:fps 30})
Neon Grid
Retro wireframe grid receding into the horizon with pulsing neon.
View source
(defn ^{:example {:output "showcase-neon-grid.gif"
:title "Neon Grid"
:desc "Retro wireframe grid receding into the horizon with pulsing neon."}}
neon-grid []
{:frames
(anim/frames 40
(fn [t]
(let [w 500 h 350
horizon-y 120
;; Horizontal lines receding
h-lines (vec
(for [i (range 15)]
(let [progress (/ (double i) 14)
y (+ horizon-y (* (- h horizon-y)
(* progress progress)))
alpha (+ 0.2 (* 0.6 progress))
hue (+ 280 (* 40 (Math/sin (+ (* i 0.5) (* t 4)))))]
{:node/type :shape/line
:line/from [0 y]
:line/to [w y]
:node/opacity alpha
:style/stroke {:color [:color/hsl hue 0.9 0.6]
:width (+ 0.5 (* 1.5 progress))}})))
;; Vertical lines converging to vanishing point
v-lines (vec
(for [i (range 20)]
(let [x-bottom (+ -50 (* i (/ 600.0 19)))
vanish-x 250
alpha 0.35
hue (+ 300 (* 20 (Math/sin (+ (* i 0.3) (* t 3)))))]
{:node/type :shape/line
:line/from [x-bottom h]
:line/to [vanish-x horizon-y]
:node/opacity alpha
:style/stroke {:color [:color/hsl hue 0.8 0.5]
:width 0.8}})))
;; Sun circle at horizon
sun-pulse (+ 0.8 (* 0.2 (Math/sin (* t 2 Math/PI))))
sun {:node/type :shape/circle
:circle/center [250 horizon-y]
:circle/radius (* 50 sun-pulse)
:node/opacity 0.3
:style/fill [:color/hsl 320 0.8 0.6]}]
{:image/size [w h]
:image/background [:color/rgb 8 5 18]
:image/nodes (into [sun] (concat v-lines h-lines))})))
:fps 20})
Orbiting Spheres
Colored spheres in circular orbits around a central point.
View source
(defn ^{:example {:output "showcase-orbits.gif"
:title "Orbiting Spheres"
:desc "Colored spheres in circular orbits around a central point."}}
orbiting-spheres []
{:frames
(anim/frames 60
(fn [t]
(let [proj (s3d/perspective
{:scale 80 :origin [250 250]
:yaw 0.3 :pitch -0.5 :distance 7})
light {:light/direction [0.7 1.0 0.4]
:light/ambient 0.2 :light/intensity 0.8}
angle (* t 2 Math/PI)
spheres
(for [i (range 5)]
(let [orbit-r (+ 1.5 (* i 0.6))
speed (/ 1.0 (+ 1.0 (* i 0.4)))
a (+ (* angle speed) (* i (/ (* 2 Math/PI) 5)))
x (* orbit-r (Math/cos a))
z (* orbit-r (Math/sin a))
hue (* i 72)
mesh (s3d/sphere-mesh 0.35 12 8)]
(s3d/render-mesh proj
(s3d/translate-mesh mesh [x 0 z])
{:style {:style/fill [:color/hsl hue 0.7 0.5]}
:light light})))
;; Central sphere
center (s3d/render-mesh proj
(s3d/sphere-mesh 0.5 16 10)
{:style {:style/fill [:color/rgb 240 230 200]}
:light light})]
{:image/size [500 500]
:image/background [:color/rgb 8 8 15]
:image/nodes (into [center] spheres)})))
:fps 24})
Pixel Dissolve
A grid of squares that scatter and reassemble with color shifts.
View source
(defn ^{:example {:output "showcase-dissolve.gif"
:title "Pixel Dissolve"
:desc "A grid of squares that scatter and reassemble with color shifts."}}
pixel-dissolve []
{:frames
(anim/frames 50
(fn [t]
(let [cols 20 rows 20
cell 18
t-ping (anim/ping-pong t)
eased (anim/ease-in-out-cubic t-ping)]
{:image/size [400 400]
:image/background [:color/rgb 15 15 20]
:image/nodes
(vec
(for [row (range rows)
col (range cols)]
(let [home-x (+ 20 (* col cell))
home-y (+ 20 (* row cell))
;; Each cell scatters to a unique direction
rng (java.util.Random. (long (+ (* row 100) col)))
scatter-x (* (.nextGaussian rng) 80 eased)
scatter-y (* (.nextGaussian rng) 80 eased)
x (+ home-x scatter-x)
y (+ home-y scatter-y)
hue (mod (+ (* col 12) (* row 12) (* t 180)) 360)
size (* cell (- 1.0 (* 0.3 eased)))]
{:node/type :shape/rect
:rect/xy [x y]
:rect/size [size size]
:rect/corner-radius (* 4 eased)
:style/fill [:color/hsl hue 0.8 (+ 0.4 (* 0.2 eased))]})))})))
:fps 20})
Prism
Light beam splitting through a prism into a spectrum.
View source
(defn ^{:example {:output "showcase-prism.png"
:title "Prism"
:desc "Light beam splitting through a prism into a spectrum."}}
prism []
(let [;; Prism triangle
prism-shape (assoc (scene/triangle [250 120] [180 300] [320 300])
:style/fill [:color/rgba 180 200 220 0.3]
:style/stroke {:color [:color/rgb 200 210 220] :width 2})
;; Incoming white beam
beam {:node/type :shape/path
:path/commands [[:move-to [0 210]]
[:line-to [250 210]]]
:style/stroke {:color [:color/rgb 255 255 255] :width 3}}
;; Spectrum rays fanning out
spectrum-colors [[:color/rgb 255 0 0] [:color/rgb 255 127 0]
[:color/rgb 255 255 0] [:color/rgb 0 255 0]
[:color/rgb 0 0 255] [:color/rgb 75 0 130]
[:color/rgb 148 0 211]]
rays (vec
(for [i (range 7)]
(let [angle (+ 0.15 (* i 0.06))
end-x (+ 250 (* 300 (Math/cos angle)))
end-y (+ 210 (* 300 (Math/sin angle)))]
{:node/type :shape/path
:path/commands [[:move-to [250 210]]
[:line-to [end-x end-y]]]
:node/opacity 0.8
:style/stroke {:color (nth spectrum-colors i)
:width 2.5}})))]
{:image/size [600 400]
:image/background [:color/rgb 10 10 15]
:image/nodes (into [beam prism-shape] rays)}))
Rainstorm
Falling rain with expanding ripple circles on a dark surface.
View source
(defn ^{:example {:output "showcase-rainstorm.gif"
:title "Rainstorm"
:desc "Falling rain with expanding ripple circles on a dark surface."}}
rainstorm []
{:frames
(anim/frames 60
(fn [t]
(let [w 500 h 400
;; Rain drops — falling lines
drops
(vec
(for [i (range 80)]
(let [x (mod (+ (* i 137.508) (* t 50)) w)
phase (mod (+ (* i 0.618) (* t 3)) 1.0)
y1 (* phase h)
y2 (+ y1 15)
alpha (* 0.4 (- 1.0 (* phase 0.3)))]
{:node/type :shape/line
:line/from [x y1]
:line/to [(- x 2) y2]
:node/opacity alpha
:style/stroke {:color [:color/rgb 150 170 200]
:width 1}})))
;; Ripples — expanding circles at ground level
ground-y 320
ripples
(vec
(for [i (range 12)]
(let [rx (mod (* i 137.508 1.3) w)
phase (mod (+ (* i 0.37) t) 1.0)
r (max 0.5 (* 30 phase))
alpha (* 0.4 (- 1.0 phase))]
{:node/type :shape/ellipse
:ellipse/center [rx (+ ground-y (* 5 (Math/sin (* i 1.7))))]
:ellipse/rx r
:ellipse/ry (* r 0.3)
:node/opacity alpha
:style/stroke {:color [:color/rgb 100 130 170]
:width 1}})))
;; Ground — subtle gradient-like lines
ground
(vec
(for [i (range 6)]
(let [y (+ ground-y (* i 4))
alpha (- 0.2 (* i 0.03))]
{:node/type :shape/line
:line/from [0 y] :line/to [w y]
:node/opacity (max 0.02 alpha)
:style/stroke {:color [:color/rgb 60 70 90]
:width 1.5}})))]
{:image/size [w h]
:image/background [:color/rgb 15 18 28]
:image/nodes (into [] (concat ground ripples drops))})))
:fps 24})
Rotating Gem
Faceted 3D icosahedron-like gem with colorful face shading.
View source
(defn ^{:example {:output "showcase-gem-3d.gif"
:title "Rotating Gem"
:desc "Faceted 3D icosahedron-like gem with colorful face shading."}}
rotating-gem []
{:frames
(anim/frames 60
(fn [t]
(let [angle (* t 2 Math/PI)
proj (s3d/perspective
{:scale 120 :origin [200 200]
:yaw angle :pitch -0.4 :distance 4.5})
light {:light/direction [0.8 1.0 0.5]
:light/ambient 0.15
:light/intensity 0.85}
;; Build a gem shape from two cones meeting at the middle
top (-> (s3d/cone-mesh 1.2 1.8 8)
(s3d/rotate-mesh :x Math/PI)
(s3d/translate-mesh [0 0.9 0]))
bottom (-> (s3d/cone-mesh 1.2 1.2 8)
(s3d/translate-mesh [0 -0.6 0]))
gem (s3d/merge-meshes top bottom)]
{:image/size [400 400]
:image/background [:color/rgb 8 8 18]
:image/nodes
[(s3d/render-mesh proj gem
{:style {:style/fill [:color/rgb 80 180 220]
:style/stroke {:color [:color/rgb 120 220 255]
:width 0.4}}
:light light})]})))
:fps 30})
Sound Wave
Layered frequency bands pulsing like an audio spectrum.
View source
(defn ^{:example {:output "showcase-soundwave.gif"
:title "Sound Wave"
:desc "Layered frequency bands pulsing like an audio spectrum."}}
sound-wave []
{:frames
(anim/frames 60
(fn [t]
(let [w 600 h 300
n-bars 48
bands
(vec
(for [i (range n-bars)]
(let [x-norm (/ (double i) n-bars)
;; Simulate multiple frequency components
f1 (Math/sin (+ (* x-norm 3 Math/PI) (* t 2 Math/PI)))
f2 (* 0.6 (Math/sin (+ (* x-norm 7 Math/PI) (* t 2 Math/PI 1.6))))
f3 (* 0.3 (Math/sin (+ (* x-norm 13 Math/PI) (* t 2 Math/PI 2.3))))
amplitude (Math/abs (+ f1 f2 f3))
bar-h (max 1 (* 100 amplitude))
bar-w (/ (double w) n-bars)
x (* i bar-w)
hue (mod (+ (* x-norm 200) (* t 120)) 360)]
{:node/type :shape/rect
:rect/xy [x (- (/ h 2.0) (/ bar-h 2.0))]
:rect/size [bar-w bar-h]
:rect/corner-radius 2
:style/fill [:color/hsl hue 0.85 (+ 0.35 (* 0.25 amplitude))]})))
;; Reflection below
reflection
(vec
(for [i (range n-bars)]
(let [x-norm (/ (double i) n-bars)
f1 (Math/sin (+ (* x-norm 3 Math/PI) (* t 2 Math/PI)))
f2 (* 0.6 (Math/sin (+ (* x-norm 7 Math/PI) (* t 2 Math/PI 1.6))))
f3 (* 0.3 (Math/sin (+ (* x-norm 13 Math/PI) (* t 2 Math/PI 2.3))))
amplitude (Math/abs (+ f1 f2 f3))
bar-h (max 1 (* 30 amplitude))
bar-w (/ (double w) n-bars)
x (* i bar-w)
hue (mod (+ (* x-norm 200) (* t 120)) 360)]
{:node/type :shape/rect
:rect/xy [x (+ (/ h 2.0) 5)]
:rect/size [bar-w bar-h]
:rect/corner-radius 2
:node/opacity 0.15
:style/fill [:color/hsl hue 0.85 0.5]})))]
{:image/size [w h]
:image/background [:color/rgb 10 10 18]
:image/nodes (into bands reflection)})))
:fps 24})
Stained Glass Rose
Rose curve voronoi tessellation with jewel-toned fills.
View source
(defn ^{:example {:output "showcase-rose.png"
:title "Stained Glass Rose"
:desc "Rose curve voronoi tessellation with jewel-toned fills."}}
stained-glass-rose []
(let [;; Generate points along a rose curve + scattered background
rose-pts (for [a (range 0 360 4)]
(let [rad (* a (/ Math/PI 180))
r (* 150 (Math/cos (* 4 rad)))
x (+ 250 (* r (Math/cos rad)))
y (+ 250 (* r (Math/sin rad)))]
[x y]))
bg-pts (scatter/poisson-disk 10 10 490 490 40 42)
all-pts (vec (concat rose-pts bg-pts))
cells (voronoi/voronoi-cells all-pts 0 0 500 500)
n-rose (count (seq rose-pts))
jewels [[:color/rgb 180 30 50] [:color/rgb 30 60 160] [:color/rgb 160 40 140]
[:color/rgb 40 140 80] [:color/rgb 200 140 30] [:color/rgb 50 120 180]
[:color/rgb 180 80 30]]]
{:image/size [500 500]
:image/background [:color/rgb 20 15 10]
:image/nodes
(vec
(map-indexed
(fn [i cell]
(let [on-rose? (< i n-rose)
color (if on-rose?
(nth jewels (mod i (count jewels)))
[:color/rgb 25 20 15])
alpha (if on-rose? 0.85 0.3)]
(-> cell
(assoc :style/fill color)
(assoc :node/opacity alpha)
(assoc :style/stroke {:color [:color/rgb 15 10 5] :width 2}))))
cells))}))
Topographic Rings
Concentric elevation contours with warm-to-cool color mapping.
View source
(defn ^{:example {:output "showcase-topo-rings.png"
:title "Topographic Rings"
:desc "Concentric elevation contours with warm-to-cool color mapping."}}
topo-rings []
(let [cx 250 cy 250
rings (vec
(for [i (range 40)]
(let [r (+ 10 (* i 6))
wobble-pts (for [a (range 0 360 5)]
(let [rad (* a (/ Math/PI 180))
noise-val (noise/perlin2d
(* 0.03 r (Math/cos rad))
(* 0.03 r (Math/sin rad))
{:seed 42})
rr (+ r (* 8 noise-val))
x (+ cx (* rr (Math/cos rad)))
y (+ cy (* rr (Math/sin rad)))]
[x y]))
t (/ (double i) 40)
color (palette/gradient-map
[[0.0 [:color/rgb 30 60 120]]
[0.3 [:color/rgb 40 150 130]]
[0.6 [:color/rgb 180 170 80]]
[1.0 [:color/rgb 200 80 40]]]
t)]
{:node/type :shape/path
:path/commands (into [[:move-to (first wobble-pts)]]
(conj (mapv #(vector :line-to %) (rest wobble-pts))
[:close]))
:style/stroke {:color color :width (if (zero? (mod i 5)) 1.5 0.6)}})))]
{:image/size [500 500]
:image/background [:color/rgb 245 240 230]
:image/nodes rings}))
Zen Garden
Raked sand patterns with placed stones — stippled and hatched.
View source
(defn ^{:example {:output "showcase-zen-garden.png"
:title "Zen Garden"
:desc "Raked sand patterns with placed stones — stippled and hatched."}}
zen-garden []
(let [w 500 h 400
;; Raked sand — curved parallel lines
rake-lines
(vec
(for [i (range 30)]
(let [y-base (+ 20 (* i 12))
pts (for [x (range 0 (inc w) 8)]
(let [;; Bend around stone positions
d1 (Math/sqrt (+ (Math/pow (- x 150) 2) (Math/pow (- y-base 180) 2)))
d2 (Math/sqrt (+ (Math/pow (- x 350) 2) (Math/pow (- y-base 250) 2)))
bend1 (if (< d1 80) (* 12 (/ (- 80 d1) 80.0)) 0)
bend2 (if (< d2 60) (* 10 (/ (- 60 d2) 60.0)) 0)]
[x (+ y-base bend1 bend2)]))]
{:node/type :shape/path
:path/commands (into [[:move-to (first pts)]]
(mapv #(vector :line-to %) (rest pts)))
:style/stroke {:color [:color/rgb 190 180 160] :width 0.7}})))
;; Stones — stippled circles
stones
[{:node/type :shape/circle
:circle/center [150 180] :circle/radius 30
:style/fill {:fill/type :stipple
:stipple/density 0.5 :stipple/radius 0.8 :stipple/seed 42
:stipple/color [:color/rgb 80 75 65]
:stipple/background [:color/rgb 210 200 180]}
:style/stroke {:color [:color/rgb 100 90 75] :width 1}}
{:node/type :shape/circle
:circle/center [350 250] :circle/radius 22
:style/fill {:fill/type :stipple
:stipple/density 0.6 :stipple/radius 0.7 :stipple/seed 99
:stipple/color [:color/rgb 70 65 55]
:stipple/background [:color/rgb 210 200 180]}
:style/stroke {:color [:color/rgb 90 80 65] :width 1}}
{:node/type :shape/circle
:circle/center [360 230] :circle/radius 12
:style/fill {:fill/type :stipple
:stipple/density 0.4 :stipple/radius 0.6 :stipple/seed 77
:stipple/color [:color/rgb 85 80 70]
:stipple/background [:color/rgb 210 200 180]}
:style/stroke {:color [:color/rgb 100 90 75] :width 0.8}}]]
{:image/size [w h]
:image/background [:color/rgb 210 200 180]
:image/nodes (into rake-lines stones)}))