Skip to content

Feat(canvas): add pressure dependent opacity for brush#9232

Open
DustyShoe wants to merge 22 commits into
invoke-ai:mainfrom
DustyShoe:Feat(Canvas)/pressure-dependent-opacity-for-brush
Open

Feat(canvas): add pressure dependent opacity for brush#9232
DustyShoe wants to merge 22 commits into
invoke-ai:mainfrom
DustyShoe:Feat(Canvas)/pressure-dependent-opacity-for-brush

Conversation

@DustyShoe

Copy link
Copy Markdown
Collaborator

Summary

This PR adds pressure-dependent brush opacity to the canvas and separates the old pressure sensitivity toggle into two independent settings: Pressure Affects Width and Pressure Affects Opacity.

2026-05-25.12-15-35.mp4

The main goal was to make opacity pressure usable with a hard brush. Previously, canvas pressure input only affected stroke width. This PR adds pressure-dependent brush opacity and implements a smoothed rasterized opacity path so the committed stroke stays visually continuous. This change switches the pressure-opacity brush path to a smoothed rasterized opacity workflow, keeps the committed stroke smooth, and preserves pressure width behavior as a separate option.

This PR also:

  • adds a settings migration from the legacy pressureSensitivity flag
  • updates the pressure-enabled brush and eraser paths to use the new settings, and lays the groundwork for pressure-controlled soft erasing in the future
  • replaces the old single pressure sensitivity setting with separate width and opacity options
  • adds focused frontend tests for settings migration and pressure stroke rendering behavior

Related Issues / Discussions

Discord discussion around pressure-dependent brush opacity, segment seams, and smoothing during stroke commit.

https://discord.com/channels/1020123559063990373/1049495067846524939/1504674583846850760
https://discord.com/channels/1020123559063990373/1020123559831539744/1508389863781568512

QA Instructions

  1. Open canvas settings and verify the two options are shown as:
    • Pressure Affects Width
    • Pressure Affects Opacity
  2. Enable only Pressure Affects Width and draw pen strokes. Verify width changes with pressure and opacity stays constant.
  3. Enable only Pressure Affects Opacity and draw pen strokes with light-to-heavy and heavy-to-light pressure. Verify the committed stroke opacity changes smoothly.
  4. Enable both options and verify width and opacity both respond to pen pressure.
  5. Draw long curved strokes and fast strokes, then confirm the committed result is smooth and does not show the obvious bead-like segment artifacts from the old approach.
  6. Reload the app and confirm the new pressure settings persist correctly.
  7. Verify legacy settings migration by loading existing persisted canvas settings and confirming width pressure remains enabled while opacity pressure stays disabled by default.

Merge Plan

Normal merge is fine.

Checklist

  • The PR has a short but descriptive title, suitable for a changelog
  • Tests added / updated (if applicable)
  • ❗Changes to a redux slice have a corresponding migration
  • Documentation added / updated (if applicable)
  • Updated What's New copy (if doing a release after this PR)

@github-actions github-actions Bot added the frontend PRs that change frontend files label May 25, 2026
@lstein lstein added the 6.14.0 label May 30, 2026
@lstein lstein moved this to 6.14.x Theme: USER EXPERIENCE in Invoke - Community Roadmap May 30, 2026
@DustyShoe
DustyShoe requested a review from JPPhoto as a code owner July 12, 2026 00:32
@dunkeroni

Copy link
Copy Markdown
Collaborator

I got a pen to test this with, and it works, but I noticed a weird pattern showing up when I loaded the canvas on another computer after testing:
image

Steps to reproduce: Enable "Pressure Affects Opacity" and draw with a pressure input on a new raster layer. Then refresh the page or connect to the invoke instance in a new window/computer. When the line is originally drawn, it looks correct. On refresh, it has artifacts wherever the edge pixels of individual line segments overlap each other.

The original line is fine and renders correctly during use. This is not (as far as I can tell) a rasterization issue since creating an image from bbox does not create the artifacts. That's the same flattening as layer merge and img2img operations use.

After reloading the page and seeing the artifacts, any flattening/copying operations will carry the artifacts to the new layer. However, if the layer is flattened to a new raster layer before refreshing, then the flattened copy does not include the artifacts. I suspect it has to do with how the layer is being saved and retrieved when it exists as a series of transparent konva line segments rather than raster data, and somehow the line continuity is getting lost in that pipeline.

image

@DustyShoe

Copy link
Copy Markdown
Collaborator Author

@dunkeroni

The issue is limited to reloading/rehydrating unflattened pressure-opacity strokes. Reconstructing them from the persisted segmented representation can introduce overlap artifacts after a refresh or in a new session.

I spent a significant amount of time investigating possible solutions. The approaches I explored mainly involved persisting a baked raster representation of pressure-opacity strokes and restoring that on reload instead of reconstructing them from their original transparent segment objects. However, every variation I tried introduced additional metadata and state complexity without providing a clean solution. From my current understanding of the architecture, a robust fix appears to require a dedicated baked persistence path for these strokes, which is beyond the scope of this PR.

For now, I propose treating reload fidelity for unflattened pressure-opacity strokes as a known limitation.

That said, my understanding of this part of the codebase may well be incomplete. If someone more familiar with the architecture has a cleaner or more robust approach, I would genuinely welcome the suggestion.

…-dependent-opacity-for-brush

# Conflicts:
#	invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasBrushToolModule.ts
@DustyShoe

Copy link
Copy Markdown
Collaborator Author

The actual culprit was the reload-only compositor introduced by ab1aab8. It attempted to distinguish local overlaps from stroke revisits, but loops were classified incorrectly and reconstructed as separate translucent shapes.
The new fix removes that compositor entirely. Live preview, commit, and reload now all use the same max-alpha rasterizer in pressure.ts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

6.14.0 frontend PRs that change frontend files

Projects

Status: 6.14.x Theme: USER EXPERIENCE

Development

Successfully merging this pull request may close these issues.

3 participants