1 nodes-scriptable-mask
Balazs Horvath edited this page 2026-04-18 10:52:20 +02:00
This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

ScriptableMask Node

Overview

The ScriptableMask node allows you to write Python scripts that generate masks. A mask is a grayscale image where each pixel value represents opacity or weight. In ComfyUI, masks are commonly used to:

  • Control where an effect is applied (e.g., only apply color correction to certain areas)
  • Blend between two images
  • Control the strength of an effect at different locations
  • Define regions for inpainting

Mask Format in ComfyUI

ComfyUI uses a specific mask format: a PyTorch tensor with shape [batch, height, width] or [height, width] where:

  • batch: Number of masks (optional, can be omitted for single mask)
  • height: Mask height in pixels
  • width: Mask width in pixels

Values are floating point numbers between 0.0 and 1.0, where:

  • 0.0 = fully transparent (effect not applied)
  • 1.0 = fully opaque (effect fully applied)
  • Values between 0 and 1 = partial opacity/weight

What is a Mask?

Think of a mask like a stencil or a transparency sheet:

  • Where the mask is white (value 1.0), the effect shows through completely
  • Where the mask is black (value 0.0), the effect is blocked
  • Gray areas (values between 0 and 1) partially apply the effect

Masks are grayscale because they only need to represent one value per pixel: the opacity or weight.

Inputs

Parameter Type Default Range Description
width INT 512 64-8192 Width of the mask in pixels
height INT 512 64-8192 Height of the mask in pixels
seed INT 0 0-4294967295 Random seed for reproducibility
enable_io_helpers BOOLEAN False - Enable file I/O helper functions
script STRING (see below) - Python script to execute

Outputs

  • mask: A tensor of shape [batch, height, width] or [height, width] with dtype float32, values in range [0, 1]

Default Script

# Scriptable Mask: set 'output_mask' (torch.Tensor [H,W] or [B,H,W] float 0-1).
# Injected: width, height, seed, torch, np.
# Example - white mask:
#   output_mask = torch.ones((height, width), dtype=torch.float32)
# Example - gradient mask:
#   output_mask = torch.linspace(0, 1, width).unsqueeze(0).repeat(height, 1)

output_mask = torch.ones((height, width), dtype=torch.float32)

How the Default Script Works

output_mask = torch.ones((height, width), dtype=torch.float32)
  • torch.ones(): Creates a tensor filled with ones
  • (height, width): The shape (no batch dimension)
  • dtype=torch.float32: Uses 32-bit floating point

This creates a completely white mask, meaning the effect will be applied everywhere at full strength.

Available Variables

Your script has access to:

  • width (int): The width parameter
  • height (int): The height parameter
  • seed (int): The seed parameter
  • torch: The PyTorch library
  • np: The NumPy library

If enable_io_helpers is True, you also get:

  • get_output_dir(): Returns ComfyUI's output directory path
  • get_input_dir(): Returns ComfyUI's input directory path
  • get_temp_dir(): Returns ComfyUI's temporary directory path

Example: Horizontal Gradient Mask

import torch

# Create a gradient from left (0) to right (1)
gradient = torch.linspace(0, 1, width)
output_mask = gradient.unsqueeze(0).repeat(height, 1)

Explanation:

  • torch.linspace(0, 1, width): Creates values from 0 to 1 across the width
  • .unsqueeze(0): Adds a dimension to make it [1, width]
  • .repeat(height, 1): Repeats the row height times to make [height, width]
  • Result: left side is black (0), right side is white (1), smooth gradient in between

Example: Vertical Gradient Mask

import torch

# Create a gradient from top (0) to bottom (1)
gradient = torch.linspace(0, 1, height)
output_mask = gradient.unsqueeze(1).repeat(1, width)

Explanation:

  • torch.linspace(0, 1, height): Creates values from 0 to 1 across the height
  • .unsqueeze(1): Adds a dimension to make it [height, 1]
  • .repeat(1, width): Repeats the column width times to make [height, width]
  • Result: top is black (0), bottom is white (1), smooth gradient in between

Example: Radial Gradient Mask

import torch

# Create coordinates from -1 to 1
y_coords = torch.linspace(-1, 1, height)
x_coords = torch.linspace(-1, 1, width)
Y, X = torch.meshgrid(y_coords, x_coords, indexing='ij')

# Calculate distance from center
distance = torch.sqrt(X**2 + Y**2)

# Create gradient (1 at center, 0 at edges)
gradient = 1 - distance
gradient = torch.clamp(gradient, 0, 1)  # Ensure values stay in [0, 1]

output_mask = gradient

Explanation:

  • Coordinates range from -1 to 1 in both directions
  • sqrt(X**2 + Y**2): Euclidean distance from center (0, 0)
  • 1 - distance: Inverts so center is 1, edges are ~0.41
  • torch.clamp(gradient, 0, 1): Ensures no negative values
  • Result: white center, fading to black at corners

Mathematical Background: The distance formula comes from the Pythagorean theorem:

d = sqrt(x² + y²)

For a unit square where x and y range from -1 to 1:

  • Center (0, 0): d = 0 → gradient = 1 (white)
  • Corner (1, 1): d = sqrt(2) ≈ 1.41 → gradient = -0.41 → clamped to 0 (black)
  • Edge (1, 0): d = 1 → gradient = 0 (black)

Example: Circular Mask

import torch

# Create coordinates from -1 to 1
y_coords = torch.linspace(-1, 1, height)
x_coords = torch.linspace(-1, 1, width)
Y, X = torch.meshgrid(y_coords, x_coords, indexing='ij')

# Calculate distance from center
distance = torch.sqrt(X**2 + Y**2)

# Create hard circle (1 inside radius, 0 outside)
radius = 0.7
circle = (distance < radius).float()

output_mask = circle

Explanation:

  • distance < radius: Creates a boolean mask (True inside circle, False outside)
  • .float(): Converts boolean to float (True → 1.0, False → 0.0)
  • Result: sharp circle with white inside, black outside

Example: Soft Circular Mask (Gaussian)

import torch

# Create coordinates from -1 to 1
y_coords = torch.linspace(-1, 1, height)
x_coords = torch.linspace(-1, 1, width)
Y, X = torch.meshgrid(y_coords, x_coords, indexing='ij')

# Calculate distance from center
distance = torch.sqrt(X**2 + Y**2)

# Create soft circle using Gaussian
radius = 0.7
softness = 0.2
gaussian = torch.exp(-distance**2 / (2 * softness**2))

output_mask = gaussian

Explanation:

  • Uses the Gaussian function: exp(-x² / (2σ²))
  • softness (σ) controls how soft the edge is
  • Result: smooth transition from white center to black edges

Mathematical Background: Gaussian Function

The Gaussian function is:

f(x) = exp(-x² / (2σ²))

Where:

  • x is the distance from center
  • σ (sigma) is the standard deviation, controlling edge softness

Key properties:

  • Maximum value is 1 at x=0 (center)
  • Approaches 0 as x increases
  • Larger σ = softer, more gradual transition
  • Smaller σ = sharper, more abrupt transition

Example: Checkerboard Mask

import torch

# Create checkerboard pattern
tile_size = 32
x_indices = torch.arange(width) // tile_size
y_indices = torch.arange(height) // tile_size
checkerboard = (x_indices.unsqueeze(0) + y_indices.unsqueeze(1)) % 2

output_mask = checkerboard.float()

Explanation:

  • torch.arange(width) // tile_size: Divides width into tiles
  • Adding the indices creates alternating sums
  • % 2: Modulo 2 gives alternating 0 and 1
  • Result: alternating black and white squares

Example: Diagonal Gradient Mask

import torch

# Create diagonal gradient from top-left to bottom-right
y_coords = torch.linspace(0, 1, height)
x_coords = torch.linspace(0, 1, width)
Y, X = torch.meshgrid(y_coords, x_coords, indexing='ij')

# Average of x and y coordinates
diagonal = (X + Y) / 2
output_mask = diagonal

Explanation:

  • Both coordinates range from 0 to 1
  • (X + Y) / 2: Average creates diagonal gradient
  • Result: top-left is black (0), bottom-right is white (1)

Example: Random Noise Mask

import torch

# Set seed for reproducibility
torch.manual_seed(seed)

# Create random noise
noise = torch.rand(height, width)

output_mask = noise

Explanation:

  • torch.rand(): Generates random values uniformly from [0, 1]
  • torch.manual_seed(seed): Ensures same random values each time with same seed
  • Result: random speckled pattern

Example: Perlin-like Noise Mask

import torch
from scipy.ndimage import gaussian_filter
import numpy as np

# Set seed for reproducibility
np.random.seed(seed)

# Generate random noise
noise = np.random.rand(height, width)

# Apply Gaussian filter to smooth it
smooth_noise = gaussian_filter(noise, sigma=10)

# Convert to tensor
output_mask = torch.from_numpy(smooth_noise).float()

Explanation:

  • np.random.rand(): Generates random noise
  • gaussian_filter(noise, sigma=10): Blurs the noise
    • sigma controls blur amount
    • Larger sigma = smoother, larger features
    • Smaller sigma = sharper, more detailed
  • Result: smooth, organic-looking noise pattern

Mathematical Background: Gaussian Filter

The Gaussian filter blurs an image by convolving it with a Gaussian kernel. This is similar to averaging nearby pixels but with weights that decrease with distance.

The Gaussian kernel in 2D is:

G(x, y) = exp(-(x² + y²) / (2σ²))

Where σ controls the spread of the blur. This creates smooth, continuous transitions rather than sharp edges.

Example: Vignette Mask

import torch

# Create coordinates from -1 to 1
y_coords = torch.linspace(-1, 1, height)
x_coords = torch.linspace(-1, 1, width)
Y, X = torch.meshgrid(y_coords, x_coords, indexing='ij')

# Calculate distance from center
distance = torch.sqrt(X**2 + Y**2)

# Create vignette (dark corners, bright center)
vignette = 1 - distance**2  # Squared for stronger falloff
vignette = torch.clamp(vignette, 0, 1)

output_mask = vignette

Explanation:

  • Similar to radial gradient but uses squared distance
  • Squaring makes the falloff more dramatic
  • Result: bright center, rapidly darkening corners

Example: Batch of Masks

import torch

# Create multiple masks in a batch
batch_size = 4
masks = []

for i in range(batch_size):
    # Each mask has a different gradient position
    gradient = torch.linspace(0, 1, width)
    shifted = torch.roll(gradient, shifts=i * width // batch_size, dims=0)
    mask = shifted.unsqueeze(0).repeat(height, 1)
    masks.append(mask)

# Stack into batch tensor
output_mask = torch.stack(masks, dim=0)  # Shape: [4, height, width]

Explanation:

  • Creates 4 masks with gradients at different positions
  • torch.roll(): Shifts the gradient circularly
  • .stack(): Combines into a batch tensor
  • Result: 4 masks, each with gradient shifted differently

Common Patterns

Pattern 1: Threshold a Gradient

import torch

# Create gradient
gradient = torch.linspace(0, 1, width).unsqueeze(0).repeat(height, 1)

# Apply threshold (binary mask)
threshold = 0.5
binary = (gradient > threshold).float()

output_mask = binary

Pattern 2: Combine Multiple Masks

import torch

# Create two masks
mask1 = torch.ones((height, width), dtype=torch.float32)  # All white
mask2 = torch.zeros((height, width), dtype=torch.float32)  # All black

# Combine by multiplication (intersection)
combined = mask1 * mask2  # Result: all black

# Combine by addition (union)
combined = torch.clamp(mask1 + mask2, 0, 1)  # Result: all white

# Blend by averaging
combined = (mask1 + mask2) / 2  # Result: 0.5 everywhere

Pattern 3: Use Conditional Logic

import torch

# Create coordinates
y_coords = torch.linspace(-1, 1, height)
x_coords = torch.linspace(-1, 1, width)
Y, X = torch.meshgrid(y_coords, x_coords, indexing='ij')

# Create different masks based on conditions
mask = torch.where(
    X > 0,  # Condition
    torch.ones_like(X),  # Value if True (right side)
    torch.zeros_like(X)  # Value if False (left side)
)

output_mask = mask

Error Handling

The node includes error handling:

  • Invalid output types are rejected
  • Output is automatically clamped to [0, 1]
  • Invalid shapes are corrected when possible
  • Errors fall back to a white mask

Security Considerations

This node uses exec() to run your script. This is intentionally powerful for local development but should not be used in production or exposed to untrusted users.

  • ScriptableImage: For generating full-color images
  • ScriptableLatent: For generating latents
  • Mask-related nodes in ComfyUI: MaskToRegion, MaskComposite, etc.