Blend & Flow

How colors merge, how strokes bend in the wind, and how ink begins to move

Contents

  1. Three blend modes: Mix / Multiply / Darken
  2. Comparing blend effects across colors
  3. Why background color matters
  4. Path Rotation: twisting stroke direction
  5. Flow Effect: eight blend types
  6. Last Stroke Only: affect only the last stroke
1

Three blend modes: Mix / Multiply / Darken

Imagine overlapping two crayons of different colors. "Mix" blends them linearly (like mixing on a palette). "Multiply" multiplies the two colors—more overlap gets darker (like two tinted glasses). "Darken" takes the darker of the two per channel (like keeping the deepest shadow).

In encode.frag, keyBlendMode controls how each new stroke blends with existing color:

ModekeyBlendModeShader logicVisual
Mix0 mix(existing, new, alpha) Linear blend by alpha; more overlap = more saturated
Multiply1 existing * new Color multiply; darker and calmer with overlap
Darken2 min(existing, new) Per-channel minimum; keeps the darkest tone

Below: orange + blue crossing, with each of the three blend modes:

Mix — orange and blue cross
Mix (linear blend)
Multiply — orange and blue cross
Multiply
Darken — orange and blue cross
Darken

Key shader code

// encode.frag — keyBlendMode decides blend if (keyBlendMode == 0) { blended = mix(existing, newColor, alpha); } else if (keyBlendMode == 1) { blended = existing * newColor; } else { blended = min(existing, newColor); }
2

Comparing blend effects across colors

The same blend mode with different color pairs gives very different results. Below: three color pairs.

Orange + Blue

Orange Blue Mix
Mix
Orange Blue Multiply
Multiply
Orange Blue Darken
Darken

Red + Teal

Red Teal Mix
Mix
Red Teal Multiply
Multiply
Red Teal Darken
Darken

Gray + Gray (reference)

Gray Gray Mix
Mix
Gray Gray Multiply
Multiply
Gray Gray Darken
Darken

What to notice

  • Complementary colors (orange/blue, red/teal) in Multiply get very dark where they overlap—RGB values multiply to small numbers.
  • Darken keeps the minimum per channel; overlap often shows a third hue.
  • Same color (gray/gray) shows how the three modes affect density without color distraction.
3

Why background color matters

Imagine placing tinted film on white paper—you see colored light. On black paper, you see almost nothing. Blend mode behavior depends entirely on the "base" color.

In encode.frag, isWhiteBase controls this:

isWhiteBase

The shader checks whether the current pixel base is "close to white." If so, it uses additive-style blending (for white backgrounds); otherwise it uses keyBlendMode. That’s why Multiply and Darken look similar on pure white or black—white base uses the additive path.

// encode.frag bool isWhiteBase = hasWhiteTag || isHighLuminanceGray || isVeryBright;

Best practice

To see clear blend-mode differences, use mid-tone backgrounds, e.g. beige [222, 212, 195], warm gray [180, 160, 140], or cool gray [150, 160, 170]. Avoid pure white or black.

4

Path Rotation: twisting stroke direction

A flat brush without rotation (Mode 1) follows your hand. Slight rotation (Mode 2) is like natural wrist turn. Strong rotation (Mode 3) is like a sharp flick—strokes twist wildly.

pathRotation controls how much noise perturbs stroke direction along the path:

ModepathRotationEffect
Mode 10No rotation; particles follow path
Mode 25–10Light perturbation, natural writing feel
Mode 310–25Strong twist; edges spread, wilder shape
Path Rotation Mode 1
Mode 1 (pathRotation = 0)
Path Rotation Mode 2
Mode 2 (pathRotation = 7)
Path Rotation Mode 3
Mode 3 (pathRotation = 17)

How it works

In the draw loop, each particle’s direction gets a Perlin-noise offset. pathRotation is the strength of that offset.

let noiseAngle = noise(x * scale, y * scale) * pathRotation; particleDir += noiseAngle;
5

Flow Effect: eight blend types

Flow is InkField’s strongest post effect (see Effects). Below, each blendType on the same strokes:

Flow Type 0
0: Basic
Flow Type 2
2: Concentric
Flow Type 3
3: Vertical
Flow Type 4
4: Horizontal
Flow Type 5
5: Crack Pattern
Flow Type 6
6: Mosaic
Flow Type 7
7: Vortex
Flow Type 8
8: Cellular

Per-type behavior

blendTypeNameDisplacement
0BasicSimplex noise + globalStyle strength
2ConcentricTwo random centers create radial ripples; uses mix() to override base noise—ripples dominate near centers, organic noise preserved at edges
3/4Vertical/Horizontalsin/cos/tan layers; directional texture
5Crack PatternDual-layer Voronoi/cellular noise; strong displacement at cell boundaries creates crack/fracture texture, base noise preserved inside cells
6MosaicCanvas split into random-sized tiles, each with independent random offset—like broken tiles shifting apart, sharp grid boundaries
7VortexTwo vortex centers with polar rotation from original coords, Gaussian falloff—vortex dominates near centers, transitions to noise farther out
8CellularVoronoi + Simplex; tissue-like
6

Last Stroke Only: affect only the last stroke

With ten strokes on paper, "Last Stroke Only" is like a sheet that covers the first nine—only the last stroke is moved by Flow. Off: all strokes are affected.
Last Stroke Only OFF
OFF — all strokes affected
Last Stroke Only ON
ON — only last stroke

Implementation

When flowEffectLastStrokeOnly is true, flow.frag uses flowEffectStrokeBounds so only pixels inside the last stroke’s bounds are displaced.

// flow.frag if (lastStrokeOnly) { bool inBounds = all(greaterThan(coord, bounds.xy)) && all(lessThan(coord, bounds.zw)); if (!inBounds) displace = vec2(0.0); }

When to use

  • OFF: Full-frame effect—apply Flow once after all strokes.
  • ON: Per-stroke control—each stroke gets its own flow while earlier strokes stay intact.

← Previous: Effects Workshop   |   Next: Recording & Playback →

InkField Tutorial Series — Understanding digital ink, explained simply