Repository navigation
Design brush family
Status: design. Nothing here is implemented yet except where noted.
| Brush | Reads | Writes | Deposits colour |
|---|---|---|---|
| Smudge | active layer | active layer | no |
| Blur | active layer | active layer | no |
| Smudge (all layers) | composite of visible layers | active layer | no |
| Blur (all layers) | composite of visible layers | active layer | no |
Reading is fixed per brush, not a setting — picking the tool picks the behaviour, as in Photoshop where Sample All Layers is a per-tool option that presets carry. Each of the four gets its own preset and its own stored settings, so tuning the sampled smudge never disturbs the isolated one.
The isolated pair is correct today. BrushEngine.StampSmudge and
StampBlur never read stroke.Color, so they are colourless by
construction, and the committed render samples the active layer's own
pixels. A live-preview bug where dragging with Smudge stamped flat
foreground-coloured dabs for the whole stroke — only snapping to the real
smear on pen-up — is fixed (MainViewModel.BeginStroke now gives smudge the
same real-pixel copy blur already had).
FrameRasterizer.Materialize renders one layer in isolation: baseline
PNG, then that layer's strokes, with targetPixels being that layer's own
bitmap. Layers are composited afterwards, in SceneRenderer.ComposeInto.
Nothing at rasterization time can see another layer.
So a sampled brush needs a backdrop — the composite of the other visible
layers — threaded from the caller into BrushEngine. Mechanically that is
an extra optional parameter on StampStroke, Materialize, Append and
AppendDraft, plus a backdrop argument on FrameBitmapCache.Get. The
mechanical part is easy. The consequence is not:
A layer's cached bitmap stops being a function of that layer alone. Edit a lower layer, and every sampled stroke above it must re-render. The cache key has to include the backdrop's identity, and invalidation has to cascade upward through the stack.
That is a genuine design fork, and it is Q6 below. It is the only thing blocking the sampled pair; everything else is plumbing.
Our BrushSettings currently carries: size, hardness, opacity, flow,
spacing, scatter, granulation, wet edge, tip id, angle, roundness,
smoothing, anti-aliasing, and the pressure curves. Photoshop's brush panel
is far larger, and the .abr importer necessarily drops what we cannot
represent.
Rather than chase the whole panel, here is the selection worth having — the
ones that change how a mark reads, that .abr and .kpp files actually
carry, and that our dab-stamping engine can honour without a rewrite.
Decided (Q7 = b): tier 1 plus Texture and Colour Dynamics. Grouping follows Photoshop's own (Q8 = a): Shape Dynamics, Scattering, Texture, Dual Brush, Transfer.
| Photoshop name | What it does | Why it earns its place |
|---|---|---|
| Size Jitter + Minimum Diameter | dab size varies per dab | The single biggest difference between a "digital" and a "natural" mark. We have scatter but not size variation. |
| Angle Jitter / Roundness Jitter | dab orientation and squash vary | Makes a custom tip read as bristles rather than a repeated stamp. We store angle and roundness but never vary them. |
| Shape Dynamics → Direction | angle follows the stroke direction | Required for any flat/chisel tip to behave like one. Cheap: we already compute segment direction for spacing. |
| Dual Brush | a second tip masks the first | This is what makes Photoshop's textured brushes look textured; it is also the single most common thing lost on .abr import. |
| Transfer → Flow Jitter | flow varies per dab | Gives the wash/build-up variation that currently only pressure can produce. |
All five are per-dab modulations of values we already have, and all five can
be seeded from dab position through Hash01, so they cost nothing in
determinism (invariant 2) and nothing in re-render divergence.
-
Texture (paper grain multiplied into the dab, with depth and scale) —
we have granulation, which is a fixed-frequency version of this; making
the tile and depth settable is most of the way there, and
PaperFieldalready supplies the tile. - Colour Dynamics (foreground/background jitter, hue/sat/brightness jitter) — natural-media colour variation. This is the one item in the agreed set that needs a model change: it wants a second colour on the stroke, since jittering toward the background colour is what makes it read as natural media rather than as noise.
Not in scope:
- Noise and Wet Edges as independent toggles — we have wet edge.
- Brush Pose (tilt, rotation from the stylus) — we read tilt from the pen already but do nothing with it.
- Airbrush/build-up timing, bristle qualities, and the full Mixer Brush
reservoir model. Each is a simulation rather than a parameter, and none
survives an
.abrround trip in a form we could honour faithfully.
The brush flyout already has General / Effects / Pressure / Presets pages. Photoshop groups by dynamic, not by parameter, and that grouping is what makes the panel legible: Shape Dynamics, Scattering, Texture, Dual Brush, Transfer. Adopting those names for the Effects page's sections would make imported presets recognisable to anyone coming from Photoshop, which is the point of aligning.
Added to .claude/quality/QUESTIONS.md as Q6–Q8.
- Getting started
- Documents and projects
- Tools and strokes
- Brushes
- Colour
- Layers, selections and guides
- The timeline
- Onion skin, references and the camera
- Symbols
- Saving and recovery
- Exporting to a game engine
- AI assistance
- Keyboard, performance and what is planned
- Bones and rigging
- Effects — fire, smoke and water
- A simplified 3D space to draw in: planes, parallax first, orbit second
- Brush performance beyond 4k: what the GPU can and cannot buy us
- The add-on boundary
- Making the AI trustworthy
- What an AI request actually costs
- Bones and spline deformers for 2D — design
- Brush tips — a generated library, not a live calculation
- Cloud and SaaS: what the architecture already fits, and what it does not
- Colour mixing — paint that picks up what it is laid on
- DESIGN: Effects
- DESIGN: Fluid effects — fire, smoke and water as drawn line and fill
- Fluid and viscous media — notes for the next brush-engine pass
- Compositing on the GPU
- Decomposing the two big files: which pieces come out, and with which tool
- One memory limit for pictures
- .NET 10: what is safe, what is unverified, and what it narrows
- One picture cache: the paused canvas on tiles
- The paint path: one mark, made once, under the pen
- Pen dynamics: tilt and speed
- A pencil is graphite on the paper's tooth
- Performance: the ratchet and the map
- Playback's first loop
- Pressure curves — a shape, not an exponent
- Importing Procreate brushes
- A project as a game's asset set
- One hierarchy: folders scope everything
- Puppet pins — design and plan
- The raster checkpoint: making a painting cheap to reopen
- Scoped resources — palettes, references and everything else shared
- Sensitive topics, and the three-track flow
- Design: pose-assisted frame-by-frame animation (rigid bones + AI gap filling)
- Stroke groups: why a merge should never turn strokes into pixels
- The project window: what you do between drawings
- Reading the subject before drawing on it
- The ceiling of a swept soft mark (B349)
- Q171 — a symbol owns a layer stack
- Pillar 3 — reusable assets, as live symbols
- What a template is
- Text, and where fonts sit
- The tiled compositor, and getting it off the CPU
- Design tokens
- Transform tool design (M14)
- Why a brush stamps twice, and when it does not
- Migrating to the designed UI
- Undo stores only what changed
- Vector tooling: making the lines you already drew editable
- The smudge/blur family, and aligning brush settings with Photoshop
Generated from the repository. Edit the files under docs/, not these pages.