Gallery
Every image on this page was rendered from code.
Filter by feature:
Generative

Boid Trails
Flocking boids leaving fading afterimage trails.
boidsanimationopacity
View source
(defn boids-trails []
(let [w 600 h 450
config (assoc boids/classic :count 40 :bounds [0 0 w h] :seed 88 :max-speed 4.0)
all-frames (boids/simulate-flock config 100 {})
trail-len 8]
{:frames
(anim/frames (count all-frames)
(fn [t]
(let [i (min (int (* t (dec (count all-frames)))) (dec (count all-frames)))
trail-indices (range (max 0 (- i trail-len)) (inc i))]
{:image/size [w h]
:image/background [:color/rgb 15 15 25]
:image/nodes
(vec (mapcat
(fn [fi]
(let [age (- i fi)
opacity (/ 1.0 (inc (* age 1.5)))
flock (nth all-frames fi)]
(mapv (fn [{[px py] :pos}]
{:node/type :shape/circle
:circle/center [px py]
:circle/radius (- 4 (* age 0.3))
:node/opacity opacity
:style/fill [:color/hsl (mod (* fi 3) 360) 0.6 0.6]})
(:boids flock))))
trail-indices))})))
:fps 24}))
Bounded Fern
L-system fern that grows to fill its canvas, constrained by bounds.
lsystemconstraintbotanical
View source
(defn bounded-fern []
(let [w 600 h 600
cmds (lsystem/lsystem->path-cmds "F" lsystem/fern
{:iterations 5 :angle 22.5 :length 4.0
:origin [300 580] :heading -90
:bounds [0 0 w h] :seed 42})]
{:image/size [w h]
:image/background [:color/rgb 245 242 235]
:image/nodes [{:node/type :shape/path
:path/commands cmds
:style/stroke {:color [:color/rgb 40 75 35] :width 1.2}}]}))
CA Quilt
Cellular automata snapshots at different generations, stitched into a quilt.
cellular-automatacolor
View source
(defn ca-quilt []
(let [w 600 h 600
cell-w 10 grid-size 28
base (ca/ca-grid grid-size grid-size :random 42)
generations [10 25 40 55 70 85 100 120 150]
grids (reductions (fn [g _] (ca/ca-run g :life 1)) base (range 150))
selected (mapv #(nth (vec grids) (min % (dec (count grids)))) generations)
hues [0 40 80 160 200 240 280 320 350]]
{:image/size [w h]
:image/background [:color/rgb 30 28 35]
:image/nodes
(vec (mapcat
(fn [grid idx]
(let [col (mod idx 3) row (quot idx 3)
ox (+ 10 (* col 197)) oy (+ 10 (* row 197))
cs (/ 185.0 grid-size)
hue (nth hues idx)]
(mapv (fn [node]
(let [[nx ny] (:rect/xy node)]
(assoc node
:rect/xy [(+ ox nx) (+ oy ny)]
:rect/size [cs cs]
:style/fill [:color/hsl hue 0.5 0.45])))
(ca/ca->nodes grid cs {:style/fill [:color/rgb 0 0 0]}))))
selected (range)))}))
Circle Pack
Packed circles with weighted sunset palette — rare accents.
circle-packingpalettecolorrecipe-pack
View source
(defn circle-pack-palette []
(let [w 600 h 600
pal (:sunset palette/palettes)
weights [3 2 2 1 5]
circles (circle/circle-pack [20 20 560 560]
{:min-radius 4 :max-radius 45 :padding 2
:max-circles 300 :seed 42})
colors (palette/weighted-sample pal weights (count circles) 42)]
{:image/size [w h]
:image/background [:color/rgb 250 245 235]
:image/nodes
(mapv (fn [{[x y] :center r :radius} color]
{:node/type :shape/circle
:circle/center [x y]
:circle/radius r
:style/fill color
:style/stroke {:color [:color/rgba 40 30 20 0.3] :width 0.5}})
circles colors)}))
Coral Growth
Gray-Scott reaction-diffusion at the coral preset.
reaction-diffusionanimationcolorrecipe-ca
View source
(defn coral []
(let [gw 120 gh 120
cell-size 5
w (* gw cell-size) h (* gh cell-size)
init (ca/rd-grid gw gh :center 42)
params (:coral ca/rd-presets)
;; Pre-compute states at intervals
states (loop [g init i 0 acc []]
(if (>= i 600)
acc
(let [g' (ca/rd-run g params 10)]
(recur g' (+ i 10)
(if (zero? (mod i 10))
(conj acc g')
acc)))))]
{:frames
(anim/frames (count states)
(fn [t]
(let [idx (min (int (* t (dec (count states)))) (dec (count states)))
g (nth states idx)]
{:image/size [w h]
:image/background [:color/rgb 10 20 40]
:image/nodes
(ca/rd->nodes g cell-size
(fn [a b]
(let [v (min 1.0 (* b 4))]
[:color/rgb
(int (+ 10 (* 80 v)))
(int (+ 20 (* 120 v)))
(int (+ 40 (* 180 (- 1.0 (* a 0.3)))))])))})
))
:fps 15}))
Dashed Flow
Flow field streamlines with dashed + smoothed paths.
flow-fielddashingsmoothingpaletterecipe-flow
View source
(defn dashed-flow []
(let [w 600 h 600
paths (flow/flow-field [20 20 560 560]
{:density 25 :steps 40 :step-size 3 :seed 42})
pal (:ocean palette/palettes)]
{:image/size [w h]
:image/background [:color/rgb 245 245 240]
:image/nodes
(vec
(mapcat
(fn [path-node i]
(let [cmds (:path/commands path-node)
smoothed (aesthetic/smooth-commands cmds {:samples 40})
dashes (aesthetic/dash-commands smoothed {:dash [12.0 6.0]})
color (nth pal (mod i (count pal)))]
(mapv (fn [dash-cmds]
{:node/type :shape/path
:path/commands dash-cmds
:style/stroke {:color color :width 1.5}})
(or dashes []))))
paths (range)))}))
Dashed Sapling
L-system tree with dashed and jittered branch strokes.
l-systemdashingjitter
View source
(defn lsystem-dashed []
(let [w 500 h 600
cmds (lsystem/lsystem->path-cmds
"F" {"F" "FF+[+F-F-F]-[-F+F+F]"}
{:iterations 3 :angle 25 :length 10 :origin [250 580] :heading -90})
jittered (aesthetic/jittered-commands cmds {:amount 1.5 :seed 42})
dashes (aesthetic/dash-commands jittered {:dash [8.0 4.0]})]
{:image/size [w h]
:image/background [:color/rgb 250 248 240]
:image/nodes
(mapv (fn [d]
{:node/type :shape/path :path/commands d
:style/stroke {:color [:color/rgb 60 80 50] :width 1.2}})
(or dashes [cmds]))}))
Depth Gradient
Subdivision where deeper cells are darker, creating an organic shadow map.
subdivisiongradients
View source
(defn depth-gradient []
(let [w 600 h 600
rects (subdivide/subdivide [10 10 580 580]
{:depth 6 :min-size 15 :split-range [0.25 0.75]
:padding 2 :seed 42})
max-d (apply max (map :depth rects))]
{:image/size [w h]
:image/background [:color/rgb 240 238 230]
:image/nodes
(mapv (fn [{[x y rw rh] :rect d :depth}]
(let [t (/ (double d) max-d)
lightness (- 0.85 (* 0.7 t))]
{:node/type :shape/rect :rect/xy [x y] :rect/size [rw rh]
:style/fill [:color/hsl 210 0.3 lightness]}))
rects)}))
Dot Cloud
Gaussian-distributed dots forming a soft, nebula-like cloud.
scatternoiseopacitycolor
View source
(defn dot-cloud []
(let [w 600 h 600 n 2000
xs (prob/gaussian n 300.0 100.0 42)
ys (prob/gaussian n 300.0 100.0 99)
sizes (prob/gaussian n 3.0 1.5 77)]
{:image/size [w h]
:image/background [:color/rgb 10 8 20]
:image/nodes
(mapv (fn [x y s i]
(let [hue (+ 220 (* 60 (noise/perlin2d (* x 0.005) (* y 0.005))))]
{:node/type :shape/circle
:circle/center [x y]
:circle/radius (max 0.5 s)
:node/opacity (max 0.1 (min 0.7 (/ 3.0 (max 1 s))))
:style/fill [:color/hsl hue 0.6 0.6]}))
xs ys sizes (range))}))
Edition Gallery
25 editions of a parametric design showing weighted-choice diversity.
colormath
View source
(defn series-showcase []
(let [w 750 h 750 cell 140
spec {:bg-hue {:type :uniform :lo 0.0 :hi 360.0}
:shape {:type :weighted-choice
:options [:circles :lines :dots]
:weights [4 3 2]}
:density {:type :gaussian :mean 8.0 :sd 2.0}
:accent {:type :boolean :probability 0.2}}
editions (series/series-range spec 999 0 25)]
{:image/size [w h]
:image/background [:color/rgb 25 25 30]
:image/nodes
(vec (mapcat
(fn [{:keys [bg-hue shape density accent]} idx]
(let [col (mod idx 5) row (quot idx 5)
ox (+ 10 (* col (+ cell 6)))
oy (+ 10 (* row (+ cell 6)))
n (max 3 (int density))
bg-color [:color/hsl bg-hue 0.15 0.15]
fg-color [:color/hsl bg-hue 0.6 0.55]
accent-color (if accent [:color/hsl (mod (+ bg-hue 180) 360) 0.8 0.6] fg-color)]
(into [{:node/type :shape/rect :rect/xy [ox oy] :rect/size [cell cell]
:style/fill bg-color}]
(case shape
:circles
(for [i (range n) j (range n)]
{:node/type :shape/circle
:circle/center [(+ ox (* (/ cell n) (+ i 0.5)))
(+ oy (* (/ cell n) (+ j 0.5)))]
:circle/radius (/ cell n 3.0)
:style/fill (if (and accent (zero? (mod (+ i j) 5)))
accent-color fg-color)})
:lines
(for [i (range n)]
{:node/type :shape/line
:line/from [(+ ox 10) (+ oy (* (/ cell n) (+ i 0.5)))]
:line/to [(+ ox cell -10) (+ oy (* (/ cell n) (+ i 0.5)))]
:style/stroke {:color fg-color :width 2}})
:dots
(for [i (range (* n 2))]
(let [x (+ ox 10 (* (mod (* i 37) (- cell 20)) 1.0))
y (+ oy 10 (* (mod (* i 59) (- cell 20)) 1.0))]
{:node/type :shape/circle
:circle/center [x y]
:circle/radius 2.5
:style/fill fg-color}))))))
editions (range)))}))
Elevation Map
Dense contour lines with altitude-banded earth tones.
contournoisepalette
View source
(defn contour-elevation []
(let [w 600 h 450
thresholds (mapv #(- (* % 0.1) 0.5) (range 12))
earth-tones [[:color/rgb 40 80 60] [:color/rgb 60 100 70]
[:color/rgb 100 140 80] [:color/rgb 160 180 100]
[:color/rgb 200 190 120] [:color/rgb 180 150 100]
[:color/rgb 160 120 80] [:color/rgb 140 100 70]
[:color/rgb 120 80 50] [:color/rgb 100 60 40]
[:color/rgb 80 45 30] [:color/rgb 60 30 20]]]
{:image/size [w h]
:image/background [:color/rgb 230 225 210]
:image/nodes
[{:node/type :contour
:contour/bounds [0 0 w h]
:contour/opts {:thresholds thresholds
:resolution 3
:noise-scale 0.008
:seed 42}
:style/stroke {:color [:color/rgb 80 60 40] :width 0.8}}]}))
Hatched Grid
Subdivision cells alternating between solid fills and cross-hatching.
subdivisionhatchingpalette
View source
(defn hatch-subdiv []
(let [w 600 h 600
rects (subdivide/subdivide [15 15 570 570]
{:depth 3 :min-size 50 :padding 5 :seed 33})
pal (:earth palette/palettes)]
{:image/size [w h]
:image/background [:color/rgb 245 240 230]
:image/nodes
(mapv (fn [{[x y rw rh] :rect :as cell} i]
(let [use-hatch (prob/coin 0.45 (+ i 42))
color (prob/pick pal (+ i 99))]
(if use-hatch
{:node/type :shape/rect :rect/xy [x y] :rect/size [rw rh]
:style/fill {:fill/type :hatch
:hatch/angle (prob/pick [30 45 60 -30 -45] (+ i 7))
:hatch/spacing (+ 4 (mod i 3))
:hatch/stroke-width 0.8
:hatch/color color
:hatch/background [:color/rgb 245 240 230]}
:style/stroke {:color [:color/rgb 60 50 40] :width 1}}
{:node/type :shape/rect :rect/xy [x y] :rect/size [rw rh]
:style/fill color
:style/stroke {:color [:color/rgb 60 50 40] :width 1}})))
rects (range))}))
Leopard Spots
Reaction-diffusion spots preset with warm animal coloring.
reaction-diffusioncolor
View source
(defn rd-spots []
(let [gw 200 gh 200 cs 3
g (ca/rd-run (ca/rd-grid gw gh :center 77)
(:spots ca/rd-presets) 2000)]
{:image/size [(* gw cs) (* gh cs)]
:image/background [:color/rgb 210 180 130]
:image/nodes
(ca/rd->nodes g cs
(fn [_a b]
(let [v (min 1.0 (* b 3))]
[:color/rgb
(int (- 210 (* 160 v)))
(int (- 180 (* 140 v)))
(int (- 130 (* 90 v)))])))}))
Lightning
Jagged branching lightning — bounded L-system on dark sky.
lsystemconstraint
View source
(defn bounded-lightning []
(let [w 600 h 600
cmds (lsystem/lsystem->path-cmds "F" lsystem/lightning
{:iterations 5 :angle 18 :length 5.0
:origin [300 20] :heading 90
:bounds [0 0 w h] :seed 7})]
{:image/size [w h]
:image/background [:color/rgb 15 10 30]
:image/nodes [{:node/type :shape/path
:path/commands cmds
:style/stroke {:color [:color/rgb 220 200 255] :width 1}}]}))
Mitosis
Reaction-diffusion cell division with the mitosis preset.
reaction-diffusionanimation
View source
(defn mitosis []
(let [gw 150 gh 150 cs 3
init (ca/rd-grid gw gh :center 42)
params (:mitosis ca/rd-presets)
states (loop [g init i 0 acc []]
(if (>= i 800)
acc
(let [g' (ca/rd-run g params 15)]
(recur g' (+ i 15)
(if (zero? (mod i 15)) (conj acc g') acc)))))]
{:frames
(anim/frames (count states)
(fn [t]
(let [idx (min (int (* t (dec (count states)))) (dec (count states)))
g (nth states idx)]
{:image/size [(* gw cs) (* gh cs)]
:image/background [:color/rgb 240 235 225]
:image/nodes
(ca/rd->nodes g cs
(fn [a b]
(let [v (min 1.0 (* b 5))]
[:color/rgb
(int (- 240 (* 200 v)))
(int (- 235 (* 160 v)))
(int (- 225 (* 100 v)))])))})))
:fps 15}))
Mondrian Grid
Recursive subdivision with primary color accents.
subdivisioncolorrecipe-subdivide
View source
(defn mondrian []
(let [w 600 h 600
rects (subdivide/subdivide [10 10 580 580]
{:depth 4 :min-size 40 :padding 6 :seed 77})
colors [[:color/rgb 245 245 240]
[:color/rgb 245 245 240]
[:color/rgb 245 245 240]
[:color/rgb 220 30 30]
[:color/rgb 30 60 180]
[:color/rgb 245 220 40]]]
{:image/size [w h]
:image/background [:color/rgb 20 20 20]
:image/nodes
(mapv (fn [{[x y rw rh] :rect :as cell}]
(let [color (prob/pick colors (+ (hash cell) 42))]
{:node/type :shape/rect
:rect/xy [x y]
:rect/size [rw rh]
:style/fill color
:style/stroke {:color [:color/rgb 20 20 20] :width 4}}))
rects)}))
Morphing Waves
Layered wave paths that smoothly morph between shapes.
smoothinganimationcolor
View source
(defn morph-wave []
(let [w 600 h 400
make-wave (fn [seed amp freq phase]
(let [pts (for [x (range 0 (inc w) 4)]
[x (+ 200 (* amp (Math/sin (+ (* x freq) phase))))])]
(into [[:move-to (first pts)]]
(mapv (fn [p] [:line-to p]) (rest pts)))))]
{:frames
(anim/frames 80
(fn [t]
(let [layers
(vec (for [i (range 6)]
(let [phase (* t Math/PI 4)
amp (+ 30 (* 25 (Math/sin (+ (* i 0.8) phase))))
freq (+ 0.01 (* i 0.003))
y-off (* i 30)
wave (make-wave i amp freq (+ phase (* i 1.2)))
smoothed (aesthetic/smooth-commands wave {:samples 50})
hue (mod (+ (* i 50) (* t 120)) 360)]
{:node/type :shape/path
:path/commands smoothed
:node/transform [[:transform/translate 0 (- y-off 60)]]
:node/opacity 0.7
:style/stroke {:color [:color/hsl hue 0.6 0.5] :width 2.5}})))]
{:image/size [w h]
:image/background [:color/rgb 15 12 25]
:image/nodes layers})))
:fps 24}))
Murmuration
Starling-like flocking with tight cohesion and wide alignment.
boidsanimation
View source
(defn murmuration []
(let [w 800 h 600
config (assoc boids/murmuration
:bounds [0 0 w h]
:count 150
:seed 42)
frames (boids/simulate-flock config 120 {})]
{:frames
(anim/frames (count frames)
(fn [t]
(let [idx (min (int (* t (dec (count frames)))) (dec (count frames)))
flock (nth frames idx)]
{:image/size [w h]
:image/background [:color/rgb 200 210 230]
:image/nodes
(boids/flock->nodes flock
{:shape :triangle :size 6
:style {:style/fill [:color/rgb 30 30 40]}})})))
:fps 30}))
Packed Type
The letter A filled with packed circles using a weighted palette.
circle-packingtypographypalette
View source
(defn text-pack []
(let [w 500 h 500
text-cmds (eido.text/text->path-commands "A"
{:font/family "SansSerif" :font/size 400
:font/weight :bold})
;; Font coords have Y pointing up; flip and center in canvas
transform-pt (fn [[x y]] [(+ 70 (* 1.2 x)) (+ 420 (* 1.2 y))])
scaled (mapv (fn [[cmd & args]]
(case cmd
:move-to [:move-to (transform-pt (first args))]
:line-to [:line-to (transform-pt (first args))]
:curve-to [:curve-to
(transform-pt (first args))
(transform-pt (second args))
(transform-pt (nth args 2))]
:quad-to [:quad-to
(transform-pt (first args))
(transform-pt (second args))]
:close [:close]))
text-cmds)
circles (circle/circle-pack-in-path scaled
{:min-radius 2 :max-radius 18 :padding 1.5
:max-circles 400 :seed 42})
pal (:neon palette/palettes)]
{:image/size [w h]
:image/background [:color/rgb 15 15 20]
:image/nodes
(mapv (fn [{[x y] :center r :radius} i]
{:node/type :shape/circle
:circle/center [x y] :circle/radius r
:style/fill (nth pal (mod i (count pal)))
:node/opacity 0.85})
circles (range))}))
Painted Flow
Flow field streamlines colored by noise, smoothed for a painted feel.
flow-fieldnoisesmoothingcolorrecipe-flow
View source
(defn noise-flow []
(let [w 700 h 500
paths (flow/flow-field [20 20 660 460]
{:density 18 :steps 50 :step-size 3 :seed 77})]
{:image/size [w h]
:image/background [:color/rgb 20 18 25]
:image/nodes
(mapv (fn [path-node i]
(let [cmds (:path/commands path-node)
smoothed (aesthetic/smooth-commands cmds {:samples 40})
[sx sy] (second (first cmds))
hue (* 360 (noise/perlin2d (* sx 0.005) (* sy 0.005) {:seed 10}))]
{:node/type :shape/path
:path/commands smoothed
:node/opacity 0.7
:style/stroke {:color [:color/hsl (mod (+ 200 (* 80 hue)) 360) 0.7 0.55]
:width (+ 1 (* 2 (Math/abs (noise/perlin2d (* i 0.3) 0 {:seed 50}))))}}))
paths (range))}))
Pinned Coloring
Artist pins center cell to gold — solver colors the rest.
voronoicoloringconstraintcolorpin
View source
(defn pinned-coloring []
(let [w 600 h 600
pts (scatter/poisson-disk [25 25 550 550] {:min-dist 50 :seed 88})
cells (voronoi/voronoi-cells pts [0 0 w h])
adj (coloring/cells-adjacency cells)
;; Find the cell closest to center
center-idx (first (apply min-key
(fn [[i [x y]]]
(+ (* (- x 300) (- x 300)) (* (- y 300) (- y 300))))
(map-indexed vector pts)))
gold [:color/rgb 230 190 50]
palette [[:color/rgb 30 50 90] [:color/rgb 50 90 140]
[:color/rgb 80 140 170] gold]
colored (coloring/color-regions cells adj palette
{:seed 42 :pin {center-idx gold}})]
{:image/size [w h]
:image/background [:color/rgb 15 15 25]
:image/nodes
(mapv #(assoc % :style/stroke {:color [:color/rgb 15 15 25] :width 2})
colored)}))
Predator and Prey
Boids seeking a point while fleeing another — dynamic tension.
boidsanimation
View source
(defn boids-seek []
(let [w 600 h 450
config {:count 60 :bounds [0 0 w h] :max-speed 3.5 :max-force 0.18
:separation {:radius 20 :strength 1.5}
:alignment {:radius 40 :strength 0.8}
:cohesion {:radius 40 :strength 0.6}
:bounds-margin 30 :seed 42}
frames (vec (take 120
(iterate
(fn [flock]
(let [t (* (:tick flock) 0.05)
seek-pt [(+ 300 (* 150 (Math/cos t)))
(+ 225 (* 100 (Math/sin (* t 1.3))))]
flee-pt [(+ 300 (* 120 (Math/cos (+ t 3.14))))
(+ 225 (* 80 (Math/sin (+ (* t 0.7) 1.5))))]]
(boids/step-flock flock
(assoc config
:seek {:target seek-pt :strength 0.8}
:flee {:target flee-pt :radius 80 :strength 1.5}))))
(boids/init-flock config))))]
{:frames
(anim/frames (count frames)
(fn [t]
(let [i (min (int (* t (dec (count frames)))) (dec (count frames)))
flock (nth frames i)
tick (* (:tick flock) 0.05)
seek-pt [(+ 300 (* 150 (Math/cos tick)))
(+ 225 (* 100 (Math/sin (* tick 1.3))))]
flee-pt [(+ 300 (* 120 (Math/cos (+ tick 3.14))))
(+ 225 (* 80 (Math/sin (+ (* tick 0.7) 1.5))))]]
{:image/size [w h]
:image/background [:color/rgb 240 238 230]
:image/nodes
(into
[{:node/type :shape/circle :circle/center seek-pt :circle/radius 8
:style/fill [:color/rgb 50 150 50] :node/opacity 0.5}
{:node/type :shape/circle :circle/center flee-pt :circle/radius 8
:style/fill [:color/rgb 200 50 50] :node/opacity 0.5}]
(boids/flock->nodes flock
{:shape :triangle :size 7
:style {:style/fill [:color/rgb 40 50 70]}}))})))
:fps 24}))
Ripple
Reaction-diffusion ripple preset with cool blue coloring.
reaction-diffusioncolor
View source
(defn rd-ripple []
(let [gw 200 gh 150 cs 3
g (ca/rd-run (ca/rd-grid gw gh :center 42)
(:ripple ca/rd-presets) 1200)]
{:image/size [(* gw cs) (* gh cs)]
:image/background [:color/rgb 5 10 30]
:image/nodes
(ca/rd->nodes g cs
(fn [_a b]
(let [v (min 1.0 (* b 3))]
[:color/rgb
(int (* 40 v))
(int (+ 10 (* 150 v)))
(int (+ 30 (* 220 v)))])))}))
Series Preview
9 editions of a parametric design driven by eido.gen.series.
noisecolormathrecipe-edition
View source
(defn series-grid []
(let [w 600 h 600
cell 190
spec {:hue {:type :uniform :lo 0.0 :hi 360.0}
:density {:type :gaussian :mean 12.0 :sd 3.0}
:radius {:type :uniform :lo 3.0 :hi 15.0}
:style-key {:type :choice :options [:filled :stroked :both]}}
editions (series/series-range spec 12345 0 9)]
{:image/size [w h]
:image/background [:color/rgb 30 30 35]
:image/nodes
(vec
(mapcat
(fn [params idx]
(let [col (mod idx 3)
row (quot idx 3)
ox (+ 10 (* col (+ cell 5)))
oy (+ 10 (* row (+ cell 5)))
{:keys [hue density radius style-key]} params
n (int (max 4 density))
pts (for [i (range n)
j (range n)]
[(+ ox (* (/ cell n) (+ i 0.5)))
(+ oy (* (/ cell n) (+ j 0.5)))])
color [:color/hsl hue 0.7 0.55]]
(mapv (fn [[x y]]
(let [r (+ radius (* 2 (noise/perlin2d (* x 0.03) (* y 0.03))))]
(merge
{:node/type :shape/circle
:circle/center [x y]
:circle/radius (max 1 r)}
(case style-key
:filled {:style/fill color}
:stroked {:style/stroke {:color color :width 1.5}}
:both {:style/fill color
:style/stroke {:color [:color/rgb 255 255 255] :width 0.5}}))))
pts)))
editions (range)))}))
Stippled Sphere
A sphere effect using density-varying stipple dots.
stipplescatter
View source
(defn stipple-gradient []
(let [w 500 h 500 cx 250 cy 250 r 200]
{:image/size [w h]
:image/background [:color/rgb 245 242 235]
:image/nodes
(let [pts (scatter/poisson-disk [30 30 440 440] {:min-dist 6 :seed 42})]
(vec (keep
(fn [[x y]]
(let [dx (- x cx) dy (- y cy)
dist (Math/sqrt (+ (* dx dx) (* dy dy)))]
(when (< dist r)
(let [norm (/ dist r)
size (* 3.5 (- 1.0 (* norm norm)))]
(when (> size 0.3)
{:node/type :shape/circle
:circle/center [x y]
:circle/radius size
:style/fill [:color/rgb 30 30 40]})))))
pts)))}))
Subdivided Packing
Each subdivision cell filled with a different circle pack and palette.
subdivisioncircle-packingpaletterecipe-subdivide-pack
View source
(defn subdiv-pack []
(let [w 600 h 600
rects (subdivide/subdivide [10 10 580 580]
{:depth 3 :min-size 80 :padding 6 :seed 55})
pals [(:ocean palette/palettes) (:sunset palette/palettes)
(:fire palette/palettes) (:forest palette/palettes)]]
{:image/size [w h]
:image/background [:color/rgb 25 25 30]
:image/nodes
(vec (mapcat
(fn [{[x y rw rh] :rect} i]
(let [pal (nth pals (mod i (count pals)))
circles (circle/circle-pack [x y rw rh]
{:min-radius 2 :max-radius (/ (min rw rh) 4)
:padding 1.5 :max-circles 60 :seed (+ i 100)})
colors (palette/weighted-sample pal [1 2 3 2 1]
(count circles) (+ i 200))]
(mapv (fn [{[cx cy] :center r :radius} c]
{:node/type :shape/circle
:circle/center [cx cy] :circle/radius r
:style/fill c})
circles colors)))
rects (range)))}))
Terrain Stripes
Horizontal lines displaced by noise, creating a topographic ribbon effect.
noisesmoothingcolor
View source
(defn terrain-stripes []
(let [w 700 h 500 n-lines 40 spacing (/ (double h) (inc n-lines))]
{:image/size [w h]
:image/background [:color/rgb 250 248 240]
:image/nodes
(vec (for [i (range n-lines)]
(let [base-y (* (inc i) spacing)
pts (for [x (range 0 (inc w) 3)]
[x (+ base-y (* 25 (noise/perlin2d (* x 0.008) (* i 0.3) {:seed 42})))])
cmds (into [[:move-to (first pts)]]
(mapv (fn [p] [:line-to p]) (rest pts)))
smoothed (aesthetic/smooth-commands cmds {:samples 60})
t (/ (double i) n-lines)]
{:node/type :shape/path
:path/commands smoothed
:style/stroke {:color [:color/hsl (+ 200 (* 40 t)) 0.4 (+ 0.3 (* 0.3 t))]
:width 1.5}})))}))
Trembling Grid
A perfect grid where each element trembles with gaussian-distributed displacement.
jittercolormath
View source
(defn jitter-grid []
(let [w 600 h 600 cols 15 rows 15
dx (/ (double w) (inc cols))
dy (/ (double h) (inc rows))
jitter-x (prob/gaussian (* cols rows) 0.0 8.0 42)
jitter-y (prob/gaussian (* cols rows) 0.0 8.0 99)]
{:image/size [w h]
:image/background [:color/rgb 245 242 235]
:image/nodes
(vec (for [row (range rows) col (range cols)]
(let [i (+ (* row cols) col)
cx (+ (* (inc col) dx) (nth jitter-x i))
cy (+ (* (inc row) dy) (nth jitter-y i))
hue (* (/ (+ cx cy) (+ w h)) 360)]
{:node/type :shape/circle
:circle/center [cx cy]
:circle/radius 12
:style/fill [:color/hsl hue 0.5 0.6]
:style/stroke {:color [:color/rgba 0 0 0 0.15] :width 0.5}})))}))
Voronoi Coloring
Constraint-solved coloring — no adjacent cells share a color.
voronoicoloringconstraintcolor
View source
(defn voronoi-coloring []
(let [w 600 h 600
pts (scatter/poisson-disk [30 30 540 540] {:min-dist 40 :seed 42})
cells (voronoi/voronoi-cells pts [0 0 w h])
adj (coloring/cells-adjacency cells)
palette [[:color/rgb 230 80 60] [:color/rgb 240 170 50]
[:color/rgb 60 160 80] [:color/rgb 40 100 180]]
colored (coloring/color-regions cells adj palette {:seed 17})]
{:image/size [w h]
:image/background [:color/rgb 25 25 30]
:image/nodes
(mapv #(assoc % :style/stroke {:color [:color/rgb 25 25 30] :width 2.5})
colored)}))
Voronoi Glass
Voronoi cells with jewel-toned weighted palette and dark leading.
voronoiscatterpalettecolor
View source
(defn voronoi-glass []
(let [w 600 h 600
pts (scatter/poisson-disk [20 20 560 560] {:min-dist 45 :seed 42})
cells (voronoi/voronoi-cells pts [0 0 w h])
jewels [[:color/rgb 180 30 50] [:color/rgb 40 100 180]
[:color/rgb 220 170 30] [:color/rgb 60 160 80]
[:color/rgb 140 50 160] [:color/rgb 220 120 50]]
weights [2 3 1 2 1 1]]
{:image/size [w h]
:image/background [:color/rgb 20 20 25]
:image/nodes
(vec (map-indexed
(fn [i cell]
(assoc cell
:style/fill (prob/pick-weighted jewels weights (+ i 42))
:style/stroke {:color [:color/rgb 20 20 25] :width 3}))
cells))}))Showcase

Aurora Borealis
Shimmering curtains of light driven by layered 3D noise.
noiseanimationopacitycolor
View source
(defn 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 1.0
: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)})))
:animation/fps 20})
Chaikin Spirograph
Sharp polygonal spirograph smoothed by Chaikin corner-cutting.
chaikinsmoothinggeometry
View source
(defn chaikin-spiral []
(let [w 500 h 500
n 200
pts (mapv (fn [i]
(let [t (/ (double i) n)
r1 180 r2 75 k 7
a (* t 2 Math/PI k)
x (+ 250 (* (+ r1 (* r2 (Math/cos a)))
(Math/cos (* t 2 Math/PI)) 0.6))
y (+ 250 (* (+ r1 (* r2 (Math/cos a)))
(Math/sin (* t 2 Math/PI)) 0.6))]
[x y]))
(range n))
raw-cmds (scene/points->path pts false)
smooth (aesthetic/chaikin-commands raw-cmds {:iterations 4})]
{:image/size [w h]
:image/background [:color/rgb 15 12 25]
:image/nodes
[{:node/type :shape/path
:path/commands smooth
:style/stroke {:color [:color/rgba 100 200 255 0.6] :width 0.8}
}]}))
Clipped Flow
Flow field paths trimmed to a circular boundary.
clippingflow-fieldbounds
View source
(defn clipped-flow []
(let [w 500 h 500
cx 250.0 cy 250.0 r 180.0
lines (flow/flow-field [0 0 w h]
{:density 10 :steps 60 :step-length 2
:noise-scale 0.007 :seed 55})]
{:image/size [w h]
:image/background [:color/rgb 250 248 240]
:image/nodes
(into
[{:node/type :shape/circle
:circle/center [cx cy]
:circle/radius r
:style/stroke {:color [:color/rgba 60 60 60 0.3] :width 1}}]
(mapcat
(fn [node]
(let [cmds (:path/commands node)
trimmed (path/trim-to-bounds cmds
[(- cx r) (- cy r) (* 2 r) (* 2 r)])]
(mapv (fn [seg]
{:node/type :shape/path
:path/commands seg
:style/stroke {:color [:color/rgba 40 80 140 0.5]
:width 1}})
trimmed)))
lines))}))
Contrast Grid
Color pairs showing WCAG contrast ratios.
contrastaccessibilitycolor
View source
(defn contrast-grid []
(let [w 600 h 280
colors [:navy :red :forestgreen :purple :darkorange :teal]
bg-colors [:white :lightyellow :lavender]
cell-w 95 cell-h 55]
{:image/size [w h]
:image/background :whitesmoke
:image/nodes
(vec (mapcat
(fn [bi bg]
(mapcat
(fn [ci fg]
(let [x (+ 15 (* ci cell-w))
y (+ 15 (* bi (+ cell-h 20)))]
[{:node/type :shape/rect
:rect/xy [x y]
:rect/size [cell-w cell-h]
:style/fill bg
:style/stroke {:color :gray :width 0.5}}
{:node/type :shape/rect
:rect/xy [(+ x 10) (+ y 10)]
:rect/size [30 30]
:style/fill fg}]))
(range) colors))
(range) bg-colors))}))
Crystal Geode
3D spherical mesh with faceted crystal surface.
3dmeshdeformation
View source
(defn crystal-geode []
(let [proj (s3d/perspective {:scale 120 :origin [250 280]
:yaw 0.6 :pitch -0.35 :distance 5})
mesh (s3d/sphere-mesh 1.2 {:segments 20 :rings 14})
light {:light/direction [-1 -1 -1] :light/ambient 0.3 :light/intensity 0.8}
style {:style/fill [:color/rgb 160 120 200]
:style/stroke {:color [:color/rgb 100 70 150] :width 0.3}}
group (s3d/render-mesh proj mesh {:style style :light light})]
{:image/size [500 500]
:image/background [:color/rgb 15 10 25]
:image/nodes [group]}))
Disc Scatter
Points uniformly inside a disc — colored by distance.
geometricdistributioncircle
View source
(defn disc-scatter []
(let [w 500 h 500
pts (prob/scatter-in-circle 800 200.0 [250.0 250.0] 42)]
{:image/size [w h]
:image/background [:color/rgb 10 10 15]
:image/nodes
(mapv (fn [[x y]]
(let [dx (- x 250) dy (- y 250)
dist (Math/sqrt (+ (* dx dx) (* dy dy)))
t (/ dist 200.0)
c (color/lerp-oklab :deepskyblue :hotpink t)]
{:node/type :shape/circle
:circle/center [x y]
:circle/radius (+ 1.0 (* 2.0 (- 1.0 t)))
:style/fill c}))
pts)}))
Eased Gradients
Non-linear gradient interpolation with different easing.
gradienteasingcolor
View source
(defn eased-gradients []
(let [w 600 h 400
stops [[0.0 :navy] [0.5 :crimson] [1.0 :gold]]
easings [nil
(fn [t] (* t t))
(fn [t] (* t t t))
(fn [t] (Math/sqrt t))]
strip-h (/ (double h) (count easings))
cols 200]
{:image/size [w h]
:image/background :white
:image/nodes
(vec (mapcat
(fn [ei easing]
(let [y0 (* ei strip-h)
col-w (/ (double w) cols)]
(mapv (fn [ci]
(let [t (/ (double ci) cols)
c (palette/gradient-map stops t
(when easing {:easing easing}))]
{:node/type :shape/rect
:rect/xy [(* ci col-w) y0]
:rect/size [(+ col-w 1) strip-h]
:style/fill c}))
(range cols))))
(range) easings))}))
Ink Botanical
Organic leaf shapes with ink media preset.
inkpresetbotanical
View source
(defn ink-botanical []
(let [w 500 h 500
leaf (fn [cx cy size angle seed]
(let [pts (mapv
(fn [i]
(let [t (/ (double i) 20)
a (* t 2 Math/PI)
r (* size (+ 0.5 (* 0.5
(Math/cos (* 2 a)))))]
[(+ cx (* r (Math/cos (+ a angle))))
(+ cy (* r (Math/sin (+ a angle))))]))
(range 20))
cmds (conj (scene/points->path pts true) [:close])
styled (aesthetic/stylize cmds
(aesthetic/ink-preset seed))]
{:node/type :shape/path
:path/commands styled
:style/stroke {:color [:color/rgba 20 60 20 0.8]
:width 1.2}
:style/fill [:color/rgba 60 120 40 0.15]}))
leaves (for [i (range 8)]
(let [a (* i (/ (* 2 Math/PI) 8))
r (+ 80 (* 30 (Math/sin (* i 2.1))))]
(leaf (+ 250 (* r (Math/cos a)))
(+ 250 (* r (Math/sin a)))
(+ 40 (* 20 (Math/sin (* i 1.7))))
a (+ 42 i))))]
{:image/size [w h]
:image/background [:color/rgb 250 248 240]
:image/nodes (vec leaves)}))
Inset Frames
Nested polygon insets creating concentric frames.
insetpolygonnesting
View source
(defn inset-frames []
(let [w 500 h 500
outer [[50 50] [450 50] [450 450] [50 450]]
layers 12
pal (palette/sort-by-lightness (:earth palette/palettes))]
{:image/size [w h]
:image/background :ivory
:image/nodes
(vec (keep
(fn [i]
(let [poly (path/inset outer (* i 15))]
(when (>= (count poly) 3)
{:node/type :shape/path
:path/commands (conj (scene/points->path poly true) [:close])
:style/fill (nth pal (mod i (count pal)))
:style/stroke {:color [:color/rgba 40 30 20 0.6]
:width 1}})))
(range layers)))}))
Margin Composition
Dense flow field cropped with scene margin control.
margincompositionflow-field
View source
(defn margin-composition []
(let [w 500 h 500
margin 40
lines (flow/flow-field [0 0 w h]
{:density 8 :steps 100 :step-length 2
:noise-scale 0.008 :seed 33})
pal (:ocean palette/palettes)]
(scene/with-margin
{:image/size [w h]
:image/background :white
:image/nodes
(vec (map-indexed
(fn [i node]
(-> node
(assoc :style/stroke {:color (nth pal (mod i (count pal)))
:width 1.2})
(dissoc :style/fill)))
lines))}
margin)))
Neon Orbit
Glowing particles orbiting a central mass.
animationglowparticles
View source
(defn neon-orbit []
{:frames
(anim/frames 60
(fn [t]
(let [w 400 h 400
cx 200 cy 200
particles
(vec
(for [i (range 30)]
(let [phase (* i 0.2094)
radius (+ 60 (* i 4.5))
speed (+ 0.5 (/ 1.0 (+ 1 i)))
angle (+ phase (* t 2.0 Math/PI speed))
x (+ cx (* radius (Math/cos angle)))
y (+ cy (* radius (Math/sin angle)))
hue (mod (+ (* i 12) (* t 360)) 360)
r (+ 2 (/ 10.0 (+ 1 (* i 0.5))))]
{:node/type :shape/circle
:circle/center [x y]
:circle/radius r
:style/fill [:color/hsl hue 1.0 0.6]
:effect/glow {:blur 8
:color [:color/hsl hue 1.0 0.5]
:opacity 0.5}})))]
{:image/size [w h]
:image/background [:color/rgb 5 5 15]
:image/nodes particles})))
:animation/fps 20})
OKLAB Sunset
Perceptually uniform gradient bands using OKLAB interpolation.
oklabgradientcolor
View source
(defn oklab-sunset []
(let [bands 50
w 600 h 400
band-h (/ (double h) bands)]
{:image/size [w h]
:image/background :midnightblue
:image/nodes
(mapv (fn [i]
(let [t (/ (double i) bands)
c (if (< t 0.5)
(color/lerp-oklab :midnightblue :orangered (* t 2.0))
(color/lerp-oklab :orangered :gold (* (- t 0.5) 2.0)))]
{:node/type :shape/rect
:rect/xy [0 (* i band-h)]
:rect/size [w (+ band-h 1)]
:style/fill c}))
(range bands))}))
OKLCH Hue Ring
Full hue rotation in perceptually uniform OKLCH space.
oklchcolor-spacehue
View source
(defn oklch-ring []
(let [w 500 h 500
n 72
cx 250 cy 250
outer-r 200 inner-r 130]
{:image/size [w h]
:image/background [:color/rgb 30 30 30]
:image/nodes
(mapv (fn [i]
(let [hue (* (/ (double i) n) 360.0)
a1 (* (/ (double i) n) 2 Math/PI)
a2 (* (/ (double (inc i)) n) 2 Math/PI)
pts [[(+ cx (* outer-r (Math/cos a1)))
(+ cy (* outer-r (Math/sin a1)))]
[(+ cx (* outer-r (Math/cos a2)))
(+ cy (* outer-r (Math/sin a2)))]
[(+ cx (* inner-r (Math/cos a2)))
(+ cy (* inner-r (Math/sin a2)))]
[(+ cx (* inner-r (Math/cos a1)))
(+ cy (* inner-r (Math/sin a1)))]]]
{:node/type :shape/path
:path/commands (conj (scene/points->path pts true) [:close])
:style/fill (color/oklch 0.7 0.15 hue)}))
(range n))}))
Palette Wheel
Warm, cool, vivid, and muted variations of a single palette.
palettemanipulationoklab
View source
(defn palette-wheel []
(let [base (:sunset palette/palettes)
variations [["Original" base]
["Warmer" (palette/warmer base 20)]
["Cooler" (palette/cooler base 20)]
["Muted" (palette/muted base 0.4)]
["Vivid" (palette/vivid base 0.3)]
["Darker" (palette/darker base 0.3)]
["Lighter" (palette/lighter base 0.3)]]
bar-h 40
w 500 h (* bar-h (count variations))
n (count base)]
{:image/size [w h]
:image/background :white
:image/nodes
(vec (mapcat
(fn [row-i [_label pal]]
(let [bar-w (/ (double w) n)]
(mapv (fn [ci]
{:node/type :shape/rect
:rect/xy [(* ci bar-w) (* row-i bar-h)]
:rect/size [bar-w bar-h]
:style/fill (nth pal ci)})
(range n))))
(range) variations))}))
Pareto Cityscape
Building heights follow a Pareto distribution.
paretodistributionarchitecture
View source
(defn pareto-city []
(let [w 600 h 400
n 40
heights (prob/pareto n 1.5 20.0 42)
bar-w (/ (double w) n)
max-h 350.0]
{:image/size [w h]
:image/background [:color/rgb 15 20 40]
:image/nodes
(mapv (fn [i]
(let [raw-h (min max-h (double (nth heights i)))
x (* i bar-w)
y (- h raw-h)
t (/ raw-h max-h)
c (color/lerp-oklab [:color/rgb 40 60 100]
[:color/rgb 255 200 80] t)]
{:node/type :shape/rect
:rect/xy [x y]
:rect/size [(- bar-w 2) raw-h]
:style/fill c}))
(range n))}))
Pencil Sketch
Geometric forms rendered with pencil media preset.
pencilpresetaesthetic
View source
(defn pencil-sketch []
(let [w 500 h 500
shapes (for [i (range 5)]
(let [cx (+ 100 (* i 75))
cy (+ 250 (* 40 (Math/sin (* i 0.8))))
r (+ 30 (* 15 (Math/sin (* i 1.3))))
pts (mapv (fn [j]
(let [a (* j (/ (* 2 Math/PI) 6))]
[(+ cx (* r (Math/cos a)))
(+ cy (* r (Math/sin a)))]))
(range 6))
cmds (conj (scene/points->path pts true) [:close])
styled (aesthetic/stylize cmds
[{:op :smooth :samples 24}
{:op :jitter :amount 0.6 :density 0.8
:seed (+ 42 i)}])]
{:node/type :shape/path
:path/commands styled
:style/stroke {:color [:color/rgba 50 40 30 0.8]
:width 1.0}
}))]
{:image/size [w h]
:image/background [:color/rgb 250 245 235]
:image/nodes (vec shapes)}))
Simplex Contour Terrain
Topographic contour lines from simplex noise.
contoursimplexterrain
View source
(defn contour-terrain []
(let [w 600 h 400
levels (mapv (fn [i] (- -0.5 (* i -0.12))) (range 10))
pal (palette/sort-by-lightness
[[:color/rgb 30 60 30] [:color/rgb 60 120 50]
[:color/rgb 140 170 60] [:color/rgb 200 190 100]
[:color/rgb 180 140 80] [:color/rgb 140 100 60]
[:color/rgb 100 80 60] [:color/rgb 220 210 190]
[:color/rgb 240 240 245] [:color/rgb 255 255 255]])]
{:image/size [w h]
:image/background [:color/rgb 20 30 20]
:image/nodes
(vec (mapcat
(fn [li level]
(let [contours (contour/contour-lines noise/simplex2d [0 0 w h]
{:threshold level :resolution 4
:noise-scale 0.008 :seed 42})
c (nth pal (min li (dec (count pal))))]
(mapv (fn [path-node]
(assoc path-node
:style/stroke {:color c :width 1.2}
))
contours)))
(range) levels))}))
Simplex Flow
Flow field using OpenSimplex2 noise with collision detection.
simplexflow-fieldcollision
View source
(defn simplex-flow []
(let [w 600 h 400
lines (flow/flow-field [0 0 w h]
{:density 12 :steps 80 :step-length 2.5
:noise-scale 0.006 :seed 77
:collision-distance 6.0})]
{:image/size [w h]
:image/background :ivory
:image/nodes
(mapv (fn [node]
(let [cmds (:path/commands node)
styled (aesthetic/stylize cmds (aesthetic/ink-preset 42))]
(assoc node
:path/commands styled
:style/stroke {:color [:color/rgba 30 40 80 0.7] :width 1.0}
)))
lines)}))
Simplified Curves
Douglas-Peucker simplification at varying epsilon.
simplifydouglas-peuckeroptimization
View source
(defn simplified-paths []
(let [w 600 h 400
n 80
raw-pts (mapv (fn [i]
(let [x (* (/ (double i) n) w)
y (+ 200 (* 60 (Math/sin (* i 0.15)))
(* 30 (noise/simplex2d (* i 0.1) 0.5)))]
[x y]))
(range n))
epsilons [0 2 8 25]
offsets [50 150 250 350]]
{:image/size [w h]
:image/background :white
:image/nodes
(vec (mapcat
(fn [eps y-off]
(let [pts (if (zero? eps)
raw-pts
(path/simplify raw-pts (double eps)))
offset-pts (mapv (fn [[x y]]
[x (+ (- y 200) y-off)])
pts)
cmds (scene/points->path offset-pts false)]
[{:node/type :shape/path
:path/commands cmds
:style/stroke {:color :steelblue :width 1.5}}]))
epsilons offsets))}))
Split & Color
A single curve split into segments, each a different hue.
splitcurvecolor
View source
(defn split-paths []
(let [w 600 h 400
pts (mapv (fn [i]
(let [x (* (/ (double i) 60) w)
y (+ 200 (* 120 (Math/sin (* i 0.12)))
(* 40 (Math/cos (* i 0.3))))]
[x y]))
(range 60))
cmds (scene/points->path pts false)
segments (path/split-at-length cmds 80)
n-seg (count segments)
pal (mapv #(color/oklch 0.65 0.2 %)
(range 0 360 (/ 360.0 (max 1 n-seg))))]
{:image/size [w h]
:image/background [:color/rgb 245 245 250]
:image/nodes
(vec (map-indexed
(fn [i seg]
{:node/type :shape/path
:path/commands seg
:style/stroke {:color (nth pal (mod i (count pal))) :width 3}})
segments))}))
Stippled Orbits
Points scattered on concentric circles with jitter.
geometricscattercircle
View source
(defn stippled-orbits []
(let [w 500 h 500
cx 250.0 cy 250.0
all-pts (into []
(mapcat
(fn [ri]
(let [r (* ri 28)
n-pts (+ 10 (* ri 8))
pts (prob/scatter-on-circle n-pts r [cx cy] (+ 42 ri))]
(scatter/jitter pts {:amount 2.5 :seed (+ 100 ri)})))
(range 1 9)))]
{:image/size [w h]
:image/background [:color/rgb 20 18 30]
:image/nodes
(mapv (fn [[x y]]
{:node/type :shape/circle
:circle/center [x y]
:circle/radius 1.5
:style/fill [:color/rgba 200 180 255 0.7]})
all-pts)}))
Triangular Peaks
Mountain silhouettes with triangular-distributed heights.
triangulardistributionlandscape
View source
(defn triangular-mountains []
(let [w 600 h 400
ranges
(for [row (range 4)]
(let [base-y (+ 200 (* row 50))
peaks (sort (prob/triangular 12 0.0 (double w) (* w 0.4) (+ 42 row)))
peak-h (prob/triangular 12 30.0 (- 200.0 (* row 30)) 80.0 (+ 99 row))
alpha (- 1.0 (* row 0.2))
blue (+ 30 (* row 40))
pts (vec (concat
[[0 base-y]]
(mapcat (fn [x ph]
[[x base-y] [x (- base-y (double ph))]])
peaks peak-h)
[[(double w) base-y]]))]
{:node/type :shape/path
:path/commands (conj (scene/points->path pts false) [:close])
:style/fill [:color/rgba blue (+ 40 (* row 20))
(+ 80 (* row 20)) alpha]}))]
{:image/size [w h]
:image/background [:color/rgb 200 210 230]
:image/nodes (vec ranges)}))
Watercolor Bloom
Translucent layered copies with jitter deformation.
watercolortexturelayering
View source
(defn watercolor-bloom []
(let [petals
(for [i (range 7)]
(let [angle (* i (/ (* 2 Math/PI) 7))
cx (+ 300 (* 80 (Math/cos angle)))
cy (+ 200 (* 80 (Math/sin angle)))
r 65
pts (mapv (fn [j]
(let [a (* j (/ (* 2 Math/PI) 30))
wobble (+ r (* 8 (Math/sin (* 3 a))))]
[(+ cx (* wobble (Math/cos a)))
(+ cy (* wobble (Math/sin a)))]))
(range 30))
cmds (conj (scene/points->path pts true) [:close])
hue (mod (+ 340 (* i 8)) 360)]
(texture/watercolor
{:node/type :shape/path
:path/commands cmds
:style/fill [:color/hsla hue 0.6 0.55 1.0]}
{:layers 25 :opacity 0.04 :amount 4.0
:seed (+ 100 i)})))
center (texture/watercolor
{:node/type :shape/path
:path/commands (conj (scene/points->path
(mapv (fn [j]
(let [a (* j (/ (* 2 Math/PI) 20))]
[(+ 300 (* 25 (Math/cos a)))
(+ 200 (* 25 (Math/sin a)))]))
(range 20))
true)
[:close])
:style/fill [:color/hsla 50 0.7 0.5 1.0]}
{:layers 20 :opacity 0.05 :amount 3.0 :seed 999})]
{:image/size [600 400]
:image/background :linen
:image/nodes (conj (vec petals) center)}))Artistic Expression

Calligraphy Flow
Animated variable-width calligraphic strokes.
brush-strokesanimationpalette
View source
(defn 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.
scatternoisegradients
View source
(defn chromatic-scatter []
(let [pts (scatter/poisson-disk [30 30 540 340] {:min-dist 18 :seed 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
(fn [v]
(let [t (+ 0.5 (* 0.5 v))]
{:style/fill (palette/gradient-map stops t)
:node/opacity (+ 0.5 (* 0.5 t))}))
{:noise-scale 0.012 :seed 42})]
{: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.
path-decoratorshatchingstipple
View source
(defn 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.
flow-fieldsymmetryanimationgradients
View source
(defn 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.
l-systemdistortion
View source
(defn 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.
hatchingdistortionbrush-strokes
View source
(defn 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.
voronoitypographygradientsscatter
View source
(defn landscape-type []
(let [pts (scatter/poisson-disk [-30 -240 640 290] {:min-dist 35 :seed 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"
[30 230]
{:font/family "SansSerif" :font/size 220 :font/weight :bold}
(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] {:min-dist 22 :seed 77})
:scatter/jitter {:x 2 :y 2 :seed 33}}))]}))
Mandala
Radial symmetry with hatching and stippling.
symmetryhatchingstipplebrush-strokes
View source
(defn 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.
animationpaletteopacity
View source
(defn 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}))
OKLAB Gradients
Vivid color gradients via perceptually uniform interpolation.
oklabcolorgradientinterpolation
View source
(defn oklab-gradients []
(let [w 600 h 400
pairs [[:red :cyan] [:blue :yellow] [:magenta :green]]
bar-h (/ h (* 2 (count pairs)))
bars (mapcat
(fn [i [ca cb]]
(let [y-rgb (* i 2 bar-h)
y-oklab (+ y-rgb bar-h)
n 60]
(concat
;; RGB lerp row
(for [j (range n)]
(let [t (/ (double j) (dec n))
x (* j (/ (double w) n))
cw (/ (double w) n)]
{:node/type :shape/rect
:rect/xy [x y-rgb]
:rect/size [cw bar-h]
:style/fill (color/lerp ca cb t)}))
;; OKLAB lerp row
(for [j (range n)]
(let [t (/ (double j) (dec n))
x (* j (/ (double w) n))
cw (/ (double w) n)]
{:node/type :shape/rect
:rect/xy [x y-oklab]
:rect/size [cw bar-h]
:style/fill (color/lerp-oklab ca cb t)})))))
(range)
pairs)]
{:image/size [w h]
:image/background :white
:image/nodes (vec bars)}))
Organic Mandala
L-system branches with radial symmetry and per-instance color.
l-systemsymmetrygradients
View source
(defn organic-mandala []
(let [branch-cmds (lsystem/lsystem->path-cmds
"F" {"F" "F[-F]F[+F]F"}
{:iterations 3 :angle 25.7 :length 8.0 :origin [300 300] :heading -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.
pattern-fillsshadowspalette
View source
(defn 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.
filterscompositingopacity
View source
(defn 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.
path-morphanimationglowgradients
View source
(defn 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.
voronoiscattercolor
View source
(defn stained-glass []
(let [pts (scatter/poisson-disk [20 20 460 360] {:min-dist 50 :seed 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.
scatterglowopacity
View source
(defn starfield []
(let [pal (:midnight palette/palettes)
star-positions (scatter/poisson-disk [0 0 600 400] {:min-dist 15 :seed 42})
bright-positions (scatter/noise-field [0 0 600 400] {:n 30 :seed 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.
stippleshadows
View source
(defn 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.
noisegradientscolor
View source
(defn 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.
contournoisegradients
View source
(defn 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.
flow-fieldbrush-strokesnoisepalette
View source
(defn 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.
path-booleanopacity
View source
(defn 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.
typographywarpgradients
View source
(defn 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" [70 190]
{:font/family "Serif" :font/size 100
:font/weight :bold})
(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}))]}]})Artisan

Art Deco Sunburst
Gold-on-black geometric rays with decorated border.
symmetrypath-decoratorsgradients
View source
(defn art-deco-sunburst []
(let [gold [:color/rgb 218 175 80]
dark [:color/rgb 15 12 8]
cx 300.0 cy 300.0
rays (vec
(for [i (range 36)]
(let [angle (* i (/ Math/PI 18))
r1 60.0 r2 260.0
w (if (even? i) 0.12 0.06)
a1 (- angle w) a2 (+ angle w)]
{:node/type :shape/path
:path/commands [[:move-to [(+ cx (* r1 (Math/cos a1)))
(+ cy (* r1 (Math/sin a1)))]]
[:line-to [(+ cx (* r2 (Math/cos a1)))
(+ cy (* r2 (Math/sin a1)))]]
[:line-to [(+ cx (* r2 (Math/cos a2)))
(+ cy (* r2 (Math/sin a2)))]]
[:line-to [(+ cx (* r1 (Math/cos a2)))
(+ cy (* r1 (Math/sin a2)))]]
[:close]]
:style/fill {:gradient/type :radial
:gradient/center [cx cy]
:gradient/radius 260.0
:gradient/stops [[0.0 [:color/rgb 255 220 120]]
[1.0 gold]]}})))
center-circle {:node/type :shape/circle
:circle/center [cx cy]
:circle/radius 55.0
:style/fill {:gradient/type :radial
:gradient/center [cx cy]
:gradient/radius 55.0
:gradient/stops [[0.0 [:color/rgb 255 240 180]]
[1.0 gold]]}
:style/stroke {:color gold :width 2}}
border-frame {:node/type :path/decorated
:path/commands [[:move-to [30.0 30.0]]
[:line-to [570.0 30.0]]
[:line-to [570.0 570.0]]
[:line-to [30.0 570.0]]
[:line-to [30.0 30.0]]]
:decorator/shape (assoc (scene/regular-polygon [0.0 0.0] 6.0 4)
:style/fill gold)
:decorator/spacing 20
:decorator/rotate? true}
outer-rect {:node/type :shape/rect
:rect/xy [20.0 20.0]
:rect/size [560.0 560.0]
:style/stroke {:color gold :width 3}}]
{:image/size [600 600]
:image/background dark
:image/nodes (conj rays center-circle border-frame outer-rect)}))
Botanical L-system
Delicate plant with L-system branches, stippled flowers, hatched leaves.
l-systemstipplehatching
View source
(defn botanical-lsystem []
(let [bg [:color/rgb 250 245 235]
brown [:color/rgb 80 55 30]
green [:color/rgb 50 90 30]
flowers
(for [[fx fy r] [[160 80 15] [130 120 12] [200 100 10]
[140 160 8] [180 140 11]]]
{:node/type :shape/circle
:circle/center [(double fx) (double fy)]
:circle/radius (double r)
:style/fill {:fill/type :stipple
:stipple/density 0.6
:stipple/radius 0.8
:stipple/seed (+ fx fy)
:stipple/color [:color/rgb 180 50 80]
:stipple/background [:color/rgb 250 200 210]}
:style/stroke {:color [:color/rgb 180 50 80] :width 0.8}})
leaves
(for [[lx ly] [[120 200] [190 180] [100 260] [200 240]]]
{:node/type :shape/path
:path/commands [[:move-to [(double lx) (double ly)]]
[:curve-to [(+ lx 15.0) (- ly 20.0)]
[(+ lx 25.0) (- ly 15.0)]
[(+ lx 30.0) (double ly)]]
[:curve-to [(+ lx 25.0) (+ ly 15.0)]
[(+ lx 15.0) (+ ly 20.0)]
[(double lx) (double ly)]]]
:style/fill {:fill/type :hatch
:hatch/angle 30
:hatch/spacing 3
:hatch/stroke-width 0.5
:hatch/color green}
:style/stroke {:color green :width 0.8}})]
{:image/size [400 450]
:image/background bg
:image/nodes
(vec (concat
[{:node/type :shape/rect
:rect/xy [0.0 380.0]
:rect/size [400.0 70.0]
:style/fill {:fill/type :hatch
:hatch/angle 0
:hatch/spacing 4
:hatch/stroke-width 0.4
:hatch/color [:color/rgb 140 120 80]
:hatch/background bg}}
{: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 4.0
:lsystem/origin [170 380]
:lsystem/heading -90.0
:style/stroke {:color brown :width 1.2}
:node/transform [[:transform/distort {:type :roughen :amount 1 :seed 42}]]}]
leaves
flowers))}))
Celtic Interlace
Knotwork paths with grid symmetry and path decorators.
symmetrypath-decorators
View source
(defn celtic-interlace []
(let [gold [:color/rgb 180 140 60]
dark [:color/rgb 30 25 15]
bg [:color/rgb 40 35 25]
knot-unit
[{:node/type :shape/path
:path/commands [[:move-to [0.0 25.0]]
[:curve-to [25.0 0.0] [75.0 50.0] [100.0 25.0]]
[:curve-to [75.0 0.0] [25.0 50.0] [0.0 25.0]]]
:style/stroke {:color gold :width 5}
:style/fill {:fill/type :hatch
:hatch/angle 45
:hatch/spacing 6
:hatch/stroke-width 0.5
:hatch/color [:color/rgb 140 110 40]}}
{:node/type :shape/path
:path/commands [[:move-to [0.0 25.0]]
[:curve-to [25.0 50.0] [75.0 0.0] [100.0 25.0]]]
:style/stroke {:color [:color/rgb 220 180 80] :width 3}}]
border-path [[:move-to [40.0 40.0]]
[:line-to [460.0 40.0]]
[:line-to [460.0 460.0]]
[:line-to [40.0 460.0]]
[:line-to [40.0 40.0]]]
decorated-border
{:node/type :path/decorated
:path/commands border-path
:decorator/shape {:node/type :shape/circle
:circle/center [0.0 0.0]
:circle/radius 4.0
:style/fill gold}
:decorator/spacing 15
:decorator/rotate? false}
inner-border
{:node/type :shape/rect
:rect/xy [50.0 50.0]
:rect/size [400.0 400.0]
:style/stroke {:color gold :width 2}}]
{:image/size [500 500]
:image/background bg
:image/nodes
[{:node/type :symmetry
:symmetry/type :grid
:symmetry/cols 4
:symmetry/rows 4
:symmetry/spacing [100 100]
:symmetry/origin [60 60]
:group/children knot-unit}
decorated-border
inner-border]}))
Contour Terrain
Topographic map with contour layers, hatched water, and compass rose.
contourhatchingpath-decorators
View source
(defn contour-terrain []
(let [thresholds (mapv #(- (* % 0.12) 0.5) (range 10))
land-pal (palette/gradient-palette [:color/rgb 60 100 50] [:color/rgb 200 180 140] 10)
water
{:node/type :shape/rect
:rect/xy [0.0 0.0]
:rect/size [600.0 450.0]
:style/fill {:fill/type :hatch
:hatch/angle 0
:hatch/spacing 6
:hatch/stroke-width 0.4
:hatch/color [:color/rgb 60 80 140]
:hatch/background [:color/rgb 200 215 240]}}
contours
(vec (map-indexed
(fn [i threshold]
{:node/type :contour
:contour/bounds [0 0 600 450]
:contour/opts {:thresholds [threshold]
:resolution 3
:noise-scale 0.01
:seed 55}
:style/stroke {:color (nth land-pal i)
:width (if (zero? (mod i 3)) 1.8 0.7)}})
thresholds))
;; Compass rose using path decorators
rose-cx 530.0 rose-cy 60.0
compass-ring
{:node/type :path/decorated
:path/commands (into [[:move-to [(+ rose-cx 30.0) rose-cy]]]
(for [a (range 10 370 10)]
(let [rad (Math/toRadians (double a))]
[:line-to [(+ rose-cx (* 30.0 (Math/cos rad)))
(+ rose-cy (* 30.0 (Math/sin rad)))]])))
:decorator/shape {:node/type :shape/circle
:circle/center [0.0 0.0]
:circle/radius 2.0
:style/fill [:color/rgb 120 80 40]}
:decorator/spacing 12
:decorator/rotate? false}
compass-needle
(assoc (scene/triangle [rose-cx (- rose-cy 25.0)]
[(- rose-cx 6.0) (+ rose-cy 8.0)]
[(+ rose-cx 6.0) (+ rose-cy 8.0)])
:style/fill [:color/rgb 180 40 30]
:style/stroke {:color [:color/rgb 80 20 10] :width 1})]
{:image/size [600 450]
:image/background [:color/rgb 200 215 240]
:image/nodes (vec (concat [water] contours [compass-ring compass-needle]))}))
Geometric Tiling
Islamic-inspired star pattern with grid symmetry and hatching.
symmetryhatching
View source
(defn geometric-tiling []
(let [teal [:color/rgb 20 100 120]
gold [:color/rgb 180 150 60]
cream [:color/rgb 245 238 220]
tile-star (scene/star [0.0 0.0] 35.0 15.0 8)
tile-inner (scene/regular-polygon [0.0 0.0] 14.0 8)]
{:image/size [500 500]
:image/background cream
:image/nodes
[{:node/type :symmetry
:symmetry/type :grid
:symmetry/cols 5
:symmetry/rows 5
:symmetry/spacing [95 95]
:symmetry/origin [55 55]
:group/children
[(assoc tile-star
:style/fill {:fill/type :hatch
:hatch/layers [{:angle 0 :spacing 4}
{:angle 60 :spacing 4}
{:angle -60 :spacing 4}]
:hatch/stroke-width 0.5
:hatch/color teal}
:style/stroke {:color teal :width 1.5})
(assoc tile-inner
:style/fill gold
:style/stroke {:color [:color/rgb 120 100 30] :width 1})]}
{:node/type :shape/rect
:rect/xy [10.0 10.0]
:rect/size [480.0 480.0]
:style/stroke {:color teal :width 3}}]}))
Glitch Art
Posterized and duotoned shapes with offset color channels.
filtersposterizeduotone
View source
(defn glitch-art []
{:image/size [500 400]
:image/background [:color/rgb 10 10 15]
:image/nodes
[;; Duotone layer — shifted left
{:node/type :group
:group/filter [:duotone [:color/rgb 0 20 60] [:color/rgb 0 255 200]]
:group/children
[{:node/type :shape/rect
:rect/xy [30.0 60.0]
:rect/size [220.0 280.0]
:style/fill [:color/rgb 200 200 200]}
{:node/type :shape/circle
:circle/center [140.0 200.0]
:circle/radius 80.0
:style/fill [:color/rgb 100 100 100]}]}
;; Posterize layer — shifted right
{:node/type :group
:group/filter [:posterize 3]
:group/children
[{:node/type :shape/rect
:rect/xy [250.0 60.0]
:rect/size [220.0 280.0]
:style/fill [:color/rgb 255 80 120]}
{:node/type :shape/circle
:circle/center [360.0 200.0]
:circle/radius 80.0
:style/fill [:color/rgb 255 200 50]}]}
;; Offset "red channel" strip
{:node/type :group
:group/composite :screen
:group/children
[{:node/type :shape/rect
:rect/xy [0.0 150.0]
:rect/size [500.0 40.0]
:style/fill [:color/rgba 255 0 50 0.4]}]}
;; Offset "cyan channel" strip
{:node/type :group
:group/composite :screen
:group/children
[{:node/type :shape/rect
:rect/xy [0.0 190.0]
:rect/size [500.0 30.0]
:style/fill [:color/rgba 0 255 200 0.3]}]}
;; Scan lines
{:node/type :group
:group/children
(vec (for [i (range 0 400 4)]
{:node/type :shape/line
:line/from [0.0 (double i)]
:line/to [500.0 (double i)]
:node/opacity 0.08
:style/stroke {:color [:color/rgb 255 255 255] :width 1}}))}]})
Halftone Layers
CMYK-style overlapping shapes with halftone filter at different angles.
filterscompositing
View source
(defn halftone-layers []
{:image/size [500 500]
:image/background [:color/rgb 245 240 235]
:image/nodes
[{:node/type :group
:group/composite :multiply
:group/filter [:halftone 6 0]
:group/children
[{:node/type :shape/circle
:circle/center [200.0 200.0]
:circle/radius 150.0
:style/fill [:color/rgb 0 180 220]}]}
{:node/type :group
:group/composite :multiply
:group/filter [:halftone 6 30]
:group/children
[{:node/type :shape/circle
:circle/center [300.0 200.0]
:circle/radius 150.0
:style/fill [:color/rgb 220 30 80]}]}
{:node/type :group
:group/composite :multiply
:group/filter [:halftone 6 60]
:group/children
[{:node/type :shape/circle
:circle/center [250.0 300.0]
:circle/radius 150.0
:style/fill [:color/rgb 255 220 40]}]}]})
Memphis Pattern
Bold 80s geometric pattern with scatter and pattern fills.
pattern-fillsscatter
View source
(defn memphis-pattern []
(let [pink [:color/rgb 255 100 150]
yellow [:color/rgb 255 220 50]
cyan [:color/rgb 50 210 230]
black [:color/rgb 20 20 20]
bg [:color/rgb 245 235 220]
circles
(for [[x y r c] [[80 80 35 pink] [320 100 45 cyan] [180 300 30 yellow]
[420 280 40 pink] [500 120 25 cyan]]]
{:node/type :shape/circle
:circle/center [(double x) (double y)]
:circle/radius (double r)
:style/fill c
:style/stroke {:color black :width 3}})
triangles
(for [[x y c] [[250 60 yellow] [100 220 cyan] [400 180 pink] [350 350 yellow]]]
(assoc (scene/triangle [(double x) (double y)]
[(+ (double x) 40.0) (+ (double y) 60.0)]
[(- (double x) 40.0) (+ (double y) 60.0)])
:style/fill c
:style/stroke {:color black :width 3}))
squiggles
(for [[sx sy] [[50 160] [280 220] [450 50] [150 380]]]
(let [pts (for [i (range 8)]
[(+ sx (* i 15)) (+ sy (* 10 (Math/sin (* i 1.5))))])]
{:node/type :shape/path
:path/commands (into [[:move-to (first pts)]]
(mapv #(vector :line-to %) (rest pts)))
:style/stroke {:color black :width 3}}))
pattern-rect
{:node/type :shape/rect
:rect/xy [380.0 320.0]
:rect/size [100.0 80.0]
:style/fill {:fill/type :pattern
:pattern/size [16 16]
:pattern/nodes
[{:node/type :shape/rect
:rect/xy [0.0 0.0] :rect/size [16.0 16.0]
:style/fill cyan}
{:node/type :shape/circle
:circle/center [8.0 8.0] :circle/radius 3.0
:style/fill black}]}
:style/stroke {:color black :width 3}}
dots (scatter/poisson-disk [10 10 530 390] {:min-dist 40 :seed 42})]
{:image/size [550 420]
:image/background bg
:image/nodes
(vec (concat circles triangles squiggles [pattern-rect]
[{:node/type :scatter
:scatter/shape {:node/type :shape/circle
:circle/center [0.0 0.0]
:circle/radius 3.0
:style/fill black}
:scatter/positions dots
:scatter/jitter {:x 5 :y 5 :seed 77}}]))}))
Morphing Geometry
Animated loop: triangle to square to pentagon to hexagon to circle.
path-morphanimation
View source
(defn morphing-geometry []
(let [cx 200.0 cy 200.0 r 100.0
tri-cmds (:path/commands (scene/regular-polygon [cx cy] r 3))
sq-cmds (:path/commands (scene/regular-polygon [cx cy] r 4))
pent-cmds (:path/commands (scene/regular-polygon [cx cy] r 5))
hex-cmds (:path/commands (scene/regular-polygon [cx cy] r 6))
circ-cmds (:path/commands (scene/regular-polygon [cx cy] r 48))
shapes [tri-cmds sq-cmds pent-cmds hex-cmds circ-cmds]
n-shapes (count shapes)
colors [[:color/rgb 230 70 70]
[:color/rgb 70 180 230]
[:color/rgb 80 200 120]
[:color/rgb 230 180 50]
[:color/rgb 180 80 220]]]
{:frames
(anim/frames 50
(fn [t]
(let [progress (* t n-shapes)
idx (min (dec n-shapes) (int progress))
next-idx (mod (inc idx) n-shapes)
local-t (- progress idx)
eased (anim/ease-in-out-cubic local-t)
cmds (morph/morph-auto (nth shapes idx)
(nth shapes next-idx)
eased)
color (palette/gradient-map
[[0.0 (nth colors idx)]
[1.0 (nth colors next-idx)]]
eased)]
{:image/size [400 400]
:image/background [:color/rgb 20 18 30]
:image/nodes
[{:node/type :shape/path
:path/commands cmds
:style/fill color
:style/stroke {:color [:color/rgba 255 255 255 0.4] :width 2}}]})))
:fps 20}))
Paper Collage
Torn-edge shapes with grain, hatching, stippling, and shadows.
graindistortionhatchingstippleshadows
View source
(defn paper-collage []
(let [bg [:color/rgb 230 220 200]]
{:image/size [500 450]
:image/background bg
:image/nodes
[;; Bottom layer: hatched rectangle with shadow and grain
{:node/type :group
:group/filter [:grain 0.12 42]
:group/children
[{:node/type :shape/rect
:rect/xy [60.0 120.0]
:rect/size [200.0 250.0]
:style/fill {:fill/type :hatch
:hatch/layers [{:angle 45 :spacing 5}
{:angle -45 :spacing 5}]
:hatch/stroke-width 0.6
:hatch/color [:color/rgb 40 60 100]
:hatch/background [:color/rgb 200 210 230]}
:style/stroke {:color [:color/rgb 40 60 100] :width 1}
:effect/shadow {:dx 5 :dy 5 :blur 8
:color [:color/rgb 0 0 0] :opacity 0.25}
:node/transform [[:transform/distort {:type :roughen :amount 4 :seed 42}]]}]}
;; Middle layer: solid warm shape with grain
{:node/type :group
:group/filter [:grain 0.1 99]
:group/children
[{:node/type :shape/path
:path/commands [[:move-to [180.0 80.0]]
[:line-to [380.0 90.0]]
[:line-to [370.0 280.0]]
[:line-to [190.0 270.0]]
[:close]]
:style/fill [:color/rgb 220 160 100]
:style/stroke {:color [:color/rgb 160 110 60] :width 1}
:effect/shadow {:dx 4 :dy 6 :blur 10
:color [:color/rgb 0 0 0] :opacity 0.2}
:node/transform [[:transform/distort {:type :roughen :amount 5 :seed 77}]]}]}
;; Top layer: stippled circle
{:node/type :shape/circle
:circle/center [320.0 300.0]
:circle/radius 80.0
:style/fill {:fill/type :stipple
:stipple/density 0.5
:stipple/radius 1.2
:stipple/seed 33
:stipple/color [:color/rgb 140 40 40]
:stipple/background [:color/rgb 240 220 210]}
:style/stroke {:color [:color/rgb 140 40 40] :width 1.5}
:effect/shadow {:dx 3 :dy 4 :blur 6
:color [:color/rgb 0 0 0] :opacity 0.2}
:node/transform [[:transform/distort {:type :roughen :amount 3 :seed 55}]]}
;; Decorative strip
{:node/type :shape/rect
:rect/xy [100.0 350.0]
:rect/size [300.0 30.0]
:style/fill {:fill/type :hatch
:hatch/angle 0
:hatch/spacing 4
:hatch/stroke-width 0.5
:hatch/color [:color/rgb 60 80 60]
:hatch/background [:color/rgb 210 220 200]}
:node/transform [[:transform/distort {:type :roughen :amount 3 :seed 88}]]}]}))
Pointillist Landscape
Dense scatter of colored dots forming gradient sky and ground.
scatternoisevariation
View source
(defn pointillist-landscape []
(let [pts (scatter/poisson-disk [5 5 595 395] {:min-dist 8 :seed 42})
sky-stops [[0.0 [:color/rgb 40 60 150]]
[0.4 [:color/rgb 120 160 220]]
[0.6 [:color/rgb 220 180 100]]
[1.0 [:color/rgb 60 130 50]]]
overrides (vary/by-noise pts
(fn [v]
(let [[_x y] nil]
{:node/opacity (+ 0.6 (* 0.4 (Math/abs v)))}))
{:noise-scale 0.008 :seed 42})
;; Color by y-position via manual override
colored-overrides
(vec (map-indexed
(fn [i [_x y]]
(let [t (/ y 400.0)
color (palette/gradient-map sky-stops t)]
(merge (nth overrides i) {:style/fill color})))
pts))]
{:image/size [600 400]
:image/background [:color/rgb 245 240 230]
:image/nodes
[{:node/type :scatter
:scatter/shape {:node/type :shape/circle
:circle/center [0.0 0.0]
:circle/radius 3.5}
:scatter/positions pts
:scatter/overrides colored-overrides}]}))
Spiral Text
Text following an Archimedean spiral with gradient color.
text-on-pathgradients
View source
(defn spiral-text []
(let [cx 250.0 cy 250.0
;; Build Archimedean spiral as path commands
spiral-pts (for [i (range 0 720 5)]
(let [angle (Math/toRadians (double i))
r (+ 30.0 (* 0.15 i))]
[(+ cx (* r (Math/cos angle)))
(+ cy (* r (Math/sin angle)))]))
spiral-cmds (into [[:move-to (first spiral-pts)]]
(mapv #(vector :line-to %) (rest spiral-pts)))
text-node (scene/text-on-path
"In the beginning was the word and the word was data and the data was good -- "
spiral-cmds
{:font/family "Serif" :font/size 14})
;; Spiral path as faint guide
guide {:node/type :shape/path
:path/commands spiral-cmds
:style/stroke {:color [:color/rgba 100 80 160 0.15] :width 0.5}}]
{:image/size [500 500]
:image/background [:color/rgb 245 240 230]
:image/nodes
[guide
(assoc text-node
:style/fill {:gradient/type :linear
:gradient/from [0.0 0.0]
:gradient/to [500.0 500.0]
:gradient/stops [[0.0 [:color/rgb 40 20 100]]
[0.5 [:color/rgb 180 40 80]]
[1.0 [:color/rgb 220 160 30]]]})]}))
Sumi-e Bamboo
Minimalist ink wash with warped brush strokes and stippled leaves.
brush-strokeswarpstipple
View source
(defn sumi-e-bamboo []
(let [ink [:color/rgb 30 30 30]
bg [:color/rgb 245 240 228]
stems
(for [[sx sy h] [[150 380 250] [200 390 220] [170 395 180]]]
{:node/type :shape/path
:path/commands [[:move-to [(double sx) (double sy)]]
[:line-to [(double sx) (double (- sy h))]]]
:stroke/profile :brush
:style/stroke {:color ink :width 8}})
nodes-on-stems
(for [[sx sy h] [[150 380 250] [200 390 220] [170 395 180]]
frac [0.3 0.5 0.7]]
(let [ny (- sy (* h frac))]
{:node/type :shape/path
:path/commands [[:move-to [(- (double sx) 6) (double ny)]]
[:line-to [(+ (double sx) 6) (double ny)]]]
:style/stroke {:color ink :width 3}}))
leaves
(for [[lx ly angle] [[130 200 -40] [180 160 30] [215 230 -20]
[140 280 35] [195 140 -50] [160 310 25]]]
{:node/type :shape/path
:path/commands [[:move-to [(double lx) (double ly)]]
[:curve-to [(+ lx (* 30 (Math/cos (Math/toRadians angle))))
(+ ly (* 30 (Math/sin (Math/toRadians angle))))]
[(+ lx (* 50 (Math/cos (Math/toRadians angle))))
(+ ly (* 15 (Math/sin (Math/toRadians angle))))]
[(+ lx (* 60 (Math/cos (Math/toRadians angle))))
(+ ly (* 5 (Math/sin (Math/toRadians angle))))]]]
:stroke/profile :pointed
:style/stroke {:color [:color/rgba 30 30 30 0.7] :width 6}})
stippled-accent
{:node/type :shape/circle
:circle/center [350.0 150.0]
:circle/radius 60.0
:style/fill {:fill/type :stipple
:stipple/density 0.15
:stipple/radius 1.0
:stipple/seed 42
:stipple/color [:color/rgba 30 30 30 0.4]
:stipple/background bg}
:node/opacity 0.5}]
{:image/size [500 450]
:image/background bg
:image/nodes
[{:node/type :group
:group/warp {:type :wave :amplitude 3 :frequency 0.02 :seed 42}
:group/children (vec (concat stems nodes-on-stems leaves [stippled-accent]))}]}))
Type Poster
Bold text-clip with gradient fill and drop shadow.
text-clipgradientsshadows
View source
(defn type-poster []
(let [gradient-bg
{:node/type :shape/rect
:rect/xy [-10.0 -250.0]
:rect/size [620.0 300.0]
:style/fill {:gradient/type :linear
:gradient/from [0.0 -250.0]
:gradient/to [0.0 50.0]
:gradient/stops [[0.0 [:color/rgb 255 80 60]]
[0.5 [:color/rgb 255 180 40]]
[1.0 [:color/rgb 60 200 255]]]}}
pattern-overlay
{:node/type :shape/rect
:rect/xy [-10.0 -250.0]
:rect/size [620.0 300.0]
:style/fill {:fill/type :pattern
:pattern/size [20 20]
:pattern/nodes
[{:node/type :shape/circle
:circle/center [10.0 10.0]
:circle/radius 3.0
:style/fill [:color/rgba 255 255 255 0.15]}]}
:node/opacity 0.5}
shadow-text
(-> (scene/text-outline "BOLD" [42 232]
{:font/family "SansSerif" :font/size 180 :font/weight :bold})
(assoc :style/fill [:color/rgba 0 0 0 0.3]))]
{:image/size [600 300]
:image/background [:color/rgb 20 15 30]
:image/nodes
[shadow-text
(scene/text-clip "BOLD"
[38 228]
{:font/family "SansSerif" :font/size 180 :font/weight :bold}
[gradient-bg pattern-overlay])]}))
Vintage Map
Contour lines, hatched ocean, stippled land, decorative frame, and text.
contourhatchingstipplepath-decoratorstext
View source
(defn vintage-map []
(let [parchment [:color/rgb 235 220 190]
ink [:color/rgb 50 40 30]
ocean
{:node/type :shape/rect
:rect/xy [30.0 30.0]
:rect/size [540.0 390.0]
:style/fill {:fill/type :hatch
:hatch/angle 0
:hatch/spacing 5
:hatch/stroke-width 0.3
:hatch/color [:color/rgb 80 100 140]
:hatch/background [:color/rgb 200 210 225]}}
island
{:node/type :shape/path
:path/commands [[:move-to [200.0 180.0]]
[:curve-to [250.0 120.0] [350.0 130.0] [380.0 180.0]]
[:curve-to [400.0 220.0] [390.0 280.0] [340.0 300.0]]
[:curve-to [290.0 320.0] [220.0 310.0] [190.0 260.0]]
[:curve-to [170.0 220.0] [175.0 200.0] [200.0 180.0]]]
:style/fill {:fill/type :stipple
:stipple/density 0.3
:stipple/radius 0.8
:stipple/seed 42
:stipple/color [:color/rgb 120 100 60]
:stipple/background parchment}
:style/stroke {:color ink :width 1.5}
:node/transform [[:transform/distort {:type :roughen :amount 3 :seed 42}]]}
contours
(vec (for [i (range 5)]
{:node/type :contour
:contour/bounds [150 100 300 250]
:contour/opts {:thresholds [(* i 0.15)]
:resolution 3
:noise-scale 0.015
:seed 42}
:style/stroke {:color [:color/rgba 100 80 40 0.5] :width 0.6}}))
frame
{:node/type :path/decorated
:path/commands [[:move-to [20.0 20.0]]
[:line-to [580.0 20.0]]
[:line-to [580.0 430.0]]
[:line-to [20.0 430.0]]
[:line-to [20.0 20.0]]]
:decorator/shape (assoc (scene/regular-polygon [0.0 0.0] 5.0 4)
:style/fill ink)
:decorator/spacing 18
:decorator/rotate? true}
title-text
(-> (scene/text-outline "TERRA INCOGNITA" [140 440]
{:font/family "Serif" :font/size 28 :font/weight :bold})
(assoc :style/fill ink))]
{:image/size [600 460]
:image/background parchment
:image/nodes (vec (concat [ocean island] contours [frame title-text]))}))
Watercolor Blooms
Soft overlapping translucent circles with multiply blending.
compositingtransparency
View source
(defn watercolor-blooms []
(let [pal (:pastel palette/palettes)
blooms
(for [[x y r ci] [[150 180 100 0] [280 160 90 1] [200 280 110 2]
[350 250 85 3] [120 320 75 4] [400 150 95 0]
[320 340 80 1] [180 120 70 2] [450 300 65 3]]]
{:node/type :shape/circle
:circle/center [(double x) (double y)]
:circle/radius (double r)
:style/fill (nth pal ci)
:node/opacity 0.35})]
{:image/size [550 450]
:image/background [:color/rgb 250 248 242]
:image/nodes
[{:node/type :group
:group/composite :multiply
:group/children (vec blooms)}]}))
Woodcut Landscape
Cross-hatched mountains with brush strokes on cream.
hatchingdistortionbrush-strokes
View source
(defn woodcut-landscape []
(let [cream [:color/rgb 245 235 215]
ink [:color/rgb 25 20 15]
sky-hatches
(for [i (range 20)]
(let [y (+ 20 (* i 12))]
{:node/type :shape/path
:path/commands [[:move-to [0.0 (double y)]]
[:line-to [600.0 (double y)]]]
:style/stroke {:color ink :width 0.4}
:node/opacity 0.3}))
mountain
{:node/type :shape/path
:path/commands [[:move-to [0.0 280.0]]
[:line-to [80.0 200.0]]
[:line-to [150.0 130.0]]
[:line-to [220.0 100.0]]
[:line-to [280.0 140.0]]
[:line-to [340.0 90.0]]
[:line-to [400.0 120.0]]
[:line-to [460.0 160.0]]
[:line-to [520.0 200.0]]
[:line-to [600.0 250.0]]
[:line-to [600.0 400.0]]
[:line-to [0.0 400.0]]
[:close]]
:style/fill {:fill/type :hatch
:hatch/layers [{:angle 45 :spacing 4}
{:angle -30 :spacing 6}
{:angle 0 :spacing 10}]
:hatch/stroke-width 0.8
:hatch/color ink}
:style/stroke {:color ink :width 2.5}
:node/transform [[:transform/distort {:type :roughen :amount 3 :seed 42}]]}
foreground
{:node/type :shape/path
:path/commands [[:move-to [0.0 340.0]]
[:line-to [200.0 320.0]]
[:line-to [400.0 330.0]]
[:line-to [600.0 310.0]]
[:line-to [600.0 400.0]]
[:line-to [0.0 400.0]]
[:close]]
:style/fill {:fill/type :hatch
:hatch/layers [{:angle 0 :spacing 3}
{:angle 90 :spacing 3}]
:hatch/stroke-width 1.0
:hatch/color ink}
:style/stroke {:color ink :width 3}
:node/transform [[:transform/distort {:type :roughen :amount 2 :seed 99}]]}
river
{:node/type :shape/path
:path/commands [[:move-to [0.0 350.0]]
[:curve-to [150.0 330.0] [300.0 360.0] [450.0 340.0]]
[:curve-to [520.0 330.0] [570.0 345.0] [600.0 335.0]]]
:stroke/profile :brush
:style/stroke {:color ink :width 6}}]
{:image/size [600 400]
:image/background cream
:image/nodes (vec (concat sky-hatches [mountain foreground river]))}))
Woven Ribbons
Animated interlocking sine waves with clipping for over/under illusion.
clippinganimation
View source
(defn woven-ribbons []
{:frames
(anim/frames 40
(fn [t]
(let [w 500 h 300
phase (* t 2 Math/PI)
ribbon-w 20.0
make-ribbon
(fn [idx color amp freq y-off]
(let [pts (for [x (range 0 (inc w) 4)]
[(double x)
(+ y-off (* amp (Math/sin (+ (* x freq) phase (* idx 1.2)))))])
upper-pts pts
lower-pts (map (fn [[x y]] [x (+ y ribbon-w)]) pts)
cmds (into [[:move-to (first upper-pts)]]
(concat
(mapv #(vector :line-to %) (rest upper-pts))
(mapv #(vector :line-to %) (reverse lower-pts))
[[:close]]))]
{:node/type :shape/path
:path/commands cmds
:style/fill color
:style/stroke {:color [:color/rgba 0 0 0 0.3] :width 0.5}}))
ribbons [(make-ribbon 0 [:color/rgb 220 60 60] 40 0.025 100.0)
(make-ribbon 1 [:color/rgb 60 120 220] 40 0.025 160.0)
(make-ribbon 2 [:color/rgb 60 180 80] 40 0.025 220.0)]
;; Clip alternating sections to create weave
clip-sections
(fn [ribbon-idx ribbon]
(let [even-clips
(for [seg (range 0 (/ w 50))]
(let [x-start (* seg 50)
show? (even? (+ seg ribbon-idx))]
(when show?
{:node/type :shape/rect
:rect/xy [(double x-start) 0.0]
:rect/size [50.0 (double h)]})))
clip-nodes (vec (remove nil? even-clips))]
[{:node/type :group
:group/clip {:node/type :shape/rect
:rect/xy [0.0 0.0]
:rect/size [(double w) (double h)]}
:group/children [ribbon]}]))]
{:image/size [w h]
:image/background [:color/rgb 240 235 225]
:image/nodes
(vec (concat
;; Back layers (full ribbons at lower opacity)
(map #(assoc % :node/opacity 0.3) ribbons)
;; Front layers with clipping
(mapcat (fn [i] (clip-sections i (nth ribbons i)))
(range 3))))})))
:fps 20})2D Scenes

Blooming Tree
A recursive fractal tree that grows from trunk to full canopy, then sways in the wind.
animationmathcolor
View source
(defn 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.
animationcolormath
View source
(defn 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.
cellular-automataanimationcolor
View source
(defn 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.
animationcolormath
View source
(defn 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.
symmetryanimationcolor
View source
(defn 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.
animationmathcolor
View source
(defn 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.
animationmathcoloropacity
View source
(defn 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.
animationmath
View source
(defn 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.
particlesanimationcolor
View source
(defn 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.
animationmathcolor
View source
(defn pendulum-wave []
{:frames
(anim/frames 150
(fn [t]
{:image/size [500 400]
:image/background [:color/rgb 245 243 238]
:image/nodes
(vec (for [p (range 15)]
(let [freq (+ 1.5 (* p 0.15))
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.
animationmathcolor
View source
(defn 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.
animationcolormath
View source
(defn 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.
animationcolormath
View source
(defn 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.
animationgradientscolor
View source
(defn 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.
animationcoloropacity
View source
(defn 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

Alien Landscape
Ridge noise heightfield with altitude-banded colors and dramatic lighting.
3dnoisegradients
View source
(defn alien-landscape []
(let [mesh (-> (s3d/heightfield-mesh
{:field (field/noise-field :scale 0.4 :variant :ridge :seed 99)
:bounds [-4 -4 8 8]
:grid [32 32]
:height 2.5})
(s3d/color-mesh {:color/type :axis-gradient
:color/axis :y
:color/palette [[:color/rgb 30 50 40]
[:color/rgb 140 120 80]
[:color/rgb 200 190 170]
[:color/rgb 255 255 255]]}))
proj (s3d/look-at (s3d/perspective {:scale 60 :origin [200 250]
:distance 7})
[3 1.8 4] [0 0.5 0])]
{:image/size [400 400]
:image/background [:color/rgb 60 70 90]
:image/nodes
[(s3d/render-mesh proj mesh
{:light {:light/direction [1 1.5 0.3]
:light/ambient 0.2
:light/intensity 0.8}
:cull-back false})]}))
Auto-Smooth Cube
A cube subdivided with hard edges preserved — smooth surfaces with crisp corners.
3dsubdivisionsmoothing
View source
(defn auto-smooth-cube []
(let [base (s3d/cube-mesh [-1 -1 -1] 2)
hard (s3d/auto-smooth-edges base {:angle (/ Math/PI 4)})
mesh (s3d/subdivide base {:iterations 3 :hard-edges hard})
proj (s3d/orbit (s3d/orthographic {:scale 65 :origin [200 200]})
[0 0 0]
{:radius 5 :yaw 0.6 :pitch -0.35})]
{:image/size [400 400]
:image/background [:color/rgb 245 243 238]
:image/nodes
[(s3d/render-mesh proj mesh
{:style {:style/fill [:color/rgb 180 160 200]}
:light {:light/direction [1 2 0.5]
:light/ambient 0.2
:light/intensity 0.8}
:shading :smooth})]}))
Camera: Perspective FOV
Field-of-view control for perspective projection; closer objects appear larger.
3dcolor
View source
(defn 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.
3dcolor
View source
(defn 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.
3danimation
View source
(defn 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]
{:radius 8
:yaw (* t 2.0 Math/PI)
:pitch -0.4}))}))
:fps 30})
Colored Point Lights
A sphere lit by warm and cool omni lights with hemisphere ambient.
3dcolor
View source
(defn colored-point-lights []
(let [mesh (s3d/sphere-mesh 1.5 {:segments 24 :rings 16})
proj (s3d/perspective {:scale 120 :origin [200 200]
:yaw 0.3 :pitch -0.25 :distance 6})]
{:image/size [400 400]
:image/background [:color/rgb 15 15 20]
:image/nodes
[(s3d/render-mesh proj mesh
{:style {:style/fill [:color/rgb 200 200 200]
:material (material/phong :ambient 0.05 :diffuse 0.8
:specular 0.5 :shininess 48.0)}
:lights [(material/omni [3 2 2]
:color [:color/rgb 255 180 100]
:multiplier 1.5
:decay :inverse :decay-start 2.0)
(material/omni [-3 1 -1]
:color [:color/rgb 80 130 255]
:multiplier 1.2
:decay :inverse :decay-start 2.0)
(material/hemisphere
[:color/rgb 40 50 80]
[:color/rgb 15 10 5]
:multiplier 0.15)]})]}))
Coral Growth
A sphere with noise-selected faces extruded outward, creating organic coral-like forms.
3dnoisegradients
View source
(defn coral-growth []
(let [mesh (-> (s3d/sphere-mesh 1.2 {:segments 12 :rings 8})
(s3d/extrude-faces {:select/type :field
:select/field (field/noise-field :scale 1.5 :seed 33)
:select/threshold 0.1
:extrude/amount 0.6
:extrude/scale 0.7})
(s3d/color-mesh {:color/type :axis-gradient
:color/axis :y
:color/palette [[:color/rgb 200 80 100]
[:color/rgb 255 160 120]
[:color/rgb 255 220 180]]}))
proj (s3d/orbit (s3d/orthographic {:scale 65 :origin [200 200]})
[0 0 0]
{:radius 5 :yaw 0.8 :pitch -0.3})]
{:image/size [400 400]
:image/background [:color/rgb 20 30 50]
:image/nodes
[(s3d/render-mesh proj mesh
{:light {:light/direction [1 2 1]
:light/ambient 0.2
:light/intensity 0.8}})]}))
Crystal Cluster
An icosahedron with noise-extruded faces creating sharp crystalline growths.
3dnoisecolor
View source
(defn crystal-cluster []
(let [mesh (-> (s3d/platonic-mesh :icosahedron 1.0)
(s3d/extrude-faces {:select/type :field
:select/field (field/noise-field :scale 2.5 :seed 17)
:select/threshold 0.0
:extrude/amount 0.8
:extrude/scale 0.3})
(s3d/color-mesh {:color/type :normal-map
:color/palette [[:color/rgb 100 140 220]
[:color/rgb 160 200 255]
[:color/rgb 80 100 180]]}))
proj (s3d/orbit (s3d/orthographic {:scale 60 :origin [200 200]})
[0 0 0]
{:radius 5 :yaw 0.5 :pitch -0.35})]
{:image/size [400 400]
:image/background [:color/rgb 15 15 25]
:image/nodes
[(s3d/render-mesh proj mesh
{:light {:light/direction [1 2 1]
:light/ambient 0.15
:light/intensity 0.85}})]}))
Detailed Panel
A cube with noise-driven surface detail — procedural mechanical panels.
3dnoisesubdivision
View source
(defn detailed-panel []
(let [mesh (-> (s3d/cube-mesh [-1 -1 -1] 2)
(s3d/subdivide {:iterations 1})
(s3d/detail-faces {:select/type :all
:detail/field (field/noise-field :scale 4.0 :seed 77)
:detail/inset 0.08
:detail/depth-range [0.01 0.12]})
(s3d/color-mesh {:color/type :field
:color/field (field/noise-field :scale 2.0 :seed 77)
:color/palette [[:color/rgb 140 150 160]
[:color/rgb 100 110 120]
[:color/rgb 170 175 180]]}))
proj (s3d/orbit (s3d/orthographic {:scale 55 :origin [200 200]})
[0 0 0]
{:radius 5 :yaw 0.65 :pitch -0.35})]
{:image/size [400 400]
:image/background [:color/rgb 25 25 30]
:image/nodes
[(s3d/render-mesh proj mesh
{:light {:light/direction [1 2 0.5]
:light/ambient 0.15
:light/intensity 0.85}})]}))
Geodesic Sphere
An icosahedron subdivided twice — a geodesic sphere with uniform face distribution.
3dsubdivision
View source
(defn geodesic-sphere []
(let [mesh (-> (s3d/platonic-mesh :icosahedron 1.5)
(s3d/subdivide {:iterations 2}))
proj (s3d/orbit (s3d/orthographic {:scale 65 :origin [200 200]})
[0 0 0]
{:radius 5 :yaw 0.4 :pitch -0.3})]
{:image/size [400 400]
:image/background [:color/rgb 245 243 238]
:image/nodes
[(s3d/render-mesh proj mesh
{:style {:style/fill [:color/rgb 100 160 200]
:style/stroke {:color [:color/rgb 60 100 140] :width 0.3}}
:light {:light/direction [1 2 1]
:light/ambient 0.25
:light/intensity 0.75}})]}))
Geometric Panels
A dodecahedron with every face inset and extruded, creating faceted panel geometry.
3dnoisecolor
View source
(defn geometric-panels []
(let [mesh (-> (s3d/platonic-mesh :dodecahedron 1.8)
(s3d/inset-faces {:select/type :all :inset/amount 0.2})
(s3d/extrude-faces {:select/type :field
:select/field (field/noise-field :scale 3.0 :seed 55)
:select/threshold -0.2
:extrude/amount -0.15})
(s3d/color-mesh {:color/type :field
:color/field (field/noise-field :scale 1.0 :seed 55)
:color/palette [[:color/rgb 200 180 60]
[:color/rgb 220 100 40]
[:color/rgb 180 60 80]]}))
proj (s3d/orbit (s3d/orthographic {:scale 55 :origin [200 200]})
[0 0 0]
{:radius 5 :yaw 0.7 :pitch -0.4})]
{:image/size [400 400]
:image/background [:color/rgb 35 30 40]
:image/nodes
[(s3d/render-mesh proj mesh
{:light {:light/direction [1 2 0.5]
:light/ambient 0.2
:light/intensity 0.8}})]}))
Glossy Torus
A rotating torus with specular highlights catching the light as it turns.
3danimation
View source
(defn glossy-torus []
{:frames (anim/frames 60
(fn [t]
(let [proj (s3d/orbit (s3d/orthographic {:scale 55 :origin [200 200]})
[0 0 0]
{:radius 5
:yaw (* t 2.0 Math/PI)
:pitch -0.35})
mesh (-> (s3d/torus-mesh 1.5 0.6 {:ring-segments 24 :tube-segments 12})
(s3d/rotate-mesh :x 0.3))]
{:image/size [400 400]
:image/background [:color/rgb 15 15 22]
:image/nodes
[(s3d/render-mesh proj mesh
{:style {:style/fill [:color/rgb 160 80 200]
:material (material/phong
:specular 0.5
:shininess 48.0)
:style/stroke {:color [:color/rgb 100 40 140]
:width 0.3}}
:light {:light/direction [1 2 0.5]
:light/ambient 0.15
:light/intensity 0.85}
:cull-back false})]})))
:fps 30})
Hatched Sphere
Non-photorealistic rendering — 3D sphere with cross-hatch fill whose density varies by lighting.
3dhatching
View source
(defn hatched-sphere []
(let [mesh (s3d/sphere-mesh 1.5 {:segments 12 :rings 8})
proj (s3d/orbit (s3d/orthographic {:scale 60 :origin [200 200]})
[0 0 0]
{:radius 5 :yaw 0.4 :pitch -0.3})]
{:image/size [400 400]
:image/background [:color/rgb 255 250 240]
:image/nodes
[(s3d/render-mesh proj mesh
{:style {:render/mode :hatch
:style/fill [:color/rgb 255 250 235]
:hatch/angle 30
:hatch/spacing 3
:hatch/color [:color/rgb 40 30 20]
:hatch/stroke-width 0.5}
:light {:light/direction [1 2 1]
:light/ambient 0.2 :light/intensity 0.8}
:cull-back false})]}))
Isometric City
An 8x8 grid of buildings with randomized heights and hue-shifted colors.
3dcolor
View source
(defn 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.
3dcolor
View source
(defn 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]
{:size 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]
{:radius 0.9 :height 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]
{:radius 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})]}))
L-System Tree
Branching organic structure from L-system rules swept into 3D mesh tubes.
3dl-systemgradients
View source
(defn lsystem-tree []
(let [mesh (-> (s3d/lsystem-mesh {:axiom "F"
:rules {"F" "FF[&+F][&-F][&^F]"}
:iterations 4
:angle 28
:length 0.1
:profile [[0.03 0] [0.021 0.021] [0 0.03] [-0.021 0.021]
[-0.03 0] [-0.021 -0.021] [0 -0.03] [0.021 -0.021]]
:segments 3})
(s3d/color-mesh {:color/type :axis-gradient
:color/axis :y
:color/palette [[:color/rgb 100 65 30]
[:color/rgb 70 110 40]
[:color/rgb 45 130 50]]}))
proj (s3d/look-at (s3d/orthographic {:scale 80 :origin [200 320]})
[2 2 3] [0 1.2 0])]
{:image/size [400 400]
:image/background [:color/rgb 230 235 225]
:image/nodes
[(s3d/render-mesh proj mesh
{:light {:light/direction [1 2 0.5]
:light/ambient 0.25
:light/intensity 0.75}})]}))
Material Showcase
Four primitives with different Blinn-Phong material properties — matte, satin, glossy, and mirror-like.
3dcolor
View source
(defn material-showcase []
(let [proj (s3d/isometric {:scale 30 :origin [300 220]})
light {:light/direction [1 1.5 0.8]
:light/ambient 0.15
:light/intensity 0.85}]
{:image/size [600 400]
:image/background [:color/rgb 30 32 38]
:image/nodes
[(s3d/render-mesh proj
(s3d/cube-mesh [-5 0 -1] 2.5)
{:style {:style/fill [:color/rgb 200 80 60]
:material (material/phong :specular 0.0 :shininess 1.0)}
:light light})
(s3d/render-mesh proj
(s3d/sphere-mesh 1.4 {:segments 16 :rings 10})
{:style {:style/fill [:color/rgb 60 160 120]
:material (material/phong :specular 0.3 :shininess 16.0)}
:light light})
(s3d/render-mesh proj
(-> (s3d/cylinder-mesh 1.0 2.5 {:segments 16})
(s3d/translate-mesh [4 0 -1]))
{:style {:style/fill [:color/rgb 80 120 200]
:material (material/phong :specular 0.6 :shininess 64.0)}
:light light})
(s3d/render-mesh proj
(-> (s3d/torus-mesh 1.2 0.5 {:ring-segments 20 :tube-segments 10})
(s3d/translate-mesh [0 0 4])
(s3d/rotate-mesh :x 0.5))
{:style {:style/fill [:color/rgb 200 180 60]
:material (material/phong :specular 0.9 :shininess 256.0)}
:light light})]}))
Mirrored Sculpture
A noise-deformed shape mirrored and merged for bilateral symmetry.
3dnoisesymmetrygradients
View source
(defn mirrored-sculpture []
(let [mesh (-> (s3d/platonic-mesh :octahedron 1.2)
(s3d/deform-mesh {:deform/type :displace
:deform/field (field/noise-field :scale 2.0 :variant :fbm :seed 13)
:deform/amplitude 0.4})
(s3d/deform-mesh {:deform/type :taper
:deform/axis :y
:deform/amount 0.3})
(s3d/mirror-mesh {:mirror/axis :x :mirror/merge true})
(s3d/color-mesh {:color/type :axis-gradient
:color/axis :y
:color/palette [[:color/rgb 60 40 80]
[:color/rgb 180 120 160]
[:color/rgb 220 200 180]]}))
proj (s3d/orbit (s3d/orthographic {:scale 60 :origin [200 200]})
[0 0 0]
{:radius 5 :yaw 0.5 :pitch -0.3})]
{:image/size [400 400]
:image/background [:color/rgb 25 22 30]
:image/nodes
[(s3d/render-mesh proj mesh
{:light {:light/direction [1 2 0.5]
:light/ambient 0.2
:light/intensity 0.8}})]}))
New Primitives
Cone, torus, and sphere combined with merge-meshes and perspective projection.
3dcolor
View source
(defn 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 {:segments 48})
{:style/fill [:color/rgb 220 160 60]}]
[(-> (s3d/torus-mesh 1.2 0.3 {:ring-segments 48 :tube-segments 24})
(s3d/translate-mesh [0 0.3 0]))
{:style/fill [:color/rgb 70 130 200]}]
[(-> (s3d/sphere-mesh 0.5 {:segments 32 :rings 16})
(s3d/translate-mesh [-2.0 0.5 0.5]))
{:style/fill [:color/rgb 200 80 80]}])
{:light light})]}))
Organic Sculpture
A cube deformed with noise, subdivided, and colored by field — the full sculpting pipeline.
3dnoisedistortioncolor
View source
(defn organic-sculpture []
(let [mesh (-> (s3d/cube-mesh [-1 -1 -1] 2)
(s3d/subdivide {:iterations 2})
(s3d/deform-mesh {:deform/type :displace
:deform/field (field/noise-field :scale 1.2 :variant :fbm :seed 7)
:deform/amplitude 0.45})
(s3d/deform-mesh {:deform/type :twist
:deform/axis :y
:deform/amount 0.6})
(s3d/color-mesh {:color/type :field
:color/field (field/noise-field :scale 2.0 :variant :fbm :seed 7)
:color/palette [[:color/rgb 180 100 60]
[:color/rgb 220 170 100]
[:color/rgb 80 50 40]]}))
proj (s3d/orbit (s3d/orthographic {:scale 70 :origin [200 200]})
[0 0 0]
{:radius 5 :yaw 0.6 :pitch -0.35})]
{:image/size [400 400]
:image/background [:color/rgb 30 28 35]
:image/nodes
[(s3d/render-mesh proj mesh
{:light {:light/direction [1 2 0.5]
:light/ambient 0.25
:light/intensity 0.75}})]}))
Procedural Texture
The full 2D→3D bridge: UV-projected noise texture with bump map and specular variation.
3dnoisesubdivisioncolor
View source
(defn procedural-textured-sphere []
(let [mesh (-> (s3d/platonic-mesh :icosahedron 1.5)
(s3d/subdivide {:iterations 2})
(s3d/uv-project {:uv/type :spherical})
(s3d/paint-mesh {:color/source :uv
:color/type :field
:color/field (field/noise-field :scale 4.0 :variant :fbm :seed 7)
:color/palette [[:color/rgb 40 30 20]
[:color/rgb 160 120 60]
[:color/rgb 200 180 140]]})
(s3d/normal-map-mesh {:normal-map/field (field/noise-field :scale 8.0 :variant :turbulence :seed 7)
:normal-map/strength 0.4})
(s3d/specular-map-mesh {:specular-map/field (field/noise-field :scale 6.0 :seed 7)
:specular-map/range [0.1 0.7]}))
proj (s3d/orbit (s3d/orthographic {:scale 65 :origin [200 200]})
[0 0 0]
{:radius 5 :yaw 0.5 :pitch -0.3})]
{:image/size [400 400]
:image/background [:color/rgb 25 22 30]
:image/nodes
[(s3d/render-mesh proj mesh
{:style {:material {:material/type :material/phong
:material/ambient 0.1
:material/diffuse 0.8
:material/specular 0.5
:material/shininess 48.0}}
:light {:light/direction [1 2 0.5]
:light/ambient 0.15
:light/intensity 0.85}
:shading :smooth})]}))
Rotating Cube
A cube tumbling in space with directional lighting.
3danimation
View source
(defn 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.
3danimation
View source
(defn 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 {:ring-segments 24 :tube-segments 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})
Scatter Forest
2D Poisson scatter distribution placing 3D tree meshes — bridging scatter to mesh instancing.
3dscatter
View source
(defn scatter-forest []
(let [;; Simple tree: cone on cylinder
tree (s3d/merge-meshes
[(s3d/cylinder-mesh 0.08 0.4 {:segments 6})
{:style/fill [:color/rgb 120 80 40]}]
[(-> (s3d/cone-mesh 0.25 0.5 {:segments 8})
(s3d/translate-mesh [0 0.4 0]))
{:style/fill [:color/rgb 50 120 40]}])
points (mapv (fn [[x y]] [(* 0.8 x) 0 (* 0.8 y)])
(scatter/poisson-disk [-3 -3 6 6] {:min-dist 1.2 :seed 42}))
forest (s3d/instance-mesh tree {:positions points
:rotate-y {:range [0 6.28] :seed 42}})
proj (s3d/look-at (s3d/orthographic {:scale 40 :origin [200 220]})
[4 3 5] [0 0.3 0])]
{:image/size [400 400]
:image/background [:color/rgb 180 200 220]
:image/nodes
[(s3d/render-mesh proj forest
{:light {:light/direction [1 2 0.5]
:light/ambient 0.3
:light/intensity 0.7}})]}))
Smooth Geodesic Sphere
Icosahedron subdivided twice with smooth shading — no facet lines visible.
3dsubdivision
View source
(defn smooth-geodesic []
(let [mesh (-> (s3d/platonic-mesh :icosahedron 1.5)
(s3d/subdivide {:iterations 2}))
proj (s3d/orbit (s3d/orthographic {:scale 65 :origin [200 200]})
[0 0 0]
{:radius 5 :yaw 0.4 :pitch -0.3})]
{:image/size [400 400]
:image/background [:color/rgb 245 243 238]
:image/nodes
[(s3d/render-mesh proj mesh
{:style {:style/fill [:color/rgb 100 160 200]}
:light {:light/direction [1 2 1]
:light/ambient 0.2
:light/intensity 0.8}
:shading :smooth})]}))
Specular Spheres
Three spheres with increasing specular highlights demonstrating Blinn-Phong materials.
3dcolor
View source
(defn specular-spheres []
(let [proj (s3d/perspective {:scale 80 :origin [300 200]
:yaw 0.2 :pitch -0.25 :distance 10})
light {:light/direction [1 2 1]
:light/ambient 0.12
:light/intensity 0.88}
mesh (s3d/sphere-mesh 1.2 {:segments 20 :rings 12})]
{:image/size [600 400]
:image/background [:color/rgb 18 20 28]
:image/nodes
[;; Matte sphere (no specular)
(s3d/render-mesh proj
(s3d/translate-mesh mesh [-3 0 0])
{:style {:style/fill [:color/rgb 180 60 60]
:material (material/phong
:specular 0.0 :shininess 1.0)}
:light light})
;; Medium specular
(s3d/render-mesh proj mesh
{:style {:style/fill [:color/rgb 60 120 180]
:material (material/phong
:specular 0.4 :shininess 32.0)}
:light light})
;; High specular (glossy)
(s3d/render-mesh proj
(s3d/translate-mesh mesh [3 0 0])
{:style {:style/fill [:color/rgb 200 180 60]
:material (material/phong
:specular 0.8 :shininess 128.0)}
:light light})]}))
Spotlight
A spot light with visible hotspot and falloff on a sphere and floor.
3dcolor
View source
(defn spotlight-scene []
(let [sphere (s3d/sphere-mesh 1.0 {:segments 20 :rings 12})
floor (s3d/cube-mesh [-3 -1.5 -3] 6)
proj (s3d/perspective {:scale 80 :origin [200 220]
:yaw 0.4 :pitch -0.35 :distance 8})]
{:image/size [400 400]
:image/background [:color/rgb 10 10 15]
:image/nodes
[(s3d/render-mesh proj
(s3d/scale-mesh floor [1.0 0.05 1.0])
{:style {:style/fill [:color/rgb 180 180 180]
:material (material/phong :ambient 0.02 :diffuse 0.8 :specular 0.1)}
:lights [(material/spot [0 8 0] [0 -1 0]
:color [:color/rgb 255 240 200]
:multiplier 2.0
:hotspot 20 :falloff 30
:decay :inverse :decay-start 3.0)
(material/hemisphere
[:color/rgb 20 25 40]
[:color/rgb 5 5 5]
:multiplier 0.1)]})
(s3d/render-mesh proj sphere
{:style {:style/fill [:color/rgb 200 60 60]
:material (material/phong :ambient 0.02 :diffuse 0.7
:specular 0.6 :shininess 64.0)}
:lights [(material/spot [0 8 0] [0 -1 0]
:color [:color/rgb 255 240 200]
:multiplier 2.0
:hotspot 20 :falloff 30
:decay :inverse :decay-start 3.0)
(material/hemisphere
[:color/rgb 20 25 40]
[:color/rgb 5 5 5]
:multiplier 0.1)]})]}))
Sweep Tube
A circular cross-section swept along a winding 3D path.
3dgradients
View source
(defn sweep-tube []
(let [;; Circular profile
n-prof 12
profile (mapv (fn [i]
(let [a (* (/ (* 2 Math/PI) n-prof) i)]
[(* 0.5 (Math/cos a)) (* 0.5 (Math/sin a))]))
(range n-prof))
mesh (-> (s3d/sweep-mesh
{:profile profile
:path [[0 0 0] [1 1.2 0.8] [2 0 1.5] [3 0.8 0]
[4 0 -1.0] [5 1.2 0]]
:segments 40})
(s3d/color-mesh {:color/type :axis-gradient
:color/axis :x
:color/palette [[:color/rgb 200 60 60]
[:color/rgb 60 60 200]
[:color/rgb 60 200 60]]}))
proj (s3d/look-at (s3d/orthographic {:scale 50 :origin [130 200]})
[3 2 4] [2.5 0.3 0])]
{:image/size [400 400]
:image/background [:color/rgb 30 28 35]
:image/nodes
[(s3d/render-mesh proj mesh
{:light {:light/direction [1 2 0.5]
:light/ambient 0.2
:light/intensity 0.8}
:shading :smooth})]}))
Torus
A golden torus with fine mesh detail and smooth Lambertian shading.
3d
View source
(defn torus []
(let [proj (s3d/isometric {:scale 55 :origin [200 200]})
mesh (-> (s3d/torus-mesh 1.8 0.7 {:ring-segments 32 :tube-segments 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})]}))
Twisted Vase
A revolved profile twisted and colored with a warm gradient.
3ddistortiongradients
View source
(defn twisted-vase []
(let [mesh (-> (s3d/revolve-mesh
{:profile [[0.0 0.0] [0.8 0.1] [0.9 0.4] [0.6 0.8]
[0.4 1.2] [0.5 1.6] [0.7 2.0] [0.6 2.2]
[0.3 2.4] [0.0 2.5]]
:segments 16})
(s3d/deform-mesh {:deform/type :twist
:deform/axis :y
:deform/amount 1.5})
(s3d/color-mesh {:color/type :axis-gradient
:color/axis :y
:color/palette [[:color/rgb 160 80 40]
[:color/rgb 210 160 100]
[:color/rgb 180 120 70]]}))
proj (s3d/look-at (s3d/orthographic {:scale 55 :origin [200 230]})
[3 2 4] [0 1.2 0])]
{:image/size [400 400]
:image/background [:color/rgb 240 235 225]
:image/nodes
[(s3d/render-mesh proj mesh
{:light {:light/direction [1 2 0.5]
:light/ambient 0.3
:light/intensity 0.7}
:cull-back false})]}))
Utah Teapot
Classic computer graphics test model loaded from OBJ, rendered with isometric projection.
3d
View source
(defn 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.
3danimation
View source
(defn 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)
{:radius 4
:yaw (* t 2.0 Math/PI)
:pitch -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}))
Vertex Painted Sphere
Per-vertex color with smooth interpolation — gradients flow across faces, not between them.
3dnoisegradientscolor
View source
(defn vertex-painted-sphere []
(let [mesh (-> (s3d/platonic-mesh :icosahedron 1.5)
(s3d/subdivide {:iterations 1})
(s3d/paint-mesh {:color/type :field
:color/field (field/noise-field :scale 2.5 :variant :fbm :seed 19)
:color/palette [[:color/rgb 30 60 180]
[:color/rgb 60 180 120]
[:color/rgb 220 200 50]
[:color/rgb 200 50 30]]}))
proj (s3d/orbit (s3d/orthographic {:scale 65 :origin [200 200]})
[0 0 0]
{:radius 5 :yaw 0.5 :pitch -0.3})]
{:image/size [400 400]
:image/background [:color/rgb 30 28 35]
:image/nodes
[(s3d/render-mesh proj mesh
{:light {:light/direction [1 2 0.5]
:light/ambient 0.2
:light/intensity 0.8}
:shading :smooth})]}))
Wireframe
A torus rendered as wireframe with deduplicated, depth-sorted edges.
3d
View source
(defn 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 {:ring-segments 20 :tube-segments 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.
3danimationcompositingopacity
View source
(defn wireframe-overlay []
{:frames (anim/frames 60
(fn [t]
(let [proj (s3d/orbit (s3d/orthographic {:scale 50 :origin [200 200]})
[0 0 0]
{:radius 5
:yaw (* t 2.0 Math/PI)
:pitch -0.3})
mesh (s3d/torus-mesh 1.5 0.6 {:ring-segments 20 :tube-segments 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.
3danimationparticlescolor
View source
(defn 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 {:segments 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.
3danimationcolorglow
View source
(defn 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]
{:major-radius 1.8 :minor-radius 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.
3danimationdashingcolor
View source
(defn 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 {:segments 24 :rings 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.
particles3danimation
View source
(defn 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
{:radius 0.25 :height 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.
particlesanimationglow
View source
(defn 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.
particlesanimationcolor
View source
(defn 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.
particlesanimation
View source
(defn 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.
particles3danimationcolor
View source
(defn 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]
{:radius 3.0 :height 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.
typographyanimationgradients
View source
(defn 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.
typographygradientsshadows
View source
(defn gradient-text-with-shadow []
{:image/size [600 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].
typographyglowfilters
View source
(defn 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.
typographycolor
View source
(defn 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.
typography3danimation
View source
(defn 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
{:depth 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}))Paint Engine

Acrylic Color Blending
Glossy acrylic paint mixing: saturated, smooth coverage.
paintacrylicsubtractivecolor-mixingglossy
View source
(defn acrylic-color-blend []
(let [w 600 h 250
ab {:brush/type :brush/dab
:brush/tip {:tip/shape :ellipse :tip/hardness 0.8}
:brush/paint {:paint/opacity 0.18 :paint/flow 0.95 :paint/spacing 0.02
:paint/blend :subtractive}}
pair (fn [x color-a color-b seed]
(vec (concat
(for [i (range 3)]
{:node/type :shape/path
:path/commands [[:move-to [(+ x (* i 4)) 30]] [:line-to [(+ x 85 (* i 4)) 220]]]
:paint/brush ab :paint/color color-a :paint/radius 22.0 :paint/seed (+ seed i)
:paint/pressure [[0.0 0.5] [0.5 1.0] [1.0 0.6]]})
(for [i (range 3)]
{:node/type :shape/path
:path/commands [[:move-to [(+ x 150 (* i 4)) 30]] [:line-to [(+ x 65 (* i 4)) 220]]]
:paint/brush ab :paint/color color-b :paint/radius 22.0 :paint/seed (+ seed 10 i)
:paint/pressure [[0.0 0.5] [0.5 1.0] [1.0 0.6]]}))))]
{:image/size [w h] :image/background [:color/rgb 240 238 232]
:image/nodes
[{:node/type :group :paint/surface {:paint/size [w h]}
:group/children
(vec (concat
(pair 30 [:color/rgb 210 30 25] [:color/rgb 25 55 210] 10)
(pair 220 [:color/rgb 25 55 210] [:color/rgb 240 220 30] 20)
(pair 410 [:color/rgb 210 30 25] [:color/rgb 240 220 30] 30)))}]}))
Blend Modes
Three blend modes: source-over (default), glazed (max), opaque (heavy coverage).
paintblendglazedopaquecomparison
View source
(defn blend-modes []
(let [w 600 h 300
mk (fn [y blend-mode]
(let [b {:brush/type :brush/dab
:brush/tip {:tip/shape :ellipse :tip/hardness 0.4}
:brush/paint {:paint/opacity 0.3 :paint/flow 0.8 :paint/spacing 0.04
:paint/blend blend-mode}}]
;; Two overlapping strokes to show how they interact
[{:paint/brush b :paint/color [:color/rgb 200 60 30] :paint/radius 16.0 :paint/seed 1
:paint/points (mapv (fn [i] [(+ 40 (* i 14)) (+ y (* 15 (Math/sin (* i 0.1)))) 0.7 0 0 0]) (range 40))}
{:paint/brush b :paint/color [:color/rgb 30 60 200] :paint/radius 16.0 :paint/seed 2
:paint/points (mapv (fn [i] [(+ 40 (* i 14)) (+ y (* -15 (Math/sin (* i 0.1)))) 0.7 0 0 0]) (range 40))}]))]
{:image/size [w h] :image/background [:color/rgb 242 237 225]
:image/nodes
[{:node/type :paint/surface :paint/size [w h]
:paint/strokes (vec (mapcat (fn [[y mode]] (mk y mode))
[[60 :source-over] [160 :glazed] [260 :opaque]]))}]}))
Botanical L-System
Branching plant structure via L-system, rendered with conte crayon.
paintlsystembotanicalgenerative
View source
(defn gen-botanical []
{:image/size [600 700] :image/background [:color/rgb 238 232 218]
:image/nodes
[{:node/type :group
:paint/surface {:paint/size [600 700] :substrate/tooth 0.25}
:group/children
[{:node/type :lsystem
:lsystem/axiom "F"
:lsystem/rules {"F" "FF+[+F-F-F]-[-F+F+F]"}
:lsystem/iterations 4
:lsystem/angle 22
:lsystem/length 6.0
:lsystem/origin [300 650]
:lsystem/heading -90
:paint/brush :conte
:paint/color [:color/rgb 45 35 25]
:paint/radius 2.5
:paint/pressure [[0.0 0.8] [0.5 0.6] [1.0 0.2]]}]}]})
Brush Sampler
All major brush families on one canvas: dry, ink, marker, paint, and tools.
paintpresetssampler
View source
(defn brush-sampler []
(let [w 700 h 600
mk (fn [y brush color label]
{:paint/brush brush :paint/color color :paint/seed (hash label)
:paint/points (mapv (fn [i] [(+ 40 (* i 30)) (+ y (* 8 (noise/perlin2d (* i 0.1) y {:seed (hash label)})))
(+ 0.3 (* 0.5 (Math/sin (* i 0.15)))) 0 0 0])
(range 21))})
rows [;; Dry media
[40 :pencil [:color/rgb 40 35 30]]
[80 :graphite [:color/rgb 35 30 25]]
[120 :charcoal [:color/rgb 25 20 15]]
[160 :chalk [:color/rgb 180 60 40]]
[200 :pastel [:color/rgb 60 100 160]]
[240 :conte [:color/rgb 80 50 30]]
;; Ink & pen
[300 :ink [:color/rgb 10 8 5]]
[340 :brush-pen [:color/rgb 10 8 5]]
[380 :felt-tip [:color/rgb 30 30 100]]
[420 :ballpoint [:color/rgb 20 20 60]]
;; Marker & paint
[480 :flat-marker [:color/rgb 220 180 50]]
[520 :watercolor [:color/rgb 40 100 170]]
[560 :oil [:color/rgb 180 50 30]]]]
{:image/size [w h] :image/background [:color/rgb 242 237 225]
:image/nodes
[{:node/type :paint/surface :paint/size [w h]
:paint/strokes (mapv (fn [[y brush color]] (mk y brush color (name brush))) rows)}]}))
Charcoal Flow Field
Flow field streamlines rendered with charcoal on textured paper.
paintflow-fieldcharcoalgenerative
View source
(defn gen-charcoal-flow []
{:image/size [600 600] :image/background [:color/rgb 230 222 208]
:image/nodes
[{:node/type :group
:paint/surface {:paint/size [600 600] :substrate/tooth 0.3}
:group/children
[{:node/type :flow-field
:flow/bounds [20 20 560 560]
:flow/opts {:density 25 :steps 50 :noise-scale 0.005 :step-length 2.5 :seed 31}
:paint/brush :charcoal
:paint/color [:color/rgb 30 25 20]
:paint/radius 3.0
:paint/pressure [[0.0 0.1] [0.2 0.6] [0.8 0.7] [1.0 0.05]]}]}]})
Delaunay Ink Web
Delaunay triangulation with technical pen — geometric precision meets handmade ink.
paintdelaunayinkgenerative
View source
(defn gen-delaunay-web []
(let [rng (prob/make-rng 88)
pts (mapv (fn [_] [(+ 40 (* 520 (.nextDouble rng))) (+ 40 (* 520 (.nextDouble rng)))]) (range 40))]
{:image/size [600 600] :image/background [:color/rgb 245 242 232]
:image/nodes
[{:node/type :group :paint/surface {:paint/size [600 600]}
:group/children
[{:node/type :delaunay
:delaunay/points pts :delaunay/bounds [0 0 600 600]
:paint/brush :technical-pen :paint/color [:color/rgb 20 18 15] :paint/radius 0.8}]}]}))
Flow Calligraphy
Sparse flow field with brush-pen dynamics — calligraphic marks guided by noise.
paintflow-fieldcalligraphygenerative
View source
(defn gen-flow-calligraphy []
{:image/size [600 600] :image/background [:color/rgb 242 237 225]
:image/nodes
[{:node/type :group :paint/surface {:paint/size [600 600]}
:group/children
[{:node/type :flow-field
:flow/bounds [40 40 520 520]
:flow/opts {:density 10 :steps 60 :noise-scale 0.004 :step-length 3.5 :seed 7}
:paint/brush {:brush/type :brush/dab
:brush/tip {:tip/shape :ellipse :tip/hardness 0.75 :tip/aspect 2.0}
:brush/paint {:paint/opacity 0.55 :paint/flow 0.9 :paint/spacing 0.025}
:brush/jitter {:jitter/angle 0.06}}
:paint/color [:color/rgb 12 10 6] :paint/radius 3.5
:paint/pressure [[0.0 0.05] [0.15 0.7] [0.5 1.0] [0.85 0.6] [1.0 0.03]]}]}]})
Gouache Color Blending
Matte gouache mixing: opaque, flat coverage with chalky blending zones.
paintgouachesubtractivecolor-mixingmatte
View source
(defn gouache-color-blend []
(let [w 600 h 250
gb {:brush/type :brush/dab
:brush/tip {:tip/shape :ellipse :tip/hardness 0.55}
:brush/paint {:paint/opacity 0.25 :paint/flow 0.85 :paint/spacing 0.03
:paint/blend :subtractive}
:brush/grain {:grain/type :canvas :grain/scale 0.06 :grain/contrast 0.35}
:brush/jitter {:jitter/position 0.03 :jitter/opacity 0.05}}
pair (fn [x color-a color-b seed]
(vec (concat
(for [i (range 3)]
{:node/type :shape/path
:path/commands [[:move-to [(+ x (* i 4)) 30]] [:line-to [(+ x 85 (* i 4)) 220]]]
:paint/brush gb :paint/color color-a :paint/radius 22.0 :paint/seed (+ seed i)
:paint/pressure [[0.0 0.5] [0.5 1.0] [1.0 0.6]]})
(for [i (range 3)]
{:node/type :shape/path
:path/commands [[:move-to [(+ x 150 (* i 4)) 30]] [:line-to [(+ x 65 (* i 4)) 220]]]
:paint/brush gb :paint/color color-b :paint/radius 22.0 :paint/seed (+ seed 10 i)
:paint/pressure [[0.0 0.5] [0.5 1.0] [1.0 0.6]]}))))]
{:image/size [w h] :image/background [:color/rgb 235 230 220]
:image/nodes
[{:node/type :group :paint/surface {:paint/size [w h]}
:group/children
(vec (concat
(pair 30 [:color/rgb 195 35 30] [:color/rgb 30 50 190] 10)
(pair 220 [:color/rgb 30 50 190] [:color/rgb 230 210 35] 20)
(pair 410 [:color/rgb 195 35 30] [:color/rgb 230 210 35] 30)))}]}))
Grain Types
Six grain types applied to identical strokes: fBm, turbulence, ridge, fiber, weave, canvas.
paintgraintexturecomparison
View source
(defn grain-types []
(let [w 600 h 400
types [:fbm :turbulence :ridge :fiber :weave :canvas]
mk (fn [y grain-type]
{:paint/brush {:brush/type :brush/dab
:brush/tip {:tip/shape :ellipse :tip/hardness 0.5}
:brush/paint {:paint/opacity 0.2 :paint/flow 0.8 :paint/spacing 0.05}
:brush/grain {:grain/type grain-type :grain/scale 0.1 :grain/contrast 0.5}}
:paint/color [:color/rgb 50 35 25] :paint/radius 14.0 :paint/seed (hash grain-type)
:paint/points (mapv (fn [i] [(+ 40 (* i 26)) y (+ 0.5 (* 0.4 (Math/sin (* i 0.15)))) 0 0 0])
(range 22))})]
{:image/size [w h] :image/background [:color/rgb 235 228 215]
:image/nodes
[{:node/type :paint/surface :paint/size [w h]
:paint/strokes (vec (map-indexed (fn [i t] (mk (+ 50 (* i 60)) t)) types))}]}))
Impasto Height
Thick paint with directional lighting from height planes. Top: flat. Bottom: impasto.
paintimpastoheightlighting
View source
(defn impasto-demo []
(let [w 600 h 250
flat {:brush/type :brush/dab
:brush/tip {:tip/shape :ellipse :tip/hardness 0.5 :tip/aspect 1.3}
:brush/paint {:paint/opacity 0.6 :paint/flow 0.9 :paint/spacing 0.06 :paint/blend :opaque}
:brush/jitter {:jitter/position 0.08 :jitter/opacity 0.12 :jitter/size 0.1}}
thick (assoc flat :brush/impasto {:impasto/height 0.6})
pts (fn [y seed] (mapv (fn [i] [(+ 40 (* i 20)) (+ y (* 8 (noise/perlin2d (* i 0.08) 0 {:seed seed})))
(+ 0.5 (* 0.4 (Math/sin (* i 0.12)))) 0 0 0])
(range 27)))]
{:image/size [w h] :image/background [:color/rgb 238 232 218]
:image/nodes
[{:node/type :paint/surface :paint/size [w h]
:paint/strokes
[{:paint/brush flat :paint/color [:color/rgb 180 60 30] :paint/radius 16.0 :paint/seed 1 :paint/points (pts 70 1)}
{:paint/brush thick :paint/color [:color/rgb 180 60 30] :paint/radius 16.0 :paint/seed 2 :paint/points (pts 180 2)}]}]}))
Ink Contour Map
Contour lines rendered with brush-pen — hand-drawn topographic map.
paintcontourinkgenerative
View source
(defn gen-ink-topo []
{:image/size [600 600] :image/background [:color/rgb 242 237 225]
:image/nodes
[{:node/type :group :paint/surface {:paint/size [600 600]}
:group/children
[{:node/type :contour
:contour/bounds [20 20 560 560]
:contour/opts {:thresholds (mapv #(- (* % 0.12) 0.7) (range 12))
:resolution 3.0 :noise-scale 0.008 :seed 42}
:paint/brush :brush-pen
:paint/color [:color/rgb 20 15 8]
:paint/radius 1.8
:paint/pressure [[0.0 0.2] [0.3 0.7] [0.7 0.8] [1.0 0.15]]}]}]})
Jitter Comparison
Same stroke with no jitter (top), light jitter (middle), heavy jitter (bottom).
paintjittertexturecomparison
View source
(defn jitter-comparison []
(let [w 600 h 300
base {:brush/type :brush/dab
:brush/tip {:tip/shape :ellipse :tip/hardness 0.5}
:brush/paint {:paint/opacity 0.15 :paint/flow 0.8 :paint/spacing 0.05}}
pts (fn [y seed] (mapv (fn [i] [(+ 40 (* i 26)) (+ y (* 10 (noise/perlin2d (* i 0.08) 0 {:seed seed})))
(+ 0.4 (* 0.5 (Math/sin (* i 0.12)))) 0 0 0])
(range 22)))]
{:image/size [w h] :image/background [:color/rgb 240 235 220]
:image/nodes
[{:node/type :paint/surface :paint/size [w h]
:paint/strokes
[{:paint/brush base
:paint/color [:color/rgb 60 40 30] :paint/radius 12.0 :paint/seed 1
:paint/points (pts 60 1)}
{:paint/brush (assoc base :brush/jitter {:jitter/position 0.1 :jitter/opacity 0.2 :jitter/size 0.08})
:paint/color [:color/rgb 60 40 30] :paint/radius 12.0 :paint/seed 2
:paint/points (pts 150 2)}
{:paint/brush (assoc base :brush/jitter {:jitter/position 0.25 :jitter/opacity 0.4 :jitter/size 0.2 :jitter/angle 0.3})
:paint/color [:color/rgb 60 40 30] :paint/radius 12.0 :paint/seed 3
:paint/points (pts 240 3)}]}]}))
Oil Bilateral Symmetry
Bilateral mirror with thick impasto oil paint — Rorschach meets color field.
paintsymmetryoilimpastogenerative
View source
(defn gen-oil-symmetry []
(let [ob {:brush/type :brush/dab
:brush/tip {:tip/shape :ellipse :tip/hardness 0.5 :tip/aspect 1.4}
:brush/paint {:paint/opacity 0.6 :paint/flow 0.85 :paint/spacing 0.06 :paint/blend :opaque}
:brush/impasto {:impasto/height 0.5}
:brush/smudge {:smudge/mode :smear :smudge/amount 0.35 :smudge/length 0.5}
:brush/jitter {:jitter/position 0.1 :jitter/opacity 0.15 :jitter/size 0.1 :jitter/angle 0.08}}]
{:image/size [600 600] :image/background [:color/rgb 235 228 215]
:image/nodes
[{:node/type :group :paint/surface {:paint/size [600 600]}
:group/children
[{:node/type :symmetry :symmetry/type :bilateral :symmetry/axis :vertical
:symmetry/center [300 300]
:group/children
[{:node/type :shape/path
:path/commands [[:move-to [310 80]] [:curve-to [380 180] [340 300] [400 420]]]
:paint/brush ob :paint/color [:color/rgb 180 50 35] :paint/radius 18.0 :paint/seed 1
:paint/pressure [[0.0 0.4] [0.3 1.0] [0.7 0.9] [1.0 0.3]]}
{:node/type :shape/path
:path/commands [[:move-to [320 120]] [:curve-to [360 200] [330 350] [370 480]]]
:paint/brush ob :paint/color [:color/rgb 40 80 150] :paint/radius 14.0 :paint/seed 2
:paint/pressure [[0.0 0.3] [0.4 0.9] [0.8 0.8] [1.0 0.2]]}
{:node/type :shape/path
:path/commands [[:move-to [340 160]] [:curve-to [400 260] [350 380] [380 520]]]
:paint/brush ob :paint/color [:color/rgb 200 170 50] :paint/radius 10.0 :paint/seed 3
:paint/pressure [[0.0 0.5] [0.5 1.0] [1.0 0.4]]}]}]}]}))
Oil Color Blending
Thick oil paint smudged together: red+blue, blue+yellow, red+yellow.
paintoilsubtractivecolor-mixingsmudge
View source
(defn oil-color-blend []
(let [w 600 h 250
ob {:brush/type :brush/dab
:brush/tip {:tip/shape :ellipse :tip/hardness 0.15 :tip/aspect 1.4}
:brush/paint {:paint/opacity 0.12 :paint/flow 0.8 :paint/spacing 0.02
:paint/blend :subtractive}
:brush/jitter {:jitter/position 0.12 :jitter/opacity 0.2
:jitter/size 0.15 :jitter/angle 0.1}}
pair (fn [x color-a color-b seed]
;; Multiple passes per color for richer coverage
(vec (concat
(for [i (range 3)]
{:node/type :shape/path
:path/commands [[:move-to [(+ x (* i 4)) 30]] [:line-to [(+ x 85 (* i 4)) 220]]]
:paint/brush ob :paint/color color-a :paint/radius 22.0 :paint/seed (+ seed i)
:paint/pressure [[0.0 0.5] [0.5 1.0] [1.0 0.6]]})
(for [i (range 3)]
{:node/type :shape/path
:path/commands [[:move-to [(+ x 150 (* i 4)) 30]] [:line-to [(+ x 65 (* i 4)) 220]]]
:paint/brush ob :paint/color color-b :paint/radius 22.0 :paint/seed (+ seed 10 i)
:paint/pressure [[0.0 0.5] [0.5 1.0] [1.0 0.6]]}))))]
{:image/size [w h] :image/background [:color/rgb 238 232 218]
:image/nodes
[{:node/type :group :paint/surface {:paint/size [w h]}
:group/children
(vec (concat
(pair 30 [:color/rgb 200 35 30] [:color/rgb 30 60 200] 10)
(pair 220 [:color/rgb 30 60 200] [:color/rgb 235 210 35] 20)
(pair 410 [:color/rgb 200 35 30] [:color/rgb 235 210 35] 30)))}]}))
Pastel Voronoi
Voronoi cell edges rendered with soft pastel on toned paper.
paintvoronoipastelgenerative
View source
(defn gen-pastel-voronoi []
(let [rng (prob/make-rng 42)
pts (mapv (fn [_] [(+ 30 (* 540 (.nextDouble rng))) (+ 30 (* 540 (.nextDouble rng)))]) (range 30))]
{:image/size [600 600] :image/background [:color/rgb 90 78 62]
:image/nodes
[{:node/type :group
:paint/surface {:paint/size [600 600] :substrate/tooth 0.35}
:group/children
[{:node/type :voronoi
:voronoi/points pts :voronoi/bounds [0 0 600 600]
:paint/brush :soft-pastel :paint/color [:color/rgb 245 230 200]
:paint/radius 7.0 :paint/pressure [[0.0 0.5] [0.5 1.0] [1.0 0.5]]}]}]}))
Spatter Modes
Two spatter modes: scatter (perpendicular) and spray (cone along stroke).
paintspatterspraycomparison
View source
(defn spatter-modes []
(let [w 600 h 250
mk (fn [y mode color seed]
(let [b {:brush/type :brush/dab
:brush/tip {:tip/shape :ellipse :tip/hardness 0.15}
:brush/paint {:paint/opacity 0.1 :paint/flow 0.7 :paint/spacing 0.035}
:brush/spatter {:spatter/threshold 0.3 :spatter/density 0.5 :spatter/spread 1.5
:spatter/size [0.05 0.15] :spatter/opacity [0.15 0.5]
:spatter/mode mode}
:brush/jitter {:jitter/position 0.06 :jitter/opacity 0.1}}]
{:paint/brush b :paint/color color :paint/radius 12.0 :paint/seed seed
:paint/points (mapv (fn [i] [(+ 60 (* i 24)) y (+ 0.4 (* 0.5 (Math/sin (* i 0.12)))) 0 0 0])
(range 22))}))]
{:image/size [w h] :image/background [:color/rgb 242 238 228]
:image/nodes
[{:node/type :paint/surface :paint/size [w h]
:paint/strokes
[(mk 80 :scatter [:color/rgb 200 40 30] 42)
(mk 170 :spray [:color/rgb 30 120 50] 99)]}]}))
Subtractive Pigment Mixing
Three primary paint colors crossing: blue+yellow=green, red+blue=purple, red+yellow=orange.
paintsubtractivecolor-mixingpigment
View source
(defn subtractive-mixing []
(let [w 500 h 400
sb {:brush/type :brush/dab
:brush/tip {:tip/shape :ellipse :tip/hardness 0.08}
:brush/paint {:paint/opacity 0.05 :paint/flow 0.45 :paint/spacing 0.025
:paint/blend :subtractive}
:brush/wet {:wet/enabled true :wet/deposit 0.2 :wet/diffusion 0.3
:wet/diffusion-steps 12 :wet/edge-darken 0.1 :wet/edge-sharpness 1.5}
:brush/jitter {:jitter/position 0.1 :jitter/opacity 0.15 :jitter/size 0.1}}]
{:image/size [w h] :image/background [:color/rgb 245 240 228]
:image/nodes
[{:node/type :group :paint/surface {:paint/size [w h]}
:group/children
[;; Blue horizontal band
{:node/type :shape/path
:path/commands [[:move-to [30 200]] [:line-to [470 200]]]
:paint/brush sb :paint/color [:color/rgb 40 90 210] :paint/radius 55.0
:paint/pressure [[0.0 0.5] [0.5 0.8] [1.0 0.5]]}
;; Yellow diagonal — crosses blue to make green
{:node/type :shape/path
:path/commands [[:move-to [120 20]] [:line-to [200 380]]]
:paint/brush sb :paint/color [:color/rgb 235 215 45] :paint/radius 50.0
:paint/pressure [[0.0 0.5] [0.5 0.8] [1.0 0.5]]}
;; Red diagonal — crosses blue to make purple
{:node/type :shape/path
:path/commands [[:move-to [380 20]] [:line-to [300 380]]]
:paint/brush sb :paint/color [:color/rgb 210 45 35] :paint/radius 50.0
:paint/pressure [[0.0 0.5] [0.5 0.8] [1.0 0.5]]}]}]}))
Watercolor Mandala
8-fold radial symmetry with watercolor washes — wet edges bloom outward.
paintsymmetrywatercolorgenerative
View source
(defn gen-watercolor-mandala []
(let [wb {:brush/type :brush/dab
:brush/tip {:tip/shape :ellipse :tip/hardness 0.25}
:brush/paint {:paint/opacity 0.05 :paint/flow 0.5 :paint/spacing 0.04}
:brush/wet {:wet/enabled true :wet/deposit 0.3 :wet/diffusion 0.25
:wet/diffusion-steps 6 :wet/edge-darken 0.3 :wet/edge-sharpness 2.0
:wet/granulation 0.3}
:brush/jitter {:jitter/position 0.1 :jitter/opacity 0.2 :jitter/size 0.1}}]
{:image/size [600 600] :image/background [:color/rgb 242 237 225]
:image/nodes
[{:node/type :group :paint/surface {:paint/size [600 600]}
:group/children
[{:node/type :symmetry :symmetry/type :radial :symmetry/n 8
:symmetry/center [300 300]
:group/children
[{:node/type :shape/path
:path/commands [[:move-to [300 300]] [:curve-to [320 200] [360 180] [380 120]]]
:paint/brush wb :paint/color [:color/hsl 200 0.5 0.55] :paint/radius 20.0
:paint/pressure [[0.0 0.8] [0.5 1.0] [1.0 0.3]]}
{:node/type :shape/path
:path/commands [[:move-to [310 280]] [:curve-to [340 220] [370 200] [350 140]]]
:paint/brush wb :paint/color [:color/hsl 30 0.45 0.55] :paint/radius 15.0
:paint/pressure [[0.0 0.6] [0.5 0.9] [1.0 0.2]]}]}]}]}))
Wet Media Granulation
Watercolor with increasing granulation: none, light, heavy. Shows salt-crystal texture.
paintwatercolorgranulationwet-mediacomparison
View source
(defn wet-granulation []
(let [w 600 h 300
mk (fn [y gran]
(let [b {:brush/type :brush/dab
:brush/tip {:tip/shape :ellipse :tip/hardness 0.25}
:brush/paint {:paint/opacity 0.07 :paint/flow 0.5 :paint/spacing 0.04}
:brush/wet {:wet/enabled true :wet/deposit 0.35 :wet/diffusion 0.25
:wet/diffusion-steps 6 :wet/edge-darken 0.35
:wet/edge-sharpness 2.0 :wet/granulation gran}
:brush/jitter {:jitter/position 0.12 :jitter/opacity 0.2 :jitter/size 0.1}}]
{:paint/brush b :paint/color [:color/hsl 200 0.5 0.5] :paint/radius 30.0 :paint/seed (hash gran)
:paint/points (mapv (fn [i] [(+ 50 (* i 25)) (+ y (* 10 (noise/perlin2d (* i 0.06) 0 {:seed (hash gran)})))
(+ 0.4 (* 0.5 (Math/sin (* i 0.1)))) 0 0 0])
(range 22))}))]
{:image/size [w h] :image/background [:color/rgb 242 237 225]
:image/nodes
[{:node/type :paint/surface :paint/size [w h]
:paint/strokes [(mk 60 0.0) (mk 155 0.3) (mk 250 0.7)]}]}))