Brush Tool
Draw vectors by hand. Pressure, tilt, smoothing — all built in.
Freehand drawing that outputs clean vector shapes. The brush sweeps a nib shape along your stroke path, producing a filled polygon outline backed by a full vector network. Pressure, tilt, calligraphic angle -- all captured. Change any parameter after drawing and the stroke regenerates from the original hand movement.
This is how Flash's brush worked -- but parametric.
Parametric Strokes
Traditional brush tools bake their output. Draw a stroke, and the raw input is gone. Formo stores the source of truth: raw points with pressure and tilt at stride 4 in a Float32Array, plus the brush configuration that produced the outline.
Source of truth Derived (cached)-------------- ----------------rawPoints + brushConfig -> VN outline (nib sweep + curve fit)
Change brush size from 20 to 40 after drawing. Change the nib from oval to rectangle. Crank smoothing from 10 to 80. The stroke regenerates from raw data every time. The original hand movement is never lost.
Nib Shape Sweep
The brush outline is the Minkowski sum envelope of the centerline path and the nib shape. At each sample point, the nib's support function computes the farthest boundary point in the path-normal direction -- O(1) per sample, no iteration.
| Parameter | Range | Effect |
|---|---|---|
| Shape | Oval / Rectangle | Nib geometry |
| Size | 1--200 px | Maximum nib diameter |
| Min Size | 1--100% of Size | Pressure floor |
| Angle | 0--360 deg | Nib rotation |
| Flatness | 0--100% | Compression (100% = circle, 0% = line) |
| Smoothing | 0--100 | Curve simplification passes |
6 parameters. Infinite calligraphic variation.
An angled elliptical nib produces thick strokes in one direction and thin strokes perpendicular -- exactly like a real calligraphy pen. Artists get this effect by drawing naturally, without touching a single bezier handle.
title: "Calligraphic brush stroke"
description: "An angled elliptical nib sweeps along the path -- thick and thin variation emerges from nib geometry"
nodes:
- type: BRUSH, name: "Stroke", x: 200, y: 200, w: 350, h: 180, fill: "#2C3E50"
config:
brushConfig: { nibShape: "oval", size: 24, minSize: 0.3, angle: 45, flatness: 0.35, smoothing: 50 }
tools_visible: [select, brush]
ui_visible: [properties]Pressure and Tilt
With a stylus, the nib scales between minSize and full size based on pen pressure. Light touch gives a thin line. Press hard for full width. The nib shape and angle stay constant -- only scale changes, preserving the calligraphic character.
Tilt sensitivity rotates the virtual nib as you tilt your hand. The same stroke drawn at different angles produces different weight distributions.
Raw input is stored at stride 4: x, y, pressure, tilt. 500 points = 8 KB. Compact enough that every stroke retains its full input history.
Schneider Curve Fitting
Raw sample points become smooth bezier curves through Schneider's algorithm:
- Parameterize points by chord length
- Fit a single bezier to the entire sequence
- If max error exceeds tolerance, split at the worst point and recurse
- Store fitted curves as vector network segments with tangents
Tolerance is inversely proportional to the smoothing parameter. Low smoothing = tight fit with more segments. High smoothing = loose fit with fewer segments. The result is a proper vector network -- editable with all vertex tools immediately after drawing.
BrushCanvas and Paint Modes
A BrushCanvas is a container node holding multiple strokes with paint mode interactions. 5 paint modes control how new strokes composite with existing content:
| Mode | Behavior |
|---|---|
| Paint Normal | New stroke renders on top |
| Paint Behind | Only fills blank areas |
| Paint Fills | Affects fills, preserves strokes |
| Paint Selection | Only affects selected regions |
| Paint Inside | Clips new stroke to the clicked fill boundary |
Double-click a BrushCanvas to enter it and add strokes. Escape to exit. Each stroke within the canvas can have its own brush config and color -- select a stroke, change its nib shape, watch it regenerate.
title: "Layered brush canvas"
description: "Multiple brush strokes composited with paint modes -- add and subtract on the same canvas"
nodes:
- type: BRUSH_CANVAS, name: "Canvas", x: 200, y: 200, w: 300, h: 250
strokes:
- config: { nibShape: "oval", size: 40, flatness: 0.8 }, color: "#3498DB", paintMode: "normal"
- config: { nibShape: "oval", size: 20, flatness: 1.0 }, color: "#E74C3C", paintMode: "normal"
- config: { nibShape: "rect", size: 30, flatness: 0.5 }, paintMode: "erase"
tools_visible: [select, brush, eraser]
ui_visible: [properties]Eraser as Subtract
The eraser is a brush stroke with paintMode: 'erase'. It stores raw points and nib config like any other stroke. Undo removes it. Change its size after erasing and the subtraction updates. Non-destructive all the way down.
Two Node Types
| Standalone BRUSH | BRUSH_CANVAS | |
|---|---|---|
| Use case | Quick one-off strokes | Multi-stroke compositions |
| Paint modes | No | Yes -- 5 modes |
| Node tree | Individual node, reorderable | Container with stroke array |
| Create | Alt+drag in brush mode | Default brush drawing |
Both store raw points. Both regenerate from config changes. Convert either to a plain VECTOR node to edit the outline directly in VectorEditMode -- a one-way expansion, like Illustrator's "Expand Appearance."
Deep dive: Vector networks -- the graph topology behind every brush outline
Parametric shapes -- the same regeneration model powers stars, ellipses, and polygons
Flash, Modernized
Flash's brush tool output filled polygons from freehand strokes. It was the fastest way to draw vector art -- artists produced clean shapes without understanding bezier curves.
Flash discarded the raw input. We keep it. Flash offered 9 preset nib shapes. We expose 6 continuous parameters. Flash's output was opaque filled shapes. Ours is a full vector network.
| Flash | Formo | |
|---|---|---|
| Output | Filled polygons | Vector network |
| Raw data retained | No | Yes -- stride 4 Float32Array |
| Nib shapes | 9 presets | 6 continuous parameters |
| Post-draw editing | None | Full parametric regeneration |
| Pressure support | Basic | Pressure + tilt + min size floor |
| Paint modes | 5 modes | 5 modes (same model) |
| Eraser | Separate tool | Brush in subtract mode |
Same artistic workflow. Better data model.
Join the Beta
Freehand vector drawing with parametric strokes, pressure sensitivity, and non-destructive editing. The brush tool Flash should have had.