Editor has no 'randomize node positions slightly' / 'organic feel' tool — user wanting a hand-drawn look has to manually nudge each node, no automated jitter operation #287

Closed
opened 2026-10-04 03:43:38 +00:00 by Mark · 1 comment
Owner

Finding

grep -rn 'jitter\|organic\|wobble' src/components/editor/ returns 0. The editor has no randomize/jitter operation.

A user with a perfectly smooth circle:

  • Wants it to look "hand-drawn" / "sketchy"
  • Has to manually nudge each node by a few pixels
  • 50+ nodes = lots of manual work

The fix is a "Jitter" / "Organic" button that adds a small random offset to each node.

Where

  • Editor's path operations: src/components/editor/EditorRightPanel.tsx (per-row actions)
  • No jitter: grep -rn 'jitter' src/components/editor/ returns 0
  • Geometry module: src/components/editor/geometry.ts (where the algorithm would live)

Suggested fix shape

  1. Algorithm: add to geometry.ts:
export function jitterNodes(nodes: PathNode[], magnitude: number, seed?: number): PathNode[] {
  const rng = seed !== undefined ? seededRandom(seed) : Math.random;
  return nodes.map((n) => ({
    ...n,
    x: n.x + (rng() - 0.5) * magnitude * 2,
    y: n.y + (rng() - 0.5) * magnitude * 2,
  }));
}
  1. UI: a "Jitter" button in the right panel's Path Properties section:
<button
  onClick={onJitter}
  data-testid="jitter-nodes"
  title="Add small random offsets to each node (organic feel)"
>
  <Sparkles size={14} /> Jitter...
</button>
  1. Modal: a small modal asking for the magnitude (0.1-3.0 mm) and a seed (for reproducibility).

  2. History: single undoable action. The user can preview the jitter before applying.

  3. Use cases: hand-drawn aesthetics, "sketch" styles, hand-embroidered looks.

Severity

Low. The user can manually nudge. Power users with custom aesthetics notice.

Discovered by

Gap analysis (2026-10-02) of editor path operations.

  • The path operations family: split, merge, mirror, rotate, reverse, close, duplicate, simplify, offset, jitter.
  • #279 (open) — Simplify. The opposite direction; jitter adds complexity, simplify removes it.
  • The auto-digitize flow produces regular shapes; jitter gives them character.
  • Procreate/Photoshop have similar "Add noise" filters.
## Finding `grep -rn 'jitter\|organic\|wobble' src/components/editor/` returns 0. The editor has no randomize/jitter operation. A user with a perfectly smooth circle: - Wants it to look "hand-drawn" / "sketchy" - Has to manually nudge each node by a few pixels - 50+ nodes = lots of manual work The fix is a "Jitter" / "Organic" button that adds a small random offset to each node. ## Where - Editor's path operations: `src/components/editor/EditorRightPanel.tsx` (per-row actions) - No jitter: `grep -rn 'jitter' src/components/editor/` returns 0 - Geometry module: `src/components/editor/geometry.ts` (where the algorithm would live) ## Suggested fix shape 1. **Algorithm**: add to `geometry.ts`: ```ts export function jitterNodes(nodes: PathNode[], magnitude: number, seed?: number): PathNode[] { const rng = seed !== undefined ? seededRandom(seed) : Math.random; return nodes.map((n) => ({ ...n, x: n.x + (rng() - 0.5) * magnitude * 2, y: n.y + (rng() - 0.5) * magnitude * 2, })); } ``` 2. **UI**: a "Jitter" button in the right panel's Path Properties section: ```tsx <button onClick={onJitter} data-testid="jitter-nodes" title="Add small random offsets to each node (organic feel)" > <Sparkles size={14} /> Jitter... </button> ``` 3. **Modal**: a small modal asking for the magnitude (0.1-3.0 mm) and a seed (for reproducibility). 4. **History**: single undoable action. The user can preview the jitter before applying. 5. **Use cases**: hand-drawn aesthetics, "sketch" styles, hand-embroidered looks. ## Severity Low. The user can manually nudge. Power users with custom aesthetics notice. ## Discovered by Gap analysis (2026-10-02) of editor path operations. ## Related - The path operations family: split, merge, mirror, rotate, reverse, close, duplicate, simplify, offset, jitter. - #279 (open) — Simplify. The opposite direction; jitter adds complexity, simplify removes it. - The auto-digitize flow produces regular shapes; jitter gives them character. - Procreate/Photoshop have similar "Add noise" filters.
Author
Owner

Shipped fix(#287) at e68da5c on origin/main.

What landed:

  • geometry.jitterNodes(nodes, magnitude, seed?) — pure function with a private Mulberry32 PRNG so the optional seed makes the operation reproducible. Preserves ids, types, and bezier handles. Clamps negative magnitudes to 0.

  • EditorRightPanel Path Jitter subsection (sibling to Simplify / Offset / Boolean). Magnitude input (0-5mm, default 0.5mm) + optional seed input. Live sample-offset preview peeks at the first nodes offset under the users current (magnitude, seed) for visual feedback. Apply button fires onJitterSelectedPath(magnitude, seed?).

  • Editor.handleJitterSelectedPath — multi-select fan-out matching the simplify / offset pattern. Single pushHistory covers the batch. Selection kept (not cleared) since node ids survive the jitter. Defensive gates: no-op when no selection, magnitude <= 0, magnitude NaN, or path has <2 nodes.

Tests: 10 new geometry.test.ts cases, 16 new panel cases, 7 new Editor-integration cases — 33 new tests total, all passing. pnpm tsc -b and pnpm lint clean.

Commit: e68da5c fix(#287): jitter/organic-feel path operation in editor

Shipped fix(#287) at e68da5c on origin/main. What landed: - geometry.jitterNodes(nodes, magnitude, seed?) — pure function with a private Mulberry32 PRNG so the optional seed makes the operation reproducible. Preserves ids, types, and bezier handles. Clamps negative magnitudes to 0. - EditorRightPanel Path Jitter subsection (sibling to Simplify / Offset / Boolean). Magnitude input (0-5mm, default 0.5mm) + optional seed input. Live sample-offset preview peeks at the first nodes offset under the users current (magnitude, seed) for visual feedback. Apply button fires onJitterSelectedPath(magnitude, seed?). - Editor.handleJitterSelectedPath — multi-select fan-out matching the simplify / offset pattern. Single pushHistory covers the batch. Selection kept (not cleared) since node ids survive the jitter. Defensive gates: no-op when no selection, magnitude <= 0, magnitude NaN, or path has <2 nodes. Tests: 10 new geometry.test.ts cases, 16 new panel cases, 7 new Editor-integration cases — 33 new tests total, all passing. pnpm tsc -b and pnpm lint clean. Commit: e68da5c fix(#287): jitter/organic-feel path operation in editor
Mark closed this issue 2026-10-04 17:32:25 +00:00
Sign in to join this conversation.
No description provided.