Paint Engine

First Painted Stroke

The paint engine renders brushstrokes as dab sequences onto a raster surface — brushes are data, strokes are data. The simplest form is a :paint/surface node listing strokes; each is a brush, a color, a radius, and a series of [x y] points the dab follows:

{:node/type :paint/surface
 :paint/size [600 400]
 :paint/strokes
 [{:paint/brush  :chalk
   :paint/color  [:color/rgb 80 60 40]
   :paint/radius 12.0
   :paint/points [[50 100] [300 60] [550 100]]}]}
Rendered output

The dab is laid down along the points in order.

Brush Presets

Six presets cover the common media — name one as :paint/brush, or override the brush directly:

;; Use a preset
:paint/brush :chalk   ;; :pencil :ink :marker :watercolor :oil :chalk

;; Or override the brush — tip shape, hardness, flow, blend
:paint/brush {:brush/tip   {:tip/shape :ellipse :tip/hardness 0.9}
              :brush/paint {:paint/flow 0.7 :paint/blend :multiply}}

Tip shapes: :round, :ellipse, :chisel, :rect, :line. Blend modes: :normal, :multiply, :screen, :overlay, :darken, :lighten, :add.

Painting Paths

To paint along curves, wrap brush-painted :shape/path nodes in a group carrying :paint/surface. Each path's commands become a stroke, and all of them render onto the same surface — so later strokes lay over earlier ones:

{:node/type :group
 :paint/surface {:paint/size [800 400]}
 :group/children
 [{:node/type :shape/path
   :path/commands [[:move-to [20 250]]
                   [:curve-to [200 100] [400 350] [780 200]]]
   :paint/brush :watercolor
   :paint/color [:color/hsl 210 0.45 0.65]
   :paint/radius 45.0}
  ;; Ink detail on top
  {:node/type :shape/path
   :path/commands [[:move-to [120 220]]
                   [:curve-to [300 150] [500 280] [680 180]]]
   :paint/brush :ink
   :paint/color [:color/rgb 25 30 50]
   :paint/radius 2.5}]}
Rendered output

Give each painted path a :paint/brush, :paint/color, and :paint/radius — and no :style/fill or :style/stroke.